diff --git a/amd/src/bootstrap-vue/readme-moodle.txt b/amd/src/bootstrap-vue/readme-moodle.txt index 37a999f..2f2af07 100644 --- a/amd/src/bootstrap-vue/readme-moodle.txt +++ b/amd/src/bootstrap-vue/readme-moodle.txt @@ -33,4 +33,4 @@ Instructions for downloading and integrating bootstrap-vue and associoated files /* eslint-disable */ /* - */ \ No newline at end of file + */ diff --git a/amd/src/portal-vue/readme-moodle.txt b/amd/src/portal-vue/readme-moodle.txt index 66d8ea7..37ea827 100644 --- a/amd/src/portal-vue/readme-moodle.txt +++ b/amd/src/portal-vue/readme-moodle.txt @@ -11,4 +11,4 @@ Instructions for downloading and integrating portal-vue import Vue from '../vue/vue'; /* End modification */ -4. add /* eslint-disable */ to top of file \ No newline at end of file +4. add /* eslint-disable */ to top of file diff --git a/amd/src/util/readme-moodle.txt b/amd/src/util/readme-moodle.txt index 25df9f0..f744206 100644 --- a/amd/src/util/readme-moodle.txt +++ b/amd/src/util/readme-moodle.txt @@ -1,2 +1,2 @@ A number of simple utility scripts that could be easily re-used are collected here. -Most are more like boilerplate tools than real separate scripts \ No newline at end of file +Most are more like boilerplate tools than real separate scripts diff --git a/build.php b/build.php index 1b893ad..b71011c 100644 --- a/build.php +++ b/build.php @@ -1,13 +1,35 @@ . +/** + * + * @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); } diff --git a/cfg_grades.php b/cfg_grades.php index c96f9e2..9b26152 100644 --- a/cfg_grades.php +++ b/cfg_grades.php @@ -1,12 +1,32 @@ . +/** + * + * @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,186 +38,184 @@ $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; } } } - + } //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')); print html_writer::tag('p', get_string('cfg_grades_desc', 'local_treestudyplan')); 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')); diff --git a/classes/aggregator.php b/classes/aggregator.php index 37c9876..f67b321 100644 --- a/classes/aggregator.php +++ b/classes/aggregator.php @@ -1,4 +1,25 @@ . +/** + * + * @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,29 +28,29 @@ 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 - "bistate", + "bistate", "tristate", # deprecated ]; } - 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,15 +58,15 @@ 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, ""); } } - + private function __construct($configstr) { $this->initialize($configstr); } @@ -59,60 +80,60 @@ 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(){ - return [ + 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(), ]; } - + return $list; } diff --git a/classes/associationservice.php b/classes/associationservice.php index 45ed0de..44d25e2 100644 --- a/classes/associationservice.php +++ b/classes/associationservice.php @@ -1,16 +1,37 @@ . +/** + * + * @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; require_once($CFG->libdir.'/externallib.php'); -class associationservice extends \external_api +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,15 +69,15 @@ 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); } - + $result = [ "id" => $r->id, "name" => $r->name, @@ -64,59 +85,59 @@ 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), ] - ]; + ]; return $result; - - } + + } - public static function list_cohort_parameters() + public static function list_cohort_parameters() { - return new \external_function_parameters( [ + return new \external_function_parameters( [ 'like' => new \external_value(PARAM_TEXT, 'search text', VALUE_OPTIONAL), 'exclude_id' => new \external_value(PARAM_INT, 'exclude members of this studyplan', VALUE_OPTIONAL), 'context_ic' => new \external_value(PARAM_INT, 'context for this request', VALUE_OPTIONAL), ] ); } - public static function list_cohort_returns() + public static function list_cohort_returns() { 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); } @@ -124,28 +145,28 @@ class associationservice extends \external_api return $cohorts; } - public static function find_user_parameters() + public static function find_user_parameters() { - return new \external_function_parameters( [ + return new \external_function_parameters( [ 'like' => new \external_value(PARAM_TEXT, 'search text'), 'exclude_id' => new \external_value(PARAM_INT, 'exclude members of this studyplan', VALUE_OPTIONAL), 'context_id' => new \external_value(PARAM_INT, 'context for this request', VALUE_OPTIONAL), ] ); } - public static function find_user_returns() + public static function find_user_returns() { 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,10 +175,10 @@ 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; - } + } $users = []; $rs = $DB->get_recordset_sql($sql, $params); @@ -170,7 +191,7 @@ class associationservice extends \external_api return $users; } - public static function connect_cohort_parameters() + public static function connect_cohort_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), @@ -178,7 +199,7 @@ class associationservice extends \external_api ] ); } - public static function connect_cohort_returns() + public static function connect_cohort_returns() { return new \external_single_structure([ "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'), @@ -186,18 +207,17 @@ 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, + 'studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id, ]); @@ -210,7 +230,7 @@ class associationservice extends \external_api } - public static function disconnect_cohort_parameters() + public static function disconnect_cohort_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), @@ -218,7 +238,7 @@ class associationservice extends \external_api ] ); } - public static function disconnect_cohort_returns() + public static function disconnect_cohort_returns() { return new \external_single_structure([ "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'), @@ -226,21 +246,20 @@ 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, + 'studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id, ]); - + $studyplan->mark_csync_changed(); return ['success' => true, 'msg'=>'Cohort Disconnected']; @@ -248,9 +267,9 @@ class associationservice extends \external_api return ['success' => true, 'msg'=>'Connection does not exist']; } - } + } - public static function connect_user_parameters() + public static function connect_user_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), @@ -258,7 +277,7 @@ class associationservice extends \external_api ] ); } - public static function connect_user_returns() + public static function connect_user_returns() { return new \external_single_structure([ "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'), @@ -266,18 +285,17 @@ 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()); - - if(!$DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id])) - { + webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context()); + + 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, + 'studyplan_id' => $studyplan_id, 'user_id' => $user_id, ]); $studyplan->mark_csync_changed(); @@ -289,7 +307,7 @@ class associationservice extends \external_api } } - public static function disconnect_user_parameters() + public static function disconnect_user_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), @@ -297,7 +315,7 @@ class associationservice extends \external_api ] ); } - public static function disconnect_user_returns() + public static function disconnect_user_returns() { return new \external_single_structure([ "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'), @@ -305,17 +323,16 @@ 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, + 'studyplan_id' => $studyplan_id, 'user_id' => $user_id, ]); @@ -327,32 +344,31 @@ class associationservice extends \external_api } } - public static function associated_users_parameters() + public static function associated_users_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), ] ); } - public static function associated_users_returns() + public static function associated_users_returns() { return new \external_multiple_structure(self::user_structure()); } - // Actual functions - public static function associated_users($studyplan_id) + // 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(); @@ -360,31 +376,30 @@ class associationservice extends \external_api return $users; } - public static function associated_cohorts_parameters() + public static function associated_cohorts_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), ] ); } - public static function associated_cohorts_returns() + public static function associated_cohorts_returns() { return new \external_multiple_structure(self::cohort_structure()); } - // Actual functions - public static function associated_cohorts($studyplan_id) + // 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(); @@ -392,42 +407,41 @@ class associationservice extends \external_api } - public static function all_associated_parameters() + public static function all_associated_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), ] ); } - public static function all_associated_returns() + public static function all_associated_returns() { return new \external_multiple_structure(self::user_structure()); } - // Actual functions - public static function all_associated($studyplan_id) + // 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 + FROM {user} u LEFT JOIN {cohort_members} cm ON u.id = cm.userid LEFT JOIN {local_treestudyplan_cohort} tc ON cm.cohortid = tc.cohort_id LEFT JOIN {local_treestudyplan_user} tu ON u.id = tu.user_id - WHERE tc.studyplan_id = {$studyplan_id} + WHERE tc.studyplan_id = {$studyplan_id} OR tu.studyplan_id = {$studyplan_id} 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']; @@ -454,32 +468,32 @@ class associationservice extends \external_api }); } - public static function cascade_cohortsync_parameters() + public static function cascade_cohortsync_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL), ] ); } - public static function cascade_cohortsync_returns() + public static function cascade_cohortsync_returns() { return success::structure(); } - // Actual functions - public static function cascade_cohortsync($studyplan_id) + // 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(); diff --git a/classes/badgeinfo.php b/classes/badgeinfo.php index fceccec..bda081e 100644 --- a/classes/badgeinfo.php +++ b/classes/badgeinfo.php @@ -1,9 +1,30 @@ . +/** + * + * @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++; } } diff --git a/classes/cascadecohortsync.php b/classes/cascadecohortsync.php index ba54ce3..94917d7 100644 --- a/classes/cascadecohortsync.php +++ b/classes/cascadecohortsync.php @@ -1,4 +1,25 @@ . +/** + * + * @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,27 +30,27 @@ 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; } } return $a; } - + 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'); - + // Check to see if the group name already exists in this course. if ($DB->record_exists('groups', array('name' => $groupname, 'courseid' => $courseid))) { $group = $DB->get_record('groups', array('name' => $groupname, 'courseid' => $courseid)); @@ -40,156 +61,156 @@ class cascadecohortsync { $groupdata->courseid = $courseid; $groupdata->name = $groupname; $groupid = groups_create_group($groupdata); - + return $groupid; } - function sync(){ + function sync() { global $DB; /* Explainer: This script uses {enrol}.customtext4 to store a json array of all studyplans that need this cohort sync to exist. Since the cohort-sync enrolment method uses only customint1 and customint2, this is a safe place to store the data. - (Should the cohortsync script at any future time be modified to use customtext fields, it is still extremely unlikely that + (Should the cohortsync script at any future time be modified to use customtext fields, it is still extremely unlikely that customtext4 will be used.) Because of the overhead involved in keeping an extra table up to date and clean it up if cohort syncs are removed outside of this script, it was determined to be the simplest and cleanest solution. */ $enrol = \enrol_get_plugin(self::METHOD); - // 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)])]); - - //\mtrace("Synchronize the enrolment"); + $enrol->update_instance($instance, (object)["customtext4"=>json_encode([(int)($this->studyplanid)])]); + + //\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");. } } \ No newline at end of file diff --git a/classes/cascadeusersync.php b/classes/cascadeusersync.php index 9d7e92b..3a202d5 100644 --- a/classes/cascadeusersync.php +++ b/classes/cascadeusersync.php @@ -1,4 +1,25 @@ . +/** + * + * @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,57 +29,57 @@ 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: This script uses {enrol}.customtext4 to store a json array of all studyplans that need this cohort sync to exist. Since the cohort-sync enrolment method uses only customint1 and customint2, this is a safe place to store the data. - (Should the cohortsync script at any future time be modified to use customtext fields, it is still extremely unlikely that + (Should the cohortsync script at any future time be modified to use customtext fields, it is still extremely unlikely that customtext4 will be used.) Because of the overhead involved in keeping an extra table up to date and clean it up if cohort syncs are removed outside of this script, it was determined to be the simplest and cleanest solution. */ $enrol = \enrol_get_plugin(self::METHOD); - // 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 = null; + // 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. + } } } \ No newline at end of file diff --git a/classes/completion.php b/classes/completion.php index 0babc86..47897e1 100644 --- a/classes/completion.php +++ b/classes/completion.php @@ -1,4 +1,24 @@ . +/** + * + * @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,29 +43,29 @@ 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 + else { return self::LABELS[self::INCOMPLETE]; } } - 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; } } diff --git a/classes/completionscanner.php b/classes/completionscanner.php index 0676e3f..e855558 100644 --- a/classes/completionscanner.php +++ b/classes/completionscanner.php @@ -1,11 +1,31 @@ . +/** + * + * @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; -class completionscanner +class completionscanner { private static $mod_supported = []; private static $course_students = []; @@ -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,28 +56,26 @@ 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); $this->crit = $crit; $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 - $this->gi = $gi; - if(self::supported($gi->itemmodule)) { + 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)) { $scannerclass = "\local_treestudyplan\\local\ungradedscanners\\{$gi->itemmodule}_scanner"; $this->scanner = new $scannerclass($gi); } @@ -67,57 +85,57 @@ 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 { - $this->pending_cache[$userid] = $this->scanner->has_ungraded_submission($userid);; + $this->pending_cache[$userid] = $this->scanner->has_ungraded_submission($userid);; } } 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(){ - - // get completion info + public function model() { + + // 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); - - 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){ + $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 ($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++; } } diff --git a/classes/contextinfo.php b/classes/contextinfo.php index 393770e..c5431d6 100644 --- a/classes/contextinfo.php +++ b/classes/contextinfo.php @@ -1,15 +1,35 @@ . +/** + * + * @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'), @@ -19,16 +39,16 @@ 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); diff --git a/classes/corecompletioninfo.php b/classes/corecompletioninfo.php index 138654c..42ab048 100644 --- a/classes/corecompletioninfo.php +++ b/classes/corecompletioninfo.php @@ -1,4 +1,24 @@ . +/** + * + * @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,142 +37,141 @@ 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(){ - global $COMPLETION_CRITERIA_TYPES; - // Just return the keys of the global array COMPLETION_CRITERIA_TYPES, so we don't have to manually + 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. // add any completion types.... return \array_keys($COMPLETION_CRITERIA_TYPES); } /** - * Translate a numeric completion constant to a text string + * 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 => "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"; } public function editor_model() { 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() + "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 - - if($type == COMPLETION_CRITERIA_TYPE_SELF){ + 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) { $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,18 +254,18 @@ 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 = [ "type" => get_string('manualcompletionby', 'completion'), "criteria" => $criteria, @@ -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" => "", @@ -273,9 +292,9 @@ class corecompletioninfo { "status" => "", ]; } - - $scanner = new completionscanner($criteria,$this->course); - + + $scanner = new completionscanner($criteria, $this->course); + // only add the items list if we actually have items... $cinfo["items"][] = [ "id" => $criteria->id, @@ -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; } @@ -323,21 +342,20 @@ class corecompletioninfo { "conditions" => [], "completed" => $this->completion->is_course_complete($userid), "aggregation" => self::aggregation_handle($this->completion->get_aggregation_method()), - "enabled" => $this->completion->is_enabled(), + "enabled" => $this->completion->is_enabled(), "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 + $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. - 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); } - + else + { + // 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) - $gi= grade_item::fetch(['itemtype' => 'course', - 'iteminstance' => $this->course->id, + 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 + } + 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....). } @@ -518,8 +532,8 @@ class corecompletioninfo { * @param int $userid The id of the user, 0 for the current user * @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,38 +548,38 @@ 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)) { $completed += 0; } else { $completed += 1; - } + } } else { - if($completion->is_complete()){ + if ($completion->is_complete()) { $completed += 1; } - } + } } } $result = new \stdClass; $result->count = $count; $result->completed = $completed; $result->percentage = ($completed / $count) * 100; - return $result; + return $result; } /** @@ -574,8 +588,8 @@ class corecompletioninfo { * @param int $userid The id of the user, 0 for the current user * @return null|\stdClass The percentage, or null if completion is not supported in the course, * or there are no activities that support completion. - */ - function get_advanced_progress_percentage($userid):\stdClass { + */ + function get_advanced_progress_percentage($userid):\stdClass { // First, let's make sure completion is enabled. if (!$this->completion->is_enabled()) { @@ -591,74 +605,74 @@ 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)) { $typecount->completed += 0; } else { $typecount->completed += 1; - } + } } 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; - } + } else { $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. } } } @@ -667,8 +681,8 @@ class corecompletioninfo { $result->count = $count; $result->completed = $completed; $result->percentage = ($count > 0)?(($completed / $count) * 100):0; - return $result; - } + return $result; + } diff --git a/classes/courseinfo.php b/classes/courseinfo.php index 419891e..9f43898 100644 --- a/classes/courseinfo.php +++ b/classes/courseinfo.php @@ -1,4 +1,24 @@ . +/** + * + * @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'); @@ -13,70 +33,70 @@ use \grade_outcome; class courseinfo { const TABLE = 'course'; - + private $course; private $context; private $coursecontext; 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 + else { $usr = $DB->get_record('user', ['id' => $userid, 'deleted' => 0]); } return($usr && is_enrolled($this->coursecontext, $usr, 'local/treestudyplan:selectowngradables')); - } + } 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; + 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,71 +104,69 @@ 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 { return "present"; } - } - else{ + } + else{ return "future"; } } - 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() { @@ -160,11 +178,11 @@ class courseinfo { 'displayname' => $this->displayname(), 'context' => $contextinfo->model() ]; - + 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,15 +199,15 @@ 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) { global $DB; $contextinfo = new contextinfo($this->context); - + $timing = $this->timing(); - + $info = [ 'id' => $this->course->id, 'fullname' => $this->course->fullname, @@ -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); - - foreach($gradables as $gradable) { + + if (!$usecorecompletioninfo) { + $gradables = gradeinfo::list_course_gradables($this->course, $studyitem); + + 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), - "completion" => corecompletioninfo::user_structure(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); } } diff --git a/classes/coursemoduleinfo.php b/classes/coursemoduleinfo.php index b709234..26c5957 100644 --- a/classes/coursemoduleinfo.php +++ b/classes/coursemoduleinfo.php @@ -1,4 +1,24 @@ . +/** + * + * @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. } } diff --git a/classes/courseservice.php b/classes/courseservice.php index bc7ac94..00a34dd 100644 --- a/classes/courseservice.php +++ b/classes/courseservice.php @@ -1,4 +1,25 @@ . +/** + * + * @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,7 +29,7 @@ use \local_treestudyplan\local\helpers\webservicehelper; use \local_treestudyplan\completionscanner; use \local_treestudyplan\gradingscanner; -class courseservice extends \external_api +class courseservice extends \external_api { const CAP_EDIT = "local/treestudyplan:editstudyplan"; const CAP_VIEW = "local/treestudyplan:viewuserreports"; @@ -19,86 +40,85 @@ class courseservice extends \external_api * * ************************/ - public static function map_categories_parameters() + public static function map_categories_parameters() { return new \external_function_parameters( [ "root_id" => new \external_value(PARAM_INT, 'root category to use as base', VALUE_DEFAULT), ] ); } - public static function map_categories_returns() + public static function map_categories_returns() { 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; - } + } - public static function get_category_parameters() + public static function get_category_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of category'), ] ); } - public static function get_category_returns() + public static function get_category_returns() { 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,40 +128,39 @@ 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); } } return $model; } - public static function list_accessible_categories_parameters() + 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), ] ); } - public static function list_accessible_categories_returns() + public static function list_accessible_categories_returns() { return new \external_multiple_structure(static::map_category_structure(true)); } - public static function list_accessible_categories($operation="edit") + 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,91 +204,91 @@ 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; } - public static function list_used_categories_parameters() + 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), ]); } - public static function list_used_categories_returns() + public static function list_used_categories_returns() { return new \external_multiple_structure(static::map_category_structure(true)); } - public static function list_used_categories($operation='edit') + 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} + $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} + $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(); @@ -283,38 +302,35 @@ class courseservice extends \external_api /************************************** - * + * * Progress scanners for teacherview - * + * **************************************/ - 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()); - - // Retrieve grade item + webservicehelper::require_capabilities(self::CAP_VIEW, $o->context()); + + // 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()}"); } @@ -323,44 +339,44 @@ class courseservice extends \external_api } - public static function scan_completion_progress_parameters() + 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), ]); } - public static function scan_completion_progress_returns() + public static function scan_completion_progress_returns() { 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() + 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), ]); } - public static function scan_badge_progress_returns() + public static function scan_badge_progress_returns() { return new \external_single_structure([ "total" => new \external_value(PARAM_INT, 'Total number of students scanned'), @@ -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), diff --git a/classes/gradeinfo.php b/classes/gradeinfo.php index 9c663c3..f09c342 100644 --- a/classes/gradeinfo.php +++ b/classes/gradeinfo.php @@ -1,4 +1,24 @@ . +/** + * + * @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 +36,7 @@ class gradeinfo { private $studyitem = null; private $id; private $gradeitem; - + private $icon; private $link; private $gradinglink; @@ -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,39 +233,39 @@ 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 + } + else { - $finalgrade = round($grade->finalgrade,1); + $finalgrade = round($grade->finalgrade, 1); } } - else + else { $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,113 +294,108 @@ 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); } else { $DB->insert_record($table, [ - 'studyitem_id' => $item_id, - 'grade_item_id' => $grade_id, - 'include' => 1, + 'studyitem_id' => $item_id, + 'grade_item_id' => $grade_id, + 'include' => 1, 'required' =>boolval($required)?1:0] ); } } 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]); } } diff --git a/classes/gradingscanner.php b/classes/gradingscanner.php index 3b8fe04..2261b8a 100644 --- a/classes/gradingscanner.php +++ b/classes/gradingscanner.php @@ -1,14 +1,34 @@ . +/** + * + * @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 -class gradingscanner +// $gi->courseid, . +// $gi->itemmodule, . +// $gi->iteminstance. +class gradingscanner { private static $mod_supported = []; private static $course_students = []; @@ -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,61 +57,61 @@ 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 { - $this->pending_cache[$userid] = $this->scanner->has_ungraded_submission($userid);; + $this->pending_cache[$userid] = $this->scanner->has_ungraded_submission($userid);; } } 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 + 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 + 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 { diff --git a/classes/local/aggregators/bistate_aggregator.php b/classes/local/aggregators/bistate_aggregator.php index 62403b9..3383439 100644 --- a/classes/local/aggregators/bistate_aggregator.php +++ b/classes/local/aggregators/bistate_aggregator.php @@ -1,4 +1,25 @@ . +/** + * + * @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,57 +33,55 @@ 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]); } } } else { - + } } - // 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,35 +183,35 @@ 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 { return completion::PROGRESS; } @@ -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 + 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; } diff --git a/classes/local/aggregators/core_aggregator.php b/classes/local/aggregators/core_aggregator.php index 22ca11f..a8ed0c2 100644 --- a/classes/local/aggregators/core_aggregator.php +++ b/classes/local/aggregators/core_aggregator.php @@ -1,4 +1,25 @@ . +/** + * + * @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,42 +32,42 @@ 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]); } } } else { - + } } - // 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,42 +132,42 @@ 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 { return completion::PROGRESS; } } - // 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 + 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; } diff --git a/classes/local/aggregators/tristate_aggregator.php b/classes/local/aggregators/tristate_aggregator.php index 53a92c3..efa0e27 100644 --- a/classes/local/aggregators/tristate_aggregator.php +++ b/classes/local/aggregators/tristate_aggregator.php @@ -1,4 +1,25 @@ . +/** + * + * @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 { @@ -64,30 +84,30 @@ class tristate_aggregator extends \local_treestudyplan\aggregator { return completion::INCOMPLETE; } } - else + 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,36 +115,34 @@ 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; } - + } else { $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 { diff --git a/classes/local/gradegenerator.php b/classes/local/gradegenerator.php index 278d57c..74a8e36 100644 --- a/classes/local/gradegenerator.php +++ b/classes/local/gradegenerator.php @@ -1,4 +1,26 @@ . +/** + * + * @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; @@ -61,180 +83,176 @@ class gradegenerator { "Praesent nec risus vestibulum quam venenatis tempor.", "Nullam rhoncus ex a quam egestas, eu auctor enim lobortis.", "Nam luctus ante id lacus scelerisque, quis blandit ante elementum.", - + ]; - 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)){ - $this->table[$student] = [ - "intelligence" => rand(70,100), - "endurance" => rand(70,100), + public function addstudent(string $student) { + if (!array_key_exists($student, $this->table)) { + $this->table[$student] = [ + "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)), + $this->table[$student]["skills"][$skill] = [ + "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){ - $r->failed = !($score > 10); + 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 + 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 = "-"; } } - + } - 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 ]; } @@ -242,50 +260,49 @@ class gradegenerator { } $rlist[] = $r; - + } 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); } - + return $path; } diff --git a/classes/local/helpers/debugger.php b/classes/local/helpers/debugger.php index c4099c6..423f724 100644 --- a/classes/local/helpers/debugger.php +++ b/classes/local/helpers/debugger.php @@ -1,49 +1,67 @@ . +/** + * + * @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; use DateTime; class debugger { - + private $fname; 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); } } diff --git a/classes/local/helpers/webservicehelper.php b/classes/local/helpers/webservicehelper.php index 145728c..b5062ba 100644 --- a/classes/local/helpers/webservicehelper.php +++ b/classes/local/helpers/webservicehelper.php @@ -1,4 +1,25 @@ . +/** + * + * @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'); @@ -10,28 +31,28 @@ class webservicehelper { private static $validated_contexts = []; /** - * Test for capability in the given context for the current user and throw a \webservice_access_exception if not + * Test for capability in the given context for the current user and throw a \webservice_access_exception if not * Note: The context is not validate * @param array|string $capability One or more capabilities to be tested OR wise (if one capability is given, the function passes) * @param \context $context The context in which to check for the capability. * @throws \webservice_access_exception If none of the capabilities provided are given to the current user - * @return boolean + * @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; } } @@ -40,34 +61,34 @@ class webservicehelper { * Test if the current user has a certain capability in any of the categories they have access to * @param string $capability The capability to scan for in the categories * @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 + * @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; } } @@ -76,17 +97,17 @@ class webservicehelper { } /** - * Test for capability in the given context for the current user and throw a \webservice_access_exception if not + * Test for capability in the given context for the current user and throw a \webservice_access_exception if not * @param array|string $capability One or more capabilities to be tested OR wise (if one capability is given, the function passes) * @param \context $context The context in which to check for the capability. Leave empty to use the system context. * @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) { - \external_api::validate_context($context); + 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,32 +119,32 @@ 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(!in_array($contextid,self::$validated_contexts)){ // Cache the context and make sure it is only validated once... + 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... 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. } } - /** + /** * Return the validated system context (validation happens only once for this call) * @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); } diff --git a/classes/local/ungradedscanners/assign_scanner.php b/classes/local/ungradedscanners/assign_scanner.php index 6875e34..b2b2d9c 100644 --- a/classes/local/ungradedscanners/assign_scanner.php +++ b/classes/local/ungradedscanners/assign_scanner.php @@ -1,66 +1,86 @@ . +/** + * + * @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 LEFT JOIN {assign_grades} ag ON ag.assignment = asgn_sub.assignment AND ag.userid = asgn_sub.userid AND asgn_sub.attemptnumber = ag.attemptnumber - WHERE a.id = {$this->gi->iteminstance} + WHERE a.id = {$this->gi->iteminstance} AND asgn_sub.status = 'submitted' - AND asgn_sub.userid > 0 + AND asgn_sub.userid > 0 AND a.grade <> 0 AND (ag.id IS NULL OR asgn_sub.timemodified >= ag.timemodified) "; - + 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 + $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); } } \ No newline at end of file diff --git a/classes/local/ungradedscanners/quiz_scanner.php b/classes/local/ungradedscanners/quiz_scanner.php index f36bec2..e6aa1b6 100644 --- a/classes/local/ungradedscanners/quiz_scanner.php +++ b/classes/local/ungradedscanners/quiz_scanner.php @@ -1,17 +1,38 @@ . +/** + * + * @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 + $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); } } \ No newline at end of file diff --git a/classes/local/ungradedscanners/scanner_base.php b/classes/local/ungradedscanners/scanner_base.php index ec643b6..7600667 100644 --- a/classes/local/ungradedscanners/scanner_base.php +++ b/classes/local/ungradedscanners/scanner_base.php @@ -1,4 +1,25 @@ . +/** + * + * @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; } diff --git a/classes/period.php b/classes/period.php index ee63cb0..2cc7d91 100644 --- a/classes/period.php +++ b/classes/period.php @@ -1,4 +1,25 @@ . +/** + * + * @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,71 +30,71 @@ 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]; } /** * 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 - $ydelta = $yend - $ystart; + + // 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,26 +102,26 @@ 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; - } + } return self::$PAGECACHE[$page->id()]; } 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; diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 29d91fc..48a9610 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -1,4 +1,25 @@ . +/** + * + * @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; @@ -12,7 +33,7 @@ use \core_privacy\local\request\helper; use \core_privacy\local\request\transform; use tool_dataprivacy\context_instance; -class provider implements \core_privacy\local\metadata\provider, +class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\plugin\provider, \core_privacy\local\request\core_userlist_provider { @@ -57,15 +78,15 @@ 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 ) "; $contextlist->add_from_sql($sql, ['userid' => $userid]); - + return $contextlist; } @@ -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]); - - foreach($plan_ids as $plan_id){ - $DB->delete_records("local_treestudyplan_user",["studyplan_id" => $plan_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]); } } } @@ -167,35 +188,35 @@ 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]); - - foreach($plan_ids as $plan_id){ - $DB->delete_records("local_treestudyplan_user",["studyplan_id" => $plan_id,"user_id" => $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]); } - // 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]); - - foreach($plan_ids as $plan_id){ - $DB->delete_records("local_treestudyplan_user",["studyplan_id" => $plan_id,"user_id" => $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]); } } - } + } } /** @@ -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 - $sql = "SELECT s.id FROM {local_treestudyplan} + 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 - - // Also delete all invitations for these users - $sql = "user_id {$userinsql}"; - $DB->delete_records_select("local_treestudyplan_invit",$sql,$userinparams); + $plan_ids = $DB->get_fieldset_sql($sql, ["contextid"=>$context->id, ]); + // If plan ids not empty, they will be processed later. - } else if ($context->contextlevel == CONTEXT_COURSECAT){ - $sql = "SELECT s.id FROM {local_treestudyplan} + // Also delete all invitations for these users. + $sql = "user_id {$userinsql}"; + $DB->delete_records_select("local_treestudyplan_invit", $sql, $userinparams); + + } 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); diff --git a/classes/reportinvite_form.php b/classes/reportinvite_form.php index 69e114b..7bb1cbb 100644 --- a/classes/reportinvite_form.php +++ b/classes/reportinvite_form.php @@ -1,16 +1,37 @@ . +/** + * + * @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; diff --git a/classes/studentstudyplanservice.php b/classes/studentstudyplanservice.php index 675ac81..eefd0c5 100644 --- a/classes/studentstudyplanservice.php +++ b/classes/studentstudyplanservice.php @@ -1,4 +1,25 @@ . +/** + * + * @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'); @@ -6,7 +27,7 @@ use \local_treestudyplan\local\helpers\webservicehelper; require_once($CFG->libdir.'/badgeslib.php'); -class studentstudyplanservice extends \external_api +class studentstudyplanservice extends \external_api { const CAP_VIEWOTHER = "local/treestudyplan:viewuserreports"; /************************ @@ -15,29 +36,28 @@ class studentstudyplanservice extends \external_api * * ************************/ - public static function list_user_studyplans_parameters() + public static function list_user_studyplans_parameters() { return new \external_function_parameters([ "userid" => new \external_value(PARAM_INT, 'id of student', VALUE_DEFAULT), ]); } - public static function list_user_studyplans_returns() + public static function list_user_studyplans_returns() { return new \external_multiple_structure( studyplan::simple_structure() ); } - 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(); } } @@ -50,30 +70,28 @@ class studentstudyplanservice extends \external_api * * ************************/ - public static function get_user_studyplans_parameters() + public static function get_user_studyplans_parameters() { return new \external_function_parameters( [ "userid" => new \external_value(PARAM_INT, 'id of user'), ] ); } - public static function get_user_studyplans_returns() + public static function get_user_studyplans_returns() { return new \external_multiple_structure( 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)){ + $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)) { $map[] = $studyplan->user_model($userid); } } @@ -87,7 +105,7 @@ class studentstudyplanservice extends \external_api * * ************************/ - public static function get_user_studyplan_parameters() + public static function get_user_studyplan_parameters() { return new \external_function_parameters( [ "userid" => new \external_value(PARAM_INT, 'id of user'), @@ -95,18 +113,17 @@ class studentstudyplanservice extends \external_api ] ); } - public static function get_user_studyplan_returns() + public static function get_user_studyplan_returns() { 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 { @@ -120,38 +137,36 @@ class studentstudyplanservice extends \external_api * * ****************************/ - public static function get_invited_studyplan_parameters() + public static function get_invited_studyplan_parameters() { return new \external_function_parameters( [ "invitekey" => new \external_value(PARAM_RAW, 'invite key'), ] ); } - public static function get_invited_studyplan_returns() + public static function get_invited_studyplan_returns() { return new \external_multiple_structure( 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; @@ -163,26 +178,25 @@ class studentstudyplanservice extends \external_api * * ************************/ - public static function list_own_studyplans_parameters() + public static function list_own_studyplans_parameters() { return new \external_function_parameters([]); } - public static function list_own_studyplans_returns() + public static function list_own_studyplans_returns() { return new \external_multiple_structure( studyplan::simple_structure() ); } - 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; @@ -194,22 +208,21 @@ class studentstudyplanservice extends \external_api * * ************************/ - public static function get_own_studyplan_parameters() + public static function get_own_studyplan_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of specific studyplan to provide', VALUE_DEFAULT), ] ); } - public static function get_own_studyplan_returns() + public static function get_own_studyplan_returns() { return new \external_multiple_structure( studyplan::user_structure() ); } - 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; @@ -242,30 +255,29 @@ class studentstudyplanservice extends \external_api * * ***************************/ - public static function get_teaching_studyplans_parameters() + public static function get_teaching_studyplans_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of specific studyplan to provide', VALUE_DEFAULT), ] ); } - public static function get_teaching_studyplans_returns() + public static function get_teaching_studyplans_returns() { return new \external_multiple_structure( studyplan::editor_structure() ); } - 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()); + + \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; diff --git a/classes/studyitem.php b/classes/studyitem.php index 2cd22d0..552f35d 100644 --- a/classes/studyitem.php +++ b/classes/studyitem.php @@ -1,4 +1,25 @@ . +/** + * + * @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,9 +37,9 @@ 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; private $studyline; private $aggregator; @@ -36,9 +57,9 @@ 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){ - $model["gradables"] = []; - foreach($gradables as $g){ + if (count($gradables) > 0) { + $model["gradables"] = []; + 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,80 +208,77 @@ 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 - $item->studyline()->studyplan()->mark_csync_changed(); + 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 { return true; } } - 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 + 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(); @@ -273,13 +290,11 @@ class studyitem { * reorder_studyitems * * * ************************/ - - 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,43 +306,42 @@ 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'), "completion" => completion::structure(), - "studyline" => new \external_value(PARAM_TEXT, 'reference label of studyline'), + "studyline" => new \external_value(PARAM_TEXT, 'reference label of studyline'), "studyplan" => new \external_value(PARAM_TEXT, 'reference label of studyplan'), ], '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, "type" => $this->r->type, - "completion" => $this->completion($userid), - "studyline" => $line->name(), + "completion" => $this->completion($userid), + "studyline" => $line->name(), "studyplan" => $plan->name(), ]; } - 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); + $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); } } diff --git a/classes/studyitemconnection.php b/classes/studyitemconnection.php index 1ffebd6..5e7e23d 100644 --- a/classes/studyitemconnection.php +++ b/classes/studyitemconnection.php @@ -1,4 +1,25 @@ . +/** + * + * @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,96 +31,92 @@ 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 - - if(!$DB->record_exists(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id])) - { + //check if link already exists. + + if (!$DB->record_exists(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id])) { $id = $DB->insert_record(self::TABLE, [ - 'from_id' => $from_id, + '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, + 'from_id' => $from_id, 'to_id' => $to_id, ]); - + return success::success('Items Disconnected'); } else { return success::success('Connection does not exist'); } - } + } public static function clear($id) { global $DB; diff --git a/classes/studyline.php b/classes/studyline.php index aa4fff2..6e71b60 100644 --- a/classes/studyline.php +++ b/classes/studyline.php @@ -1,4 +1,25 @@ . +/** + * + * @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'); @@ -21,10 +42,10 @@ class studyline { ]; public const TABLE = "local_treestudyplan_line"; - + private static $STUDYLINE_CACHE = []; - private $r; // Holds database record + private $r; // Holds database record. private $id; private $page; private $studyplan; @@ -42,54 +63,54 @@ 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]; } 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'), "shortname"=> new \external_value(PARAM_TEXT, 'idnumber of studyline'), "color"=> new \external_value(PARAM_TEXT, 'description of studyline'), "sequence" => new \external_value(PARAM_INT, 'order of studyline'), - "slots" => new \external_multiple_structure( + "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 => []]; @@ -128,25 +149,24 @@ class studyline { } $model['slots'][$i] = $slots; } - + $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,36 +242,35 @@ 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'), "shortname"=> new \external_value(PARAM_TEXT, 'idnumber of studyline'), "color"=> new \external_value(PARAM_TEXT, 'description of studyline'), "sequence" => new \external_value(PARAM_INT, 'order of studyline'), - "slots" => new \external_multiple_structure( + "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; } } diff --git a/classes/studyplan.php b/classes/studyplan.php index 8ede066..7ef157b 100644 --- a/classes/studyplan.php +++ b/classes/studyplan.php @@ -1,4 +1,25 @@ . +/** + * + * @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,50 +31,50 @@ 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]; } 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 = [ @@ -144,25 +165,24 @@ class studyplan { 'context_id' => $this->context()->id, "aggregation" => $this->r->aggregation, "aggregation_config" => $this->aggregator->config_string(), - 'aggregation_info' => $this->aggregator->basic_model(), + 'aggregation_info' => $this->aggregator->basic_model(), '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(!array_key_exists('advanced',$model)){ - // Create advanced node if it does not exist + if (has_capability('local/treestudyplan:forcescales', \context_system::instance())) { + + 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 the context... - $this->context = null; - $this->context(); - // reload aggregator - $this->aggregator = aggregator::createOrDefault($this->r->aggregation, $this->r->aggregation_config); + //reload record after edit. + $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST); - // Start temporary skräpp code - // TODO: Until proper page editing is implemented, copy data from studyplan to it's first page + //reload the context... + $this->context = null; + $this->context(); + // 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. // 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,38 +326,36 @@ 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 + $sql = "SELECT s.id FROM {local_treestudyplan} s INNER JOIN {local_treestudyplan_cohort} j ON j.studyplan_id = s.id INNER JOIN {cohort_members} cm ON j.cohort_id = cm.cohortid WHERE cm.userid = :userid"; $cohort_plan_ids = $DB->get_fieldset_sql($sql, ['userid' => $userid]); - $sql = "SELECT s.id FROM {local_treestudyplan} s + $sql = "SELECT s.id FROM {local_treestudyplan} s INNER JOIN {local_treestudyplan_user} j ON j.studyplan_id = s.id WHERE j.user_id = :userid"; $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,17 +364,16 @@ 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 + $sql = "SELECT s.* FROM {local_treestudyplan} s INNER JOIN {local_treestudyplan_cohort} j ON j.studyplan_id = s.id INNER JOIN {cohort_members} cm ON j.cohort_id = cm.cohortid WHERE cm.userid = :userid"; $count += $DB->count_records_sql($sql, ['userid' => $userid]); - $sql = "SELECT s.* FROM {local_treestudyplan} s + $sql = "SELECT s.* FROM {local_treestudyplan} s INNER JOIN {local_treestudyplan_user} j ON j.studyplan_id = s.id WHERE j.user_id = :userid"; $count += $DB->count_records_sql($sql, ['userid' => $userid]); @@ -365,50 +381,50 @@ class studyplan { return ($count > 0); } - /** + /** * 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; } - /** + /** * Retrieve the user id's of the users linked to this studyplan. * @return array of int (User Id) */ 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 - $sql = "SELECT cm.userid FROM {local_treestudyplan_cohort} j + // 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); } @@ -416,23 +432,23 @@ class studyplan { } /** Check if this studyplan is linked to a particular user - * @param bool|stdClass $user The userid or user record of the 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 - foreach($this->pages() as $p){ + // next, copy the studylines. + + 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;} - - $content = json_decode($content,true); - if($content["type"] == "studyplan" && $content["version"] >= 2.0){ - - // Make sure the aggregation_config is re-encoded as json text + 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) { + + // 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,38 +597,38 @@ 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) FROM {local_treestudyplan} INNER JOIN {local_treestudyplan_line} l ON p.id = l.studyplan_id INNER JOIN {local_treestudyplan_item} i ON l.id = i.line_id - WHERE p.id = :planid + 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,16 +677,16 @@ 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) FROM {local_treestudyplan} INNER JOIN {local_treestudyplan_line} l ON p.id = l.studyplan_id INNER JOIN {local_treestudyplan_item} i ON l.id = i.line_id - WHERE p.id = :planid + 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; } diff --git a/classes/studyplanpage.php b/classes/studyplanpage.php index ceb9413..93a85ab 100644 --- a/classes/studyplanpage.php +++ b/classes/studyplanpage.php @@ -1,4 +1,25 @@ . +/** + * + * @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,30 +31,30 @@ 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]; } 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, @@ -262,101 +278,98 @@ class studyplanpage { 'startdate' => $this->r->startdate, '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{ + else{ $csv .= "\r\n"; } $csv .= "- ".str_replace('"', '\'', $g["name"]); @@ -369,9 +382,9 @@ class studyplanpage { $ct++; } } - } - 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 - $itemtranslation = []; + // 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; diff --git a/classes/studyplanservice.php b/classes/studyplanservice.php index 3ef8e5a..3b76f84 100644 --- a/classes/studyplanservice.php +++ b/classes/studyplanservice.php @@ -1,4 +1,25 @@ . +/** + * + * @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; @@ -9,43 +30,40 @@ require_once($CFG->dirroot.'/course/modlib.php'); -class studyplanservice extends \external_api +class studyplanservice extends \external_api { const CAP_EDIT = "local/treestudyplan:editstudyplan"; const CAP_VIEW = "local/treestudyplan:viewuserreports"; - + /************************ * * * list_studyplans * * * ************************/ - public static function list_studyplans_parameters() - { + public static function list_studyplans_parameters() { return new \external_function_parameters([ "context_id" => new \external_value(PARAM_INT, 'context to search in for studyplans', VALUE_DEFAULT), ]); } - public static function list_studyplans_returns() - { + public static function list_studyplans_returns() { return new \external_multiple_structure( studyplan::simple_structure() ); } - public static function list_studyplans($context_id = 0){ + public static function list_studyplans($context_id = 0) { global $CFG, $DB; - // Check if the user has the correct permissions for this context + // Check if the user has the correct permissions for this context. $context = webservicehelper::find_context($context_id); - webservicehelper::require_capabilities([self::CAP_EDIT,self::CAP_VIEW],$context); + webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $context); - // Now list all the studyplans in the relevant context + // Now list all the studyplans in the relevant context. $list = []; $studyplans = studyplan::find_all($context_id); - foreach($studyplans as $studyplan) - { + foreach ($studyplans as $studyplan) { $list[] = $studyplan->simple_model(); } return $list; @@ -58,23 +76,20 @@ class studyplanservice extends \external_api * * ************************/ - public static function get_studyplan_map_parameters() - { + public static function get_studyplan_map_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of a studyplan to check usage on', VALUE_REQUIRED), ] ); } - public static function get_studyplan_map_returns() - { + public static function get_studyplan_map_returns() { return studyplan::editor_structure(); } - public static function get_studyplan_map($id) - { - if(isset($id) && $id > 0){ + public static function get_studyplan_map($id) { + if (isset($id) && $id > 0) { $studyplan = studyplan::findById($id); - webservicehelper::require_capabilities([self::CAP_EDIT,self::CAP_VIEW],$studyplan->context()); + webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $studyplan->context()); return $studyplan->editor_model(); } else { @@ -88,23 +103,20 @@ class studyplanservice extends \external_api * * ************************/ - public static function get_studyline_map_parameters() - { + public static function get_studyline_map_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of a studyline to check usage on', VALUE_DEFAULT), ]); } - public static function get_studyline_map_returns() - { + public static function get_studyline_map_returns() { return new \external_multiple_structure( studyline::editor_structure() ); } - public static function get_studyline_map($id) - { - + public static function get_studyline_map($id) { + $o = studyline::findById($id); - webservicehelper::require_capabilities([self::CAP_EDIT,self::CAP_VIEW],$o->context()); + webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $o->context()); return $o->editor_model(); } @@ -114,8 +126,7 @@ class studyplanservice extends \external_api * * ************************/ - public static function add_studyplan_parameters() - { + public static function add_studyplan_parameters() { return new \external_function_parameters( [ "name" => new \external_value(PARAM_TEXT, 'name of studyplan'), "shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'), @@ -124,22 +135,20 @@ class studyplanservice extends \external_api "periods" => new \external_value(PARAM_INT, 'number of periods in studyplan'), "startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'), "enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'), - "aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator',VALUE_DEFAULT), - "aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator',VALUE_DEFAULT), - "context_id" => new \external_value(PARAM_INT, 'context of the study plan',VALUE_DEFAULT), + "aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator', VALUE_DEFAULT), + "aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator', VALUE_DEFAULT), + "context_id" => new \external_value(PARAM_INT, 'context of the study plan', VALUE_DEFAULT), ] ); } - public static function add_studyplan_returns() - { + public static function add_studyplan_returns() { return studyplan::simple_structure(); } - public static function add_studyplan($name, $shortname, $idnumber, $description, $periods, $startdate, $enddate, $aggregation="bistate", $aggregation_config='',$context_id=0) - { - // Check if we have the proper rights for the requested context + public static function add_studyplan($name, $shortname, $idnumber, $description, $periods, $startdate, $enddate, $aggregation="bistate", $aggregation_config='', $context_id=0) { + // Check if we have the proper rights for the requested context. $context = webservicehelper::find_context($context_id); - webservicehelper::require_capabilities(self::CAP_EDIT,$context); + webservicehelper::require_capabilities(self::CAP_EDIT, $context); $o = studyplan::add([ 'name' => $name, @@ -155,15 +164,14 @@ class studyplanservice extends \external_api ]); return $o->simple_model(); } - + /************************ * * * edit_studyplan * * * ************************/ - public static function edit_studyplan_parameters() - { + public static function edit_studyplan_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of studyplan'), "name" => new \external_value(PARAM_TEXT, 'name of studyplan'), @@ -173,25 +181,23 @@ class studyplanservice extends \external_api "periods" => new \external_value(PARAM_INT, 'number of periods in studyplan'), "startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'), "enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'), - "aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator',VALUE_DEFAULT), - "aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator',VALUE_DEFAULT), - "context_id" => new \external_value(PARAM_INT, 'context of the study plan',VALUE_DEFAULT), + "aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator', VALUE_DEFAULT), + "aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator', VALUE_DEFAULT), + "context_id" => new \external_value(PARAM_INT, 'context of the study plan', VALUE_DEFAULT), ] ); } - public static function edit_studyplan_returns() - { + public static function edit_studyplan_returns() { return studyplan::simple_structure(); } - - public static function edit_studyplan($id, $name, $shortname, $idnumber, $description, $periods, $startdate, $enddate, $aggregation="bistate", $aggregation_config='',$context_id=0) - { - // Validate access in the intended context + + public static function edit_studyplan($id, $name, $shortname, $idnumber, $description, $periods, $startdate, $enddate, $aggregation="bistate", $aggregation_config='', $context_id=0) { + // Validate access in the intended context. $context = webservicehelper::find_context($context_id); - webservicehelper::require_capabilities(self::CAP_EDIT,$context,false); // do not validate the context in this case, just check the permissions + webservicehelper::require_capabilities(self::CAP_EDIT, $context, false); // do not validate the context in this case, just check the permissions. $o = studyplan::findById($id); - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); $o->edit([ 'name' => $name, @@ -214,24 +220,21 @@ class studyplanservice extends \external_api * * ************************/ - public static function delete_studyplan_parameters() - { + public static function delete_studyplan_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of studyplan'), "force" => new \external_value(PARAM_BOOL, 'id of studyplan', VALUE_DEFAULT), ] ); } - public static function delete_studyplan_returns() - { + public static function delete_studyplan_returns() { return success::structure(); } - - public static function delete_studyplan($id,$force=false) - { + + public static function delete_studyplan($id, $force=false) { $o = studyplan::findById($id); - // validate if the requesting user has the right to edit the plan in it's current context - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + // validate if the requesting user has the right to edit the plan in it's current context. + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); return $o->delete(!!$force)->model(); } @@ -241,8 +244,7 @@ class studyplanservice extends \external_api * * ************************/ - public static function add_studyline_parameters() - { + public static function add_studyline_parameters() { return new \external_function_parameters( [ "page_id" => new \external_value(PARAM_INT, 'id of studyplan to add line to'), "name" => new \external_value(PARAM_TEXT, 'shortname of studyline'), @@ -252,16 +254,14 @@ class studyplanservice extends \external_api ] ); } - public static function add_studyline_returns() - { + public static function add_studyline_returns() { return studyline::editor_structure(); } - - public static function add_studyline($page_id, $name, $shortname,$color,$sequence) - { - // validate if the requesting user has the right to edit the plan in it's current context + + public static function add_studyline($page_id, $name, $shortname, $color, $sequence) { + // validate if the requesting user has the right to edit the plan in it's current context. $page = studyplanpage::findById($page_id); - webservicehelper::require_capabilities(self::CAP_EDIT,$page->studyplan()->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, $page->studyplan()->context()); $o = studyline::add([ 'page_id' => $page_id, @@ -279,8 +279,7 @@ class studyplanservice extends \external_api * * ************************/ - public static function edit_studyline_parameters() - { + public static function edit_studyline_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of studyline'), "name" => new \external_value(PARAM_TEXT, 'shortname of studyline'), @@ -289,16 +288,14 @@ class studyplanservice extends \external_api ] ); } - public static function edit_studyline_returns() - { + public static function edit_studyline_returns() { return studyline::editor_structure(); } - - public static function edit_studyline($id, $name, $shortname, $color) - { + + public static function edit_studyline($id, $name, $shortname, $color) { $o = studyline::findById($id); - // validate if the requesting user has the right to edit the plan in it's current context - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + // validate if the requesting user has the right to edit the plan in it's current context. + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); $o->edit([ 'name' => $name, @@ -308,7 +305,7 @@ class studyplanservice extends \external_api return $o->editor_model(); } - + /************************ * * @@ -316,23 +313,20 @@ class studyplanservice extends \external_api * * ************************/ - public static function delete_studyline_parameters() - { + public static function delete_studyline_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of studyline'), ] ); } - public static function delete_studyline_returns() - { + public static function delete_studyline_returns() { return success::structure(); } - - public static function delete_studyline($id) - { + + public static function delete_studyline($id) { $o = studyline::findById($id); - // validate if the requesting user has the right to edit the plan in it's current context - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + // validate if the requesting user has the right to edit the plan in it's current context. + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); return $o->delete()->model(); } @@ -343,8 +337,7 @@ class studyplanservice extends \external_api * * ************************/ - public static function reorder_studylines_parameters() - { + public static function reorder_studylines_parameters() { return new \external_function_parameters( [ "sequence" => new \external_multiple_structure( new \external_single_structure([ @@ -355,22 +348,20 @@ class studyplanservice extends \external_api ] ); } - public static function reorder_studylines_returns() - { + public static function reorder_studylines_returns() { return success::structure(); } - - public static function reorder_studylines($resequence) - { - // validate if the requesting user has the right to edit the lines in it's current context - foreach($resequence as $sq){ + + public static function reorder_studylines($resequence) { + // validate if the requesting user has the right to edit the lines in it's current context. + foreach ($resequence as $sq) { $o = studyline::findById(($sq['id'])); - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); } return studyline::reorder($resequence)->model(); } - + /*********************************** * STUDYITEM FUNCTIONS ***********************************/ @@ -381,63 +372,57 @@ class studyplanservice extends \external_api * * ************************/ - public static function get_studyitem_parameters() - { + public static function get_studyitem_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of a study item to retrieve'), ] ); } - public static function get_studyitem_returns() - { + public static function get_studyitem_returns() { return studyitem::editor_structure(); } - - public static function get_studyitem($id) - { + + public static function get_studyitem($id) { $o = studyitem::findById($id); - webservicehelper::require_capabilities([self::CAP_EDIT,self::CAP_VIEW],$o->context()); + webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $o->context()); return $o->editor_model(); } - + /************************ * * * add_studyitem * * * ************************/ - public static function add_studyitem_parameters() - { + public static function add_studyitem_parameters() { return new \external_function_parameters( [ "line_id" => new \external_value(PARAM_INT, 'id of related study line'), "type" => new \external_value(PARAM_TEXT, 'type of study item'), "details" => new \external_single_structure([ - "conditions"=> new \external_value(PARAM_TEXT, 'conditions for completion',VALUE_OPTIONAL), - "competency_id" => new \external_value(PARAM_INT, 'id of referenced competency',VALUE_OPTIONAL), - "course_id" => new \external_value(PARAM_INT, 'id of referenced course',VALUE_OPTIONAL), - "badge_id" => new \external_value(PARAM_INT, 'id of referenced badge',VALUE_OPTIONAL), - "continuation_id" => new \external_value(PARAM_INT, 'id of continued item',VALUE_OPTIONAL), + "conditions"=> new \external_value(PARAM_TEXT, 'conditions for completion', VALUE_OPTIONAL), + "competency_id" => new \external_value(PARAM_INT, 'id of referenced competency', VALUE_OPTIONAL), + "course_id" => new \external_value(PARAM_INT, 'id of referenced course', VALUE_OPTIONAL), + "badge_id" => new \external_value(PARAM_INT, 'id of referenced badge', VALUE_OPTIONAL), + "continuation_id" => new \external_value(PARAM_INT, 'id of continued item', VALUE_OPTIONAL), ]), - "slot" => new \external_value(PARAM_INT, 'slot in the study plan',VALUE_DEFAULT), - "layer" => new \external_value(PARAM_INT, 'layer in the slot',VALUE_OPTIONAL), + "slot" => new \external_value(PARAM_INT, 'slot in the study plan', VALUE_DEFAULT), + "layer" => new \external_value(PARAM_INT, 'layer in the slot', VALUE_OPTIONAL), ] ); } - public static function add_studyitem_returns() - { + public static function add_studyitem_returns() { return studyitem::editor_structure(); } - public static function add_studyitem($line_id,$type,$details,$slot=-1,$layer=0) - { - webservicehelper::require_capabilities(self::CAP_EDIT,studyline::findById($line_id)->context()); + public static function add_studyitem($line_id, $type, $details, $slot=-1, $layer=0) { + webservicehelper::require_capabilities(self::CAP_EDIT, studyline::findById($line_id)->context()); $o = studyitem::add([ 'line_id' => $line_id, 'type' => $type, - //'conditions' => $conditions, + //'conditions' => $conditions,. 'slot' => $slot, 'layer' => $layer, 'competency_id' => isset($details['competency_id'])?$details['competency_id']:null, @@ -447,37 +432,34 @@ class studyplanservice extends \external_api ]); return $o->editor_model(); } - + /************************ * * * edit_studyitem * * * ************************/ - public static function edit_studyitem_parameters() - { + public static function edit_studyitem_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of study item'), "conditions"=> new \external_value(PARAM_TEXT, 'conditions for completion'), - "continuation_id" => new \external_value(PARAM_INT, 'id of continued item',VALUE_DEFAULT), + "continuation_id" => new \external_value(PARAM_INT, 'id of continued item', VALUE_DEFAULT), ]); } - public static function edit_studyitem_returns() - { + public static function edit_studyitem_returns() { return studyitem::editor_structure(); } - - public static function edit_studyitem($id,$conditions,$continuation_id=false) - { - + + public static function edit_studyitem($id, $conditions, $continuation_id=false) { + $o = studyitem::findById($id); - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); $config = [ 'conditions' => $conditions, ]; - if($continuation_id !== false) { + if ($continuation_id !== false) { $config['continuation_id'] = $continuation_id; } @@ -492,8 +474,7 @@ class studyplanservice extends \external_api * * ************************/ - public static function reorder_studyitems_parameters() - { + public static function reorder_studyitems_parameters() { return new \external_function_parameters( [ "items" => new \external_multiple_structure( new \external_single_structure([ @@ -506,16 +487,14 @@ class studyplanservice extends \external_api ] ); } - public static function reorder_studyitems_returns() - { + public static function reorder_studyitems_returns() { return success::structure(); } - - public static function reorder_studyitems($resequence) - { - // Check for permissions to modify the studyplan - foreach($resequence as $sq){ - webservicehelper::require_capabilities(self::CAP_EDIT,studyitem::findById(($sq['id']))->context()); + + public static function reorder_studyitems($resequence) { + // Check for permissions to modify the studyplan. + foreach ($resequence as $sq) { + webservicehelper::require_capabilities(self::CAP_EDIT, studyitem::findById(($sq['id']))->context()); } return studyitem::reorder($resequence)->model(); @@ -528,22 +507,19 @@ class studyplanservice extends \external_api * * ************************/ - public static function delete_studyitem_parameters() - { + public static function delete_studyitem_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of studyitem'), ] ); } - public static function delete_studyitem_returns() - { + public static function delete_studyitem_returns() { return success::structure(); } - - public static function delete_studyitem($id) - { + + public static function delete_studyitem($id) { $o = studyitem::findById($id); - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); return $o->delete()->model(); } @@ -554,26 +530,23 @@ class studyplanservice extends \external_api * * ************************/ - public static function connect_studyitems_parameters() - { + public static function connect_studyitems_parameters() { return new \external_function_parameters( [ "from_id" => new \external_value(PARAM_INT, 'id of studyitem connect start '), "to_id" => new \external_value(PARAM_INT, 'id ofstudyitem connect end'), ] ); } - public static function connect_studyitems_returns() - { + public static function connect_studyitems_returns() { return studyitemconnection::structure(); } - - public static function connect_studyitems($from_id,$to_id) - { - // Validate permissions - webservicehelper::require_capabilities(self::CAP_EDIT,studyitem::findById($from_id)->context()); - webservicehelper::require_capabilities(self::CAP_EDIT,studyitem::findById($to_id)->context()); - $o = studyitemconnection::connect($from_id,$to_id); + public static function connect_studyitems($from_id, $to_id) { + // Validate permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, studyitem::findById($from_id)->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, studyitem::findById($to_id)->context()); + + $o = studyitemconnection::connect($from_id, $to_id); return $o->model(); } @@ -583,26 +556,23 @@ class studyplanservice extends \external_api * * ****************************/ - public static function disconnect_studyitems_parameters() - { + public static function disconnect_studyitems_parameters() { return new \external_function_parameters( [ "from_id" => new \external_value(PARAM_INT, 'id of studyitem '), "to_id" => new \external_value(PARAM_INT, 'id of related study line'), ] ); } - public static function disconnect_studyitems_returns() - { + public static function disconnect_studyitems_returns() { return success::structure(); } - - public static function disconnect_studyitems($from_id,$to_id) - { - // Validate permissions - webservicehelper::require_capabilities(self::CAP_EDIT,studyitem::findById($from_id)->context()); - webservicehelper::require_capabilities(self::CAP_EDIT,studyitem::findById($to_id)->context()); - return studyitemconnection::disconnect($from_id,$to_id)->model(); - } + + public static function disconnect_studyitems($from_id, $to_id) { + // Validate permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, studyitem::findById($from_id)->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, studyitem::findById($to_id)->context()); + return studyitemconnection::disconnect($from_id, $to_id)->model(); + } /**************************** * * @@ -610,34 +580,31 @@ class studyplanservice extends \external_api * * ****************************/ - public static function list_badges_parameters() - { + public static function list_badges_parameters() { return new \external_function_parameters( [] ); } - public static function list_badges_returns() - { + public static function list_badges_returns() { return new \external_multiple_structure(badgeinfo::editor_structure()); } - - public static function list_badges() - { + + public static function list_badges() { $systemcontext = webservicehelper::system_context(); $result = []; - $badges = badges_get_badges(BADGE_TYPE_SITE,"timemodified"); + $badges = badges_get_badges(BADGE_TYPE_SITE, "timemodified"); foreach ($badges as $badge) { -// TODO: Add config option to list only active badges -// if($badge->is_active()){ +// TODO: Add config option to list only active badges. +// if ($badge->is_active()) {. $result[] = (new badgeinfo($badge))->editor_model(); -// } +// }. -//TODO: Include course badges somehow... Just site badges is not enough +//TODO: Include course badges somehow... Just site badges is not enough. - } + } return $result; - } + } /**************************** * * @@ -645,8 +612,7 @@ class studyplanservice extends \external_api * * ****************************/ - public static function include_grade_parameters() - { + public static function include_grade_parameters() { return new \external_function_parameters( [ "grade_id" => new \external_value(PARAM_INT, 'id of gradeitem '), "item_id" => new \external_value(PARAM_INT, 'id of studyitem '), @@ -655,31 +621,29 @@ class studyplanservice extends \external_api ] ); } - public static function include_grade_returns() - { + public static function include_grade_returns() { return success::structure(); } - - public static function include_grade($grade_id,$item_id,$include,$required=false) - { + + public static function include_grade($grade_id, $item_id, $include, $required=false) { global $USER; - // find related course and course context + // find related course and course context. $coursecontext = gradeinfo::getCourseContextById($grade_id); - // do sanity checks + // do sanity checks. \external_api::validate_context($coursecontext); - // check correct capabilities - if(has_capability('local/treestudyplan:editstudyplan', studyitem::findById($item_id)->context()) || - is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables')){ - return gradeinfo::include_grade($grade_id,$item_id,$include,$required)->model(); + // check correct capabilities. + if (has_capability('local/treestudyplan:editstudyplan', studyitem::findById($item_id)->context()) || + is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables')) { + return gradeinfo::include_grade($grade_id, $item_id, $include, $required)->model(); } else { return success::fail("Access denied")->model(); } - } + } /**************************** * * @@ -687,119 +651,113 @@ class studyplanservice extends \external_api * * ****************************/ - public static function list_aggregators_parameters() - { + public static function list_aggregators_parameters() { return new \external_function_parameters([]); } - public static function list_aggregators_returns() - { + public static function list_aggregators_returns() { return aggregator::list_structure(); } - - public static function list_aggregators() - { + + public static function list_aggregators() { return aggregator::list_model(); - } + } /**************************** * * * force_studyplan_scale * * * ****************************/ - - public static function force_studyplan_scale_parameters() - { + + public static function force_studyplan_scale_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan'), "scale_id" => new \external_value(PARAM_INT, 'scale_id to set'), ] ); } - public static function force_studyplan_scale_returns() - { + public static function force_studyplan_scale_returns() { return new \external_multiple_structure(new \external_single_structure([ "course" => courseinfo::simple_structure(), "grades" => new \external_multiple_structure(new \external_single_structure([ "name" => new \external_value(PARAM_TEXT, 'grade name'), "changed" => new \external_value(PARAM_TEXT, 'changed or not'), - "debug" => new \external_value(PARAM_TEXT, 'debug',VALUE_OPTIONAL), + "debug" => new \external_value(PARAM_TEXT, 'debug', VALUE_OPTIONAL), ])), ])); } - - public static function force_studyplan_scale($studyplan_id, $scale_id) - { + + public static function force_studyplan_scale($studyplan_id, $scale_id) { global $DB; $dbman = $DB->get_manager(); - // Validate permissions - webservicehelper::require_capabilities(self::CAP_EDIT,studyplan::findById($studyplan_id)->context()); + // Validate permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, studyplan::findById($studyplan_id)->context()); $list = []; - // check if scaleid is valid + // check if scaleid is valid. $scale = \grade_scale::fetch(['id' => $scale_id]); $scale->load_items(); - if($scale){ - $gradecfg = $DB->get_record("local_treestudyplan_gradecfg",["scale_id"=>$scale->id]); + if ($scale) { + $gradecfg = $DB->get_record("local_treestudyplan_gradecfg", ["scale_id"=>$scale->id]); $scalepass = ($gradecfg)?$gradecfg->min_completed:0; $scalemax = count($scale->scale_items); - // find studyline id's - $studyline_ids = $DB->get_fieldset_select(studyline::TABLE,"id","studyplan_id = :plan_id",['plan_id' => $studyplan_id]); - foreach($studyline_ids as $studyline_id) { - // find id's of studyitems of type course - $records = $DB->get_records(studyitem::TABLE,['line_id' => $studyline_id]); + // find studyline id's. + $studyline_ids = $DB->get_fieldset_select(studyline::TABLE, "id", "studyplan_id = :plan_id", ['plan_id' => $studyplan_id]); + foreach ($studyline_ids as $studyline_id) { + // find id's of studyitems of type course. + $records = $DB->get_records(studyitem::TABLE, ['line_id' => $studyline_id]); - foreach($records as $item_r){ + foreach ($records as $item_r) { $studyitem = new studyitem($item_r->id); - if($studyitem->isValid() && $studyitem->type() == studyitem::COURSE){ + if ($studyitem->isValid() && $studyitem->type() == studyitem::COURSE) { $courseinfo = $studyitem->getcourseinfo(); $gradables = gradeinfo::list_studyitem_gradables($studyitem); - + $gradelist = []; - foreach($gradables as $g){ + foreach ($gradables as $g) { $gi = $g->getGradeItem(); - - // only change items that do not yet have grades - // otherwise we will need to implement grade recalculations and it is not worth the trouble. + + // only change items that do not yet have grades. + // otherwise we will need to implement grade recalculations and it is not worth the trouble. . // if grades are given, you likely don't want to change it like this anyway. - - if(!$gi->has_grades()){ + + if (!$gi->has_grades()) { $gi->gradetype = GRADE_TYPE_SCALE; $gi->scaleid = $scale->id; $gi->grademin = 1; $gi->grademax = $scalemax; - $gi->gradepass = $scalepass; + $gi->gradepass = $scalepass; + + // Update grade_item. + $result = $gi->update("local/treestudyplan"); // update, signalling with our signature and bulkupdate. - // Update grade_item - $result = $gi->update("local/treestudyplan"); // update, signalling with our signature and bulkupdate - $debug = ""; - if($result){ $updated = "converted";} + if ($result) { $updated = "converted";} else { $updated = "error";} - // next update the activity's table if it has a grade field + // next update the activity's table if it has a grade field. // grade is generally set to the negative scale id if it is a scale. $tablename = $gi->itemmodule; $fieldname = "grade"; - if($result && $gi->itemtype == "mod" && $dbman->table_exists($tablename)){ - if($dbman->field_exists($tablename,$fieldname)){ + if ($result && $gi->itemtype == "mod" && $dbman->table_exists($tablename)) { + if ($dbman->field_exists($tablename, $fieldname)) { $grade_value = intval(0-($scale->id)); try { - $DB->set_field($tablename,$fieldname,$grade_value, ["id" => $gi->iteminstance]); - } - catch(\dml_exception $x){ + $DB->set_field($tablename, $fieldname, $grade_value, ["id" => $gi->iteminstance]); + } + catch(\dml_exception $x) { $updated = "fail"; $debug = strval($x); } } } - + } - else { + else { $updated = "skipped"; } @@ -821,34 +779,31 @@ class studyplanservice extends \external_api return $list; - } - + } + /**************************** * * * list_scales * * * ****************************/ - public static function list_scales_parameters() - { + public static function list_scales_parameters() { return new \external_function_parameters( [] ); } - public static function list_scales_returns() - { + public static function list_scales_returns() { return new \external_multiple_structure(new \external_single_structure([ "id" => new \external_value(PARAM_INT, 'id of scale'), "name" => new \external_value(PARAM_TEXT, 'scale name'), ])); } - - public static function list_scales() - { + + public static function list_scales() { global $DB; $list = []; $scales = \grade_scale::fetch_all_global(); - foreach($scales as $scale){ + foreach ($scales as $scale) { $list[] = [ "id" => $scale->id, "name" => $scale->name, @@ -857,61 +812,57 @@ class studyplanservice extends \external_api return $list; - } + } /**************************** * * * disable_autoenddate * * * ****************************/ - - public static function disable_autoenddate_parameters() - { + + public static function disable_autoenddate_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan'), ] ); } - public static function disable_autoenddate_returns() - { + public static function disable_autoenddate_returns() { return success::structure(); } - - public static function disable_autoenddate($studyplan_id) - { + + public static function disable_autoenddate($studyplan_id) { global $DB; - // Validate permissions - webservicehelper::require_capabilities(self::CAP_EDIT,studyplan::findById($studyplan_id)->context()); + // Validate permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, studyplan::findById($studyplan_id)->context()); - // find studyline id's - $studyline_ids = $DB->get_fieldset_select(studyline::TABLE,"id","studyplan_id = :plan_id",['plan_id' => $studyplan_id]); - foreach($studyline_ids as $studyline_id) { - // find id's of studyitems of type course - $records = $DB->get_records(studyitem::TABLE,['line_id' => $studyline_id]); + // find studyline id's. + $studyline_ids = $DB->get_fieldset_select(studyline::TABLE, "id", "studyplan_id = :plan_id", ['plan_id' => $studyplan_id]); + foreach ($studyline_ids as $studyline_id) { + // find id's of studyitems of type course. + $records = $DB->get_records(studyitem::TABLE, ['line_id' => $studyline_id]); - foreach($records as $item_r){ + foreach ($records as $item_r) { $studyitem = new studyitem($item_r->id); - if($studyitem->isValid() && $studyitem->type() == studyitem::COURSE){ - $record = $DB->get_record("course_format_options",["courseid" => $studyitem->courseid(), "name" => "automaticenddate"]); - if($record && $record->value){ + if ($studyitem->isValid() && $studyitem->type() == studyitem::COURSE) { + $record = $DB->get_record("course_format_options", ["courseid" => $studyitem->courseid(), "name" => "automaticenddate"]); + if ($record && $record->value) { $record->value = false; - $DB->update_record("course_format_options",$record); + $DB->update_record("course_format_options", $record); } } } } return success::success()->model(); - } + } /**************************** * * * duplicate studyplan * * * ****************************/ - public static function duplicate_plan_parameters() - { + public static function duplicate_plan_parameters() { return new \external_function_parameters( [ "plan_id" => new \external_value(PARAM_INT, 'id of plan to copy '), "name" => new \external_value(PARAM_TEXT, 'name of copy '), @@ -919,18 +870,16 @@ class studyplanservice extends \external_api ] ); } - public static function duplicate_plan_returns() - { + public static function duplicate_plan_returns() { return studyplan::simple_structure(); } - - public static function duplicate_plan($studyplan_id,$name,$shortname) - { - // Validate permissions - webservicehelper::require_capabilities(self::CAP_EDIT,studyplan::findById($studyplan_id)->context()); - return studyplan::duplicate_plan($studyplan_id,$name,$shortname); - } + public static function duplicate_plan($studyplan_id, $name, $shortname) { + // Validate permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, studyplan::findById($studyplan_id)->context()); + + return studyplan::duplicate_plan($studyplan_id, $name, $shortname); + } /**************************** * * @@ -938,28 +887,25 @@ class studyplanservice extends \external_api * * ****************************/ - public static function export_plan_parameters() - { + public static function export_plan_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of plan to export '), - "format" => new \external_value(PARAM_TEXT,'export format', VALUE_OPTIONAL), + "format" => new \external_value(PARAM_TEXT, 'export format', VALUE_OPTIONAL), ] ); } - public static function export_plan_returns() - { + public static function export_plan_returns() { return studyplan::export_structure(); } - - public static function export_plan($studyplan_id, $format="json") - { + + public static function export_plan($studyplan_id, $format="json") { try{ - // Validate permissions - webservicehelper::require_capabilities(self::CAP_EDIT,studyplan::findById($studyplan_id)->context()); + // Validate permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, studyplan::findById($studyplan_id)->context()); $plan = studyplan::findById($studyplan_id); - if($format == "csv"){ - // FIXME: Make sure this webservice function gets called for the page instead of the studyplan + if ($format == "csv") { + // FIXME: Make sure this webservice function gets called for the page instead of the studyplan. return $plan->pages()[0]->export_page_csv(); } else{ @@ -969,72 +915,65 @@ class studyplanservice extends \external_api catch(\webservice_access_exception $x) { return [ "format" => "", "content" => ""]; } - } + } - public static function export_studylines_parameters() - { + public static function export_studylines_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of plan to export '), ] ); } - public static function export_studylines_returns() - { + public static function export_studylines_returns() { return studyplan::export_structure(); } - - public static function export_studylines($studyplan_id) - { + + public static function export_studylines($studyplan_id) { $systemcontext = webservicehelper::system_context(); try{ - webservicehelper::require_capabilities(self::CAP_EDIT,studyplan::findById($studyplan_id)->context()); + webservicehelper::require_capabilities(self::CAP_EDIT, studyplan::findById($studyplan_id)->context()); $plan = studyplan::findById($studyplan_id); - // FIXME: Make sure this gets called for the page instead of the studyplan + // FIXME: Make sure this gets called for the page instead of the studyplan. return $plan->pages()[0]->export_studylines(); } catch(\webservice_access_exception $x) { return [ "format" => "", "content" => ""]; } - } + } /**************************** * * * import studyplan * * * ****************************/ - public static function import_plan_parameters() - { + public static function import_plan_parameters() { return new \external_function_parameters( [ "content" => new \external_value(PARAM_TEXT, 'import file content'), "format" => new \external_value(PARAM_TEXT, 'import format'), - "context_id" => new \external_value(PARAM_INT, 'context of the study plan',VALUE_DEFAULT), + "context_id" => new \external_value(PARAM_INT, 'context of the study plan', VALUE_DEFAULT), ] ); } - public static function import_plan_returns() - { + public static function import_plan_returns() { return success::structure(); } - - public static function import_plan($content,$format="application/json",$context_id=1) - { + + public static function import_plan($content, $format="application/json", $context_id=1) { try{ - // Validate import context + // Validate import context. $context = webservicehelper::find_context($context_id); - webservicehelper::require_capabilities(self::CAP_EDIT,$context); + webservicehelper::require_capabilities(self::CAP_EDIT, $context); - $result = studyplan::import_studyplan($content,$format,$context_id); + $result = studyplan::import_studyplan($content, $format, $context_id); return (new success($result, "During study plan import"))->model(); } catch(\webservice_access_exception $x) { return success::fail("Access denied")->model(); } - } + } - public static function import_studylines_parameters() - { + public static function import_studylines_parameters() { return new \external_function_parameters( [ "studyplan_id" => new \external_value(PARAM_INT, 'id of plan to export '), "content" => new \external_value(PARAM_TEXT, 'import file content'), @@ -1042,65 +981,63 @@ class studyplanservice extends \external_api ] ); } - public static function import_studylines_returns() - { + public static function import_studylines_returns() { return success::structure(); } - - public static function import_studylines($studyplan_id, $content,$format="application/json") - { + + public static function import_studylines($studyplan_id, $content, $format="application/json") { try{ $plan = studyplan::findById($studyplan_id); - // Validate import context - webservicehelper::require_capabilities(self::CAP_EDIT,$plan->context()); + // Validate import context. + webservicehelper::require_capabilities(self::CAP_EDIT, $plan->context()); - // FIXME: Make sure this gets called for the page instead of the studyplan - // Once proper page management is implemented - $result = $plan->pages()[0]->import_studylines($content,$format); + // FIXME: Make sure this gets called for the page instead of the studyplan. + // Once proper page management is implemented. + $result = $plan->pages()[0]->import_studylines($content, $format); return ($result?success::success():success::fail())->model(); } catch(\webservice_access_exception $x) { return success::fail("Access denied")->model(); } - } - + } + /******************************************************** * * * Read and write course module title and desc * - * Used only in bistate aggregation method * + * Used only in bistate aggregation method * * * ********************************************************/ - public static function submit_cm_editform_parameters(){ + public static function submit_cm_editform_parameters() { return new \external_function_parameters( [ "cmid" => new \external_value(PARAM_INT, 'id of course module'), "formdata" => new \external_value(PARAM_RAW, 'url encoded form data'), ] ); } - public static function submit_cm_editform_returns(){ + public static function submit_cm_editform_returns() { return success::structure(); } /** DEPRECATED, will remove hacked edit form in the future */ - public static function submit_cm_editform($cmid,$formdata){ + public static function submit_cm_editform($cmid, $formdata) { global $CFG; global $DB; // Check the course module exists. $cm = \get_coursemodule_from_id('', $cmid, 0, false, MUST_EXIST); - // Get some context ready + // Get some context ready. $context = \context_course::instance($cm->course); self::validate_context($context); // 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. - // get needed info to create the correct form + // get needed info to create the correct form. list($cm, $context, $module, $data, $cw) = \get_moduleinfo_data($cm, $course); $modmoodleform = "$CFG->dirroot/mod/$module->name/mod_form.php"; if (file_exists($modmoodleform)) { @@ -1110,18 +1047,18 @@ class studyplanservice extends \external_api } $mformclassname = 'mod_'.$module->name.'_mod_form'; - // now hack the received data into $_POST, so the mform thinks it has been submitted "normally" - foreach(explode("&",$formdata) as $pair){ - $p = explode("=",$pair,2); + // now hack the received data into $_POST, so the mform thinks it has been submitted "normally". + foreach (explode("&", $formdata) as $pair) { + $p = explode("=", $pair, 2); $k = urldecode($p[0]); $v = (count($p)>1)?urldecode($p[1]):""; - - if(strpos($k,"[") > 0 && strpos($k,"]") == strlen($k) -1){ - // its a bracketet field, like filename[text] which should be separated and put into a named array - list($k,$h) = explode("[",$k,2); - if(strlen($k) > 0 && strlen($h) > 1){ - $h = rtrim($h,"]"); - if(!array_key_exists($k,$_POST) || !is_array($_POST[$k])){ + + if (strpos($k, "[") > 0 && strpos($k, "]") == strlen($k) -1) { + // its a bracketet field, like filename[text] which should be separated and put into a named array. + list($k, $h) = explode("[", $k, 2); + if (strlen($k) > 0 && strlen($h) > 1) { + $h = rtrim($h, "]"); + if (!array_key_exists($k, $_POST) || !is_array($_POST[$k])) { $_POST[$k] = []; } $_POST[$k][$h] = $v; @@ -1130,9 +1067,9 @@ class studyplanservice extends \external_api $_POST[$k] = $v; } } - + // Now create the mform and update the module... - // update_moduleinfo() actually needs the mform somehow. Hence the ugly hacks + // update_moduleinfo() actually needs the mform somehow. Hence the ugly hacks. $mform = new $mformclassname($data, $cw->section, $cm, $course); $mform->set_data($data); @@ -1150,8 +1087,7 @@ class studyplanservice extends \external_api * * ************************/ - public static function edit_period_parameters() - { + public static function edit_period_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of study item'), "fullname" => new \external_value(PARAM_TEXT, 'Full name of period'), @@ -1160,18 +1096,16 @@ class studyplanservice extends \external_api "enddate" => new \external_value(PARAM_TEXT, 'end date of period'), ]); } - - public static function edit_period_returns() - { + + public static function edit_period_returns() { return period::structure(); } - - public static function edit_period($id,$fullname,$shortname,$startdate,$enddate) - { - + + public static function edit_period($id, $fullname, $shortname, $startdate, $enddate) { + $p = period::findById($id); - webservicehelper::require_capabilities(self::CAP_EDIT,$p->page()->studyplan()->context()); - + webservicehelper::require_capabilities(self::CAP_EDIT, $p->page()->studyplan()->context()); + $p->edit([ 'fullname' => $fullname, @@ -1180,67 +1114,65 @@ class studyplanservice extends \external_api 'enddate' => $enddate, ]); return $p->model(); - + } /************************ * * * Change course timing * - * * + * * ************************/ - public static function course_period_timing_parameters() - { + public static function course_period_timing_parameters() { return new \external_function_parameters( [ "period_id" => new \external_value(PARAM_INT, 'Period number within page'), "course_id"=> new \external_value(PARAM_INT, 'Id of course to adjust dates for'), - "span"=> new \external_value(PARAM_INT, 'Period span (default 1)',VALUE_DEFAULT), + "span"=> new \external_value(PARAM_INT, 'Period span (default 1)', VALUE_DEFAULT), ]); } - - public static function course_period_timing_returns() - { + + public static function course_period_timing_returns() { return courseinfo::editor_structure(); } - - public static function course_period_timing($period_id, $course_id, $span=1){ + + public static function course_period_timing($period_id, $course_id, $span=1) { global $DB; $period = period::findById($period_id); $periodnr = $period->period(); $page = $period->page(); - // Check for studyplan edit permissions - webservicehelper::require_capabilities(self::CAP_EDIT,$page->studyplan()->context()); + // Check for studyplan edit permissions. + webservicehelper::require_capabilities(self::CAP_EDIT, $page->studyplan()->context()); $course = \get_course($course_id); $coursecontext = \context_course::instance($course_id); - if(webservicehelper::has_capabilities("moodle/course:update",$coursecontext)){ + if (webservicehelper::has_capabilities("moodle/course:update", $coursecontext)) { - // Get the proper list of all the periods for this page + // Get the proper list of all the periods for this page. $periods = period::findForPage($page); $pstart = $periods[$periodnr]; - - // Determine end period number - Clip span between 1 and last period - if($span <= 1){ + + // Determine end period number - Clip span between 1 and last period. + if ($span <= 1) { $pend = $pstart; } - else if ($periodnr + ($span - 1) > $page->periods()){ + else if ($periodnr + ($span - 1) > $page->periods()) { $pend = $periods[$page->periods()]; } else { $pend = $periods[$periodnr + ($span - 1)]; } - // First disable the automatic end date option, since it messes with the timing - // Unfortunately there is still no default option to turn this off - $record = $DB->get_record("course_format_options",["courseid" => $course->id, "name" => "automaticenddate"]); - if($record && $record->value){ + // First disable the automatic end date option, since it messes with the timing. + // Unfortunately there is still no default option to turn this off . + $record = $DB->get_record("course_format_options", ["courseid" => $course->id, "name" => "automaticenddate"]); + if ($record && $record->value) { $record->value = false; - $DB->update_record("course_format_options",$record); + $DB->update_record("course_format_options", $record); } - // Actually perform the timing changes, while also updating the module times - // Like what happens on a course "reset" + // Actually perform the timing changes, while also updating the module times. + // Like what happens on a course "reset". reset_course_userdata((object)[ 'id' => $course->id, 'reset_start_date' => $pstart->startdate()->getTimestamp(), @@ -1248,38 +1180,35 @@ class studyplanservice extends \external_api 'reset_start_date_old' => $course->startdate, 'reset_end_date_old' => $course->enddate, ]); - // purge course cache so the dates are properly reflected + // purge course cache so the dates are properly reflected. \course_modinfo::purge_course_cache($course->id); return (new courseinfo($course->id))->editor_model(); } else { - // probably should return a nice message + // probably should return a nice message. throw new \webservice_access_exception("You do not have date change permissions on this course"); } } - public static function set_studyitem_span_parameters() - { + public static function set_studyitem_span_parameters() { return new \external_function_parameters( [ "id" => new \external_value(PARAM_INT, 'id of study item'), "span" => new \external_value(PARAM_INT, 'span of item'), ]); } - - public static function set_studyitem_span_returns() - { + + public static function set_studyitem_span_returns() { return studyitem::editor_structure(); } - - public static function set_studyitem_span($id,$span=null) - { + + public static function set_studyitem_span($id, $span=null) { $o = studyitem::findById($id); - webservicehelper::require_capabilities(self::CAP_EDIT,$o->context()); - + webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); + $config = [ 'span' => $span]; $o->edit($config); return $o->editor_model(); - + } } \ No newline at end of file diff --git a/classes/success.php b/classes/success.php index f80da46..c8d2e6c 100644 --- a/classes/success.php +++ b/classes/success.php @@ -1,4 +1,24 @@ . +/** + * + * @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; } diff --git a/classes/task/autocohortsync.php b/classes/task/autocohortsync.php index 4bc46d4..4691fea 100644 --- a/classes/task/autocohortsync.php +++ b/classes/task/autocohortsync.php @@ -1,4 +1,25 @@ . +/** + * + * @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,24 +41,24 @@ 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(); } $studyplan->clear_csync_changed(); - } + } } \mtrace("Task done"); } else { diff --git a/classes/task/refreshteacherlist.php b/classes/task/refreshteacherlist.php index f258e33..6db0ad1 100644 --- a/classes/task/refreshteacherlist.php +++ b/classes/task/refreshteacherlist.php @@ -1,11 +1,32 @@ . +/** + * + * @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); } diff --git a/classes/teachingfinder.php b/classes/teachingfinder.php index c9dca4e..0dc4565 100644 --- a/classes/teachingfinder.php +++ b/classes/teachingfinder.php @@ -1,22 +1,43 @@ . +/** + * + * @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 + $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) { - $sql = "SELECT i.course_id FROM {local_treestudyplan_item} i - INNER JOIN {local_treestudyplan_line} l ON i.line_id = l.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')){ - $linked = true; - break; // No need to search further + if (is_enrolled($coursecontext, $userid, 'mod/assign:grade')) { + $linked = true; + 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."}"); } diff --git a/cli/prime_students.php b/cli/prime_students.php index d570c21..85d546b 100644 --- a/cli/prime_students.php +++ b/cli/prime_students.php @@ -1,4 +1,25 @@ . +/** + * + * @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()); } } } diff --git a/cli/randomize_grades.php b/cli/randomize_grades.php index 16cc2ea..fecf577 100644 --- a/cli/randomize_grades.php +++ b/cli/randomize_grades.php @@ -1,4 +1,25 @@ . +/** + * + * @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,64 +106,64 @@ $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(); $name = $gi->itemname; $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"); } @@ -155,7 +176,7 @@ foreach($plans as $plan){ } } } - else + else { cli_writeln(" Skipping since it has not started yet"); } diff --git a/db/access.php b/db/access.php index 05830a3..4967393 100644 --- a/db/access.php +++ b/db/access.php @@ -1,4 +1,24 @@ . +/** + * + * @package local_treestudyplan + * @copyright 2023 P.M. Kuipers + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ $capabilities = [ diff --git a/db/services.php b/db/services.php index ea2cba3..66bbdfe 100644 --- a/db/services.php +++ b/db/services.php @@ -1,4 +1,24 @@ . +/** + * + * @package local_treestudyplan + * @copyright 2023 P.M. Kuipers + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ $services = [ "Competency listing" => [ @@ -34,530 +54,530 @@ $services = [ $functions = [ /*************************** - * Studyplan 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 - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - 'capabilities' => 'local/treestudyplan:editstudyplan', // 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_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_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', // 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_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 - '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) - 'ajax' => true, - '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_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_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_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_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_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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + 'capabilities' => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required. + 'loginrequired' => true, + ], /*************************** - * Badge functions + * 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 + * 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 - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - 'capabilities' => 'local/treestudyplan:editstudyplan, local/treestudyplan:selectowngradables', // Advises the admin which capabilities are required + 'capabilities' => '', // 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_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' => '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - '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) - 'ajax' => true, - 'capabilities' => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required + 'capabilities' => '', // 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_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_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_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_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_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', // 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) - 'ajax' => true, - 'capabilities' => 'local/treestudyplan:editstudyplan', // 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_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_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:editstudyplan', // Advises the admin which capabilities are required + 'capabilities' => 'local/treestudyplan:viewuserreports', // 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_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 - '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) - '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_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:viewuserreports', // Advises the admin which capabilities are required + 'capabilities' => 'local/treestudyplan:forcescales', // 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_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. + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). + 'ajax' => true, + '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). '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, - ], + ], ]; diff --git a/db/tasks.php b/db/tasks.php index e6e460f..5e6a842 100644 --- a/db/tasks.php +++ b/db/tasks.php @@ -1,4 +1,25 @@ . +/** + * + * @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', diff --git a/db/upgrade.php b/db/upgrade.php index c3293cd..26480fb 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -1,4 +1,25 @@ . +/** + * + * @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'); diff --git a/doc.php b/doc.php index fc194da..a0ffbfd 100644 --- a/doc.php +++ b/doc.php @@ -1,11 +1,32 @@ . +/** + * + * @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(); diff --git a/edit-invite.php b/edit-invite.php index de49c70..6e9d4da 100644 --- a/edit-invite.php +++ b/edit-invite.php @@ -1,23 +1,32 @@ . +/** + * + * @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,25 +125,25 @@ 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)) { } - else + else { print_error("invaliddata"); } @@ -152,7 +154,7 @@ else if ($data = $mform->get_data()) else { $data = null; - if($unitid > 0) + if ($unitid > 0) { } diff --git a/edit-plan.php b/edit-plan.php index 459d73f..54a7502 100644 --- a/edit-plan.php +++ b/edit-plan.php @@ -1,4 +1,25 @@ . +/** + * + * @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,71 +28,70 @@ 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; } - else + else { $studyplancontext = $systemcontext; } } 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(); @@ -86,11 +106,11 @@ print $OUTPUT->header();
-
+
/ {{ p }} ({{ ctx.studyplancount }}) + > / {{ p }} ({{ ctx.studyplancount }})

@@ -98,7 +118,7 @@ print $OUTPUT->header();   - {{ studyplan.name }} + {{ studyplan.name }}   header();

- header();
- + diff --git a/invitations.php b/invitations.php index 89c927c..9403a08 100644 --- a/invitations.php +++ b/invitations.php @@ -1,14 +1,34 @@ . +/** + * + * @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 "

".get_string('invite_description', 'local_treestudyplan')."

"; $invites = $DB->get_records('local_treestudyplan_invit', array('user_id' => $USER->id)); -print "

".get_string('invite_tablecaption','local_treestudyplan')."

"; +print "

".get_string('invite_tablecaption', 'local_treestudyplan')."

"; print ""; print ""; -print ""; -print ""; -print ""; +print ""; +print ""; +print ""; print ""; print ""; print ""; -if(count($invites) > 0) -{ - foreach($invites as $invite) +if (count($invites) > 0) { + foreach ($invites as $invite) { $testlink = $INVITED_URL."?key={$invite->invitekey}"; print ""; @@ -70,27 +88,27 @@ if(count($invites) > 0) print ""; print ""; } } -else +else { - print ""; + print ""; } print "
".get_string('invite_name','local_treestudyplan')."".get_string('invite_email','local_treestudyplan')."".get_string('invite_date','local_treestudyplan')."".get_string('invite_name', 'local_treestudyplan')."".get_string('invite_email', 'local_treestudyplan')."".get_string('invite_date', 'local_treestudyplan')." 
".userdate($invite->idate, "%x").""; - print ""; + print ""; print "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 " >"; - print ""; + print ""; print "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 " >"; print "
".get_string('invite_table_empty','local_treestudyplan')."
".get_string('invite_table_empty', 'local_treestudyplan')."
"; diff --git a/invited.php b/invited.php index cb754d4..143a862 100644 --- a/invited.php +++ b/invited.php @@ -1,24 +1,43 @@ . +/** + * + * @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 "
"; - print get_string('invalid_invitekey_error','local_treestudyplan'); + print get_string('invalid_invitekey_error', 'local_treestudyplan'); print "
"; 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}" )); diff --git a/lang/en/local_treestudyplan.php b/lang/en/local_treestudyplan.php index 3028e6c..d226f58 100644 --- a/lang/en/local_treestudyplan.php +++ b/lang/en/local_treestudyplan.php @@ -1,4 +1,25 @@ . +/** + * + * @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.'; @@ -40,18 +61,18 @@ $string['invite_confirm_delete'] = 'Are your sure tou want to delete/revoke the $string['invite_desc_new'] = "Create a new invitation"; $string['invite_desc_edit'] = "Edit an existing invitation"; $string['invite_name'] = "Name"; -$string['invite_email'] = "Email"; -$string['invite_date'] = "Date"; +$string['invite_email'] = "Email"; +$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'] = ' -

Dear {$a->invitee},

+

Dear {$a->invitee},

I\'d like to invite you to view my study plan and progess.

The link below gives you access at any time to view the most recent results. Feel free to bookmark this link in your browser.

Click the link below to view the study plan:
{$a->link}

-

Kind regards,
+

Kind regards,
{$a->sender}

'; diff --git a/lang/nl/local_treestudyplan.php b/lang/nl/local_treestudyplan.php index 25777c5..22055e2 100644 --- a/lang/nl/local_treestudyplan.php +++ b/lang/nl/local_treestudyplan.php @@ -1,4 +1,24 @@ . +/** + * + * @package local_treestudyplan + * @copyright 2023 P.M. Kuipers + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ $string['pluginname'] = 'Studieplannen'; @@ -41,12 +61,12 @@ $string['invite_confirm_delete'] = 'Weet je zeker dat je de uitnodiging voor {$a $string['invite_desc_new'] = "Nieuwe uitnodiging maken"; $string['invite_desc_edit'] = "Uitnodiging bewerken"; $string['invite_name'] = "Naam"; -$string['invite_email'] = "Email"; -$string['invite_date'] = "Datum"; +$string['invite_email'] = "Email"; +$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'] = ' -

Beste {$a->invitee},

+

Beste {$a->invitee},

Bij deze wil ik je graag uitnodigen om mijn studieplan en studievoortgang te bekijken.

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.

@@ -54,7 +74,7 @@ $string['invite_mail_text'] = '

Klik op de volgende link om het studieplan te bekijken:
{$a->link}

-

Met vriendelijke groet,
+

Met vriendelijke groet,
{$a->sender}

'; diff --git a/lib.php b/lib.php index e519a8b..707991a 100644 --- a/lib.php +++ b/lib.php @@ -1,4 +1,25 @@ . +/** + * + * @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,47 +36,47 @@ 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, + global_navigation::TYPE_SYSTEM, + null, "local_treestudyplan_myreport", new pix_icon("myreport", '', 'local_treestudyplan') ); $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, + global_navigation::TYPE_CUSTOM , + null, "local_treestudyplan_invitemgmt", new pix_icon("invitemgmt", '', 'local_treestudyplan') ); @@ -63,68 +84,68 @@ 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, + global_navigation::TYPE_SYSTEM , + null, "local_treestudyplan_viewplan", new pix_icon("viewplans", '', 'local_treestudyplan') ); $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, + global_navigation::TYPE_SYSTEM , + null, "local_treestudyplan_editplan", new pix_icon("viewplans", '', 'local_treestudyplan') ); $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"; } - } + } else { $hide_primary_hrefs[] = "/local/treestudyplan/myreport.php"; $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, + global_navigation::TYPE_USER , + null, "local_treestudyplan_invitemgmt", 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, + 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, + global_navigation::TYPE_CATEGORY, + null, "local_treestudyplan_viewplan", new pix_icon("viewplans", '', 'local_treestudyplan') ); - //$node->make_active(); + //$node->make_active();. } } @@ -184,50 +205,49 @@ 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}"; - $data = [ 'permissions'=> '', + $data = [ 'permissions'=> '', 'invitee' => $invite->name, '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'] .= "
    \n"; - if($invite->allow_details ) + if ($invite->allow_details ) { - $data['permissions'] .= "
  • ".get_string('invite_allow_details','local_treestudyplan')."
  • \n"; + $data['permissions'] .= "
  • ".get_string('invite_allow_details', 'local_treestudyplan')."
  • \n"; } - if($invite->allow_calendar) + if ($invite->allow_calendar) { - $data['permissions'] .= "
  • ".get_string('invite_allow_calendar','local_treestudyplan')."
  • \n"; + $data['permissions'] .= "
  • ".get_string('invite_allow_calendar', 'local_treestudyplan')."
  • \n"; } - if($invite->allow_badges) + if ($invite->allow_badges) { - $data['permissions'] .= "
  • ".get_string('invite_allow_badges','local_treestudyplan')."
  • \n"; + $data['permissions'] .= "
  • ".get_string('invite_allow_badges', 'local_treestudyplan')."
  • \n"; } $data['permissions'] .= "

\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 = " - - + . + . {$subject} @@ -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); diff --git a/myreport-embed.php b/myreport-embed.php index e98a738..fcae423 100644 --- a/myreport-embed.php +++ b/myreport-embed.php @@ -1,4 +1,25 @@ . +/** + * + * @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(); diff --git a/myreport.php b/myreport.php index 872a0a1..7a19376 100644 --- a/myreport.php +++ b/myreport.php @@ -1,4 +1,25 @@ . +/** + * + * @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(); ?>
- +
diff --git a/reports.php b/reports.php deleted file mode 100644 index ed59083..0000000 --- a/reports.php +++ /dev/null @@ -1,168 +0,0 @@ -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 "
"; - if(isset($prevstudent)) - { - print " {$prevstudent->name} "; - } - if(isset($nextstudent)) - { - print "{$nextstudent->name} "; - } - print "
"; - print ""; - - print "
"; - - print ""; - - print ""; - - print ""; - - print "
"; - - - 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 ""; - print "
"; - print "
"; - print "
"; - } - } - print "
"; - - print $OUTPUT->footer(); -} diff --git a/settings.php b/settings.php index b77f706..5a8c8cf 100644 --- a/settings.php +++ b/settings.php @@ -1,17 +1,17 @@ . /** @@ -19,7 +19,7 @@ * * @package local_chronotable * @copyright 2017 Alexander Bias, Ulm University - * @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(); @@ -29,10 +29,10 @@ use local_treestudyplan\aggregator; if ($hassiteconfig) { /************************************** - * + * * Main studyplan settings - * - *************************************/ + * + *************************************/ // Create admin settings category. $ADMIN->add('courses', new admin_category('local_treestudyplan', @@ -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') @@ -129,10 +129,10 @@ if ($hassiteconfig) { $ADMIN->add('local_treestudyplan', $page); /************************************** - * + * * Manage plans link (systemwide) - * - *************************************/ + * + *************************************/ $ADMIN->add('local_treestudyplan', new admin_externalpage( 'local_treestudyplan_editplans', @@ -141,57 +141,57 @@ if ($hassiteconfig) { /************************************** - * - * Settings page: Cohort sync - * - *************************************/ + * + * Settings page: Cohort sync + * + *************************************/ $page_csync = new admin_settingpage('local_treestudyplan_settings_cohortsync', 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 - $page_csync->add(new admin_setting_configcheckbox('local_treestudyplan/csync_enable', + // 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'); @@ -206,19 +206,19 @@ if ($hassiteconfig) { $ADMIN->add('local_treestudyplan', $page_csync); /************************************** - * + * * Grade and scale interpretation - * - *************************************/ + * + *************************************/ $ADMIN->add('local_treestudyplan', new admin_externalpage( 'local_treestudyplan_gradeconfig', get_string('cfg_grades', 'local_treestudyplan', null, true), $CFG->wwwroot . '/local/treestudyplan/cfg_grades.php')); /************************************** - * + * * Add the help link (Temporary until a better place is found) - * + * **************************************/ $ADMIN->add('local_treestudyplan', new admin_externalpage( diff --git a/test/bistate_aggregator_test.php b/test/bistate_aggregator_test.php deleted file mode 100644 index 123e2dc..0000000 --- a/test/bistate_aggregator_test.php +++ /dev/null @@ -1,182 +0,0 @@ -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, - ], - ); - } - -} \ No newline at end of file diff --git a/version.php b/version.php index dea835d..285ddb8 100644 --- a/version.php +++ b/version.php @@ -1,7 +1,29 @@ 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 . +/** + * + * @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, diff --git a/view-plan.php b/view-plan.php index f487587..bbd6553 100644 --- a/view-plan.php +++ b/view-plan.php @@ -1,4 +1,25 @@ . +/** + * + * @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,66 +28,65 @@ 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; } - else + else { $studyplancontext = $systemcontext; } } 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(); @@ -78,11 +98,11 @@ print $OUTPUT->header();
-
+
/ {{ p }} ({{ ctx.studyplancount }}) + > / {{ p }} ({{ ctx.studyplancount }})

@@ -90,7 +110,7 @@ print $OUTPUT->header();   - {{ studyplan.name }} + {{ studyplan.name }}  
@@ -104,9 +124,9 @@ print $OUTPUT->header();

-