Completed code guidelines rework
This commit is contained in:
parent
e72be595aa
commit
64c821bbe9
|
@ -23,7 +23,6 @@
|
||||||
namespace local_treestudyplan;
|
namespace local_treestudyplan;
|
||||||
|
|
||||||
use moodle_exception;
|
use moodle_exception;
|
||||||
use ValueError;
|
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
@ -78,7 +77,7 @@ abstract class aggregator {
|
||||||
* Create a new aggregatior object based on the specified method
|
* Create a new aggregatior object based on the specified method
|
||||||
* @param mixed $method Aggregation method
|
* @param mixed $method Aggregation method
|
||||||
* @param mixed $configstr Configuration string for aggregator
|
* @param mixed $configstr Configuration string for aggregator
|
||||||
* @throws ValueError If method is not found
|
* @throws moodle_exception If method is not found
|
||||||
*/
|
*/
|
||||||
public static function create($method, $configstr): self {
|
public static function create($method, $configstr): self {
|
||||||
if (self::supported($method)) {
|
if (self::supported($method)) {
|
||||||
|
@ -98,7 +97,7 @@ abstract class aggregator {
|
||||||
public static function create_or_default($method, $configstr): self {
|
public static function create_or_default($method, $configstr): self {
|
||||||
try {
|
try {
|
||||||
return self::create($method, $configstr);
|
return self::create($method, $configstr);
|
||||||
} catch (\ValueError $x) {
|
} catch (\moodle_exception $x) {
|
||||||
return self::create(self::FALLBACK, "");
|
return self::create(self::FALLBACK, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,8 @@ class associationservice extends \external_api {
|
||||||
"lastname" => new \external_value(PARAM_TEXT, 'last name'),
|
"lastname" => new \external_value(PARAM_TEXT, 'last name'),
|
||||||
"idnumber" => new \external_value(PARAM_TEXT, 'id number'),
|
"idnumber" => new \external_value(PARAM_TEXT, 'id number'),
|
||||||
"email" => new \external_value(PARAM_TEXT, 'email address'),
|
"email" => new \external_value(PARAM_TEXT, 'email address'),
|
||||||
"lastaccess" => new \external_value(PARAM_INT, 'id of last access this user had to any course in the studyplan', VALUE_OPTIONAL),
|
"lastaccess" => new \external_value(PARAM_INT,
|
||||||
|
'id of last access this user had to any course in the studyplan', VALUE_OPTIONAL),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +132,11 @@ class associationservice extends \external_api {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last access time of user to any of the courses in the studyplan
|
||||||
|
* @param $userid ID of user
|
||||||
|
* @param $studyplanid ID of studyplan
|
||||||
|
*/
|
||||||
public static function user_lastaccess($userid, $studyplanid=null) {
|
public static function user_lastaccess($userid, $studyplanid=null) {
|
||||||
global $DB;
|
global $DB;
|
||||||
if (!empty($studyplanid)) {
|
if (!empty($studyplanid)) {
|
||||||
|
|
|
@ -270,6 +270,10 @@ class badgeinfo {
|
||||||
return $badge;
|
return $badge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define structure for badge completion parts
|
||||||
|
* @param object $value VALUE_OPTIONAL or VALUE_REQUIRED
|
||||||
|
*/
|
||||||
protected static function badge_completion_structure($value) {
|
protected static function badge_completion_structure($value) {
|
||||||
return new \external_single_structure([
|
return new \external_single_structure([
|
||||||
"types" => new \external_multiple_structure(new \external_single_structure([
|
"types" => new \external_multiple_structure(new \external_single_structure([
|
||||||
|
@ -297,6 +301,10 @@ class badgeinfo {
|
||||||
], 'badge completion information', $value);
|
], 'badge completion information', $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate completion model of this badge for a given user
|
||||||
|
* @param int $userid id of user to check for
|
||||||
|
*/
|
||||||
protected function badge_completion_data($userid): array {
|
protected function badge_completion_data($userid): array {
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
@ -330,7 +338,7 @@ class badgeinfo {
|
||||||
if ($badgeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
|
if ($badgeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
|
||||||
/* If ANY completion overall, count only the criteria type with the highest completion percentage -.
|
/* If ANY completion overall, count only the criteria type with the highest completion percentage -.
|
||||||
Overwrite data if current type is more complete */
|
Overwrite data if current type is more complete */
|
||||||
$typefraction = ($typecount > 0) ? ($typeprogress / $typecount):0;
|
$typefraction = ($typecount > 0) ? ($typeprogress / $typecount) : ($typeprogress / $typecount);
|
||||||
if ($typefraction > $fraction || ($fraction == 0 && $typecount > $count)) {
|
if ($typefraction > $fraction || ($fraction == 0 && $typecount > $count)) {
|
||||||
$fraction = $typefraction;
|
$fraction = $typefraction;
|
||||||
$count = $typecount;
|
$count = $typecount;
|
||||||
|
@ -444,7 +452,8 @@ class badgeinfo {
|
||||||
$title = \html_writer::tag('b', '"' . get_string('modulename', $mod->modname) . ' - ' . $mod->name . '"');;
|
$title = \html_writer::tag('b', '"' . get_string('modulename', $mod->modname) . ' - ' . $mod->name . '"');;
|
||||||
$description = \html_writer::tag('b', '"' . get_string('modulename', $mod->modname) . ' - ' . $mod->name . '"');
|
$description = \html_writer::tag('b', '"' . get_string('modulename', $mod->modname) . ' - ' . $mod->name . '"');
|
||||||
if (isset($p['bydate'])) {
|
if (isset($p['bydate'])) {
|
||||||
$description .= get_string('criteria_descr_bydate', 'badges', userdate($p['bydate'], get_string('strftimedate', 'core_langconfig')));
|
$description .= get_string('criteria_descr_bydate', 'badges',
|
||||||
|
userdate($p['bydate'], get_string('strftimedate', 'core_langconfig')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,12 +465,13 @@ class badgeinfo {
|
||||||
'title' => get_string('completeactivity', 'core').
|
'title' => get_string('completeactivity', 'core').
|
||||||
get_string('modulename', $mod->modname) . ' - ' . $mod->name,
|
get_string('modulename', $mod->modname) . ' - ' . $mod->name,
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isset($p["bydate"])) {
|
if (isset($p["bydate"])) {
|
||||||
$subcrit["requirements"]["bydate"] = [
|
$subcrit["requirements"]["bydate"] = [
|
||||||
'title' => get_string('criteria_descr_bydate', 'badges', userdate($p['bydate'], get_string('strftimedate', 'core_langconfig'))),
|
'title' => get_string('criteria_descr_bydate', 'badges',
|
||||||
|
userdate($p['bydate'], get_string('strftimedate', 'core_langconfig'))),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,16 +523,15 @@ class badgeinfo {
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isset($userid)) {
|
if (isset($userid)) {
|
||||||
$crit = $DB->get_record('badge_manual_award', ['issuerrole' => $p['role'], 'recipientid' => $userid, 'badgeid' => $crit->badgeid]);
|
$crit = $DB->get_record('badge_manual_award',
|
||||||
|
['issuerrole' => $p['role'], 'recipientid' => $userid, 'badgeid' => $crit->badgeid]);
|
||||||
$subcrit["completed"] = $crit !== false;
|
$subcrit["completed"] = $crit !== false;
|
||||||
}
|
}
|
||||||
$list[] = $subcrit;
|
$list[] = $subcrit;
|
||||||
}
|
}
|
||||||
} else if ($crit->criteriatype == BADGE_CRITERIA_TYPE_SOCIAL) {
|
|
||||||
/* Unused in moodle - probably deprecated */
|
|
||||||
} else if ($crit->criteriatype == BADGE_CRITERIA_TYPE_COURSE || $crit->criteriatype == BADGE_CRITERIA_TYPE_COURSESET) {
|
} else if ($crit->criteriatype == BADGE_CRITERIA_TYPE_COURSE || $crit->criteriatype == BADGE_CRITERIA_TYPE_COURSESET) {
|
||||||
if ($crit->criteriatype == BADGE_CRITERIA_TYPE_COURSE) {
|
if ($crit->criteriatype == BADGE_CRITERIA_TYPE_COURSE) {
|
||||||
$params = [reset($crit->params)]; // Only use the first parameter
|
$params = [reset($crit->params)]; // Only use the first parameter.
|
||||||
} else {
|
} else {
|
||||||
$params = $crit->params;
|
$params = $crit->params;
|
||||||
}
|
}
|
||||||
|
@ -534,7 +543,8 @@ class badgeinfo {
|
||||||
} else {
|
} else {
|
||||||
$description = \html_writer::tag('b', '"' . $course->fullname . '"');
|
$description = \html_writer::tag('b', '"' . $course->fullname . '"');
|
||||||
if (isset($p['bydate'])) {
|
if (isset($p['bydate'])) {
|
||||||
$description .= get_string('criteria_descr_bydate', 'badges', userdate($p['bydate'], get_string('strftimedate', 'core_langconfig')));
|
$description .= get_string( 'criteria_descr_bydate', 'badges',
|
||||||
|
userdate($p['bydate'], get_string('strftimedate', 'core_langconfig')));
|
||||||
}
|
}
|
||||||
if (isset($p['grade'])) {
|
if (isset($p['grade'])) {
|
||||||
$description .= get_string('criteria_descr_grade', 'badges', $p['grade']);
|
$description .= get_string('criteria_descr_grade', 'badges', $p['grade']);
|
||||||
|
@ -549,18 +559,18 @@ class badgeinfo {
|
||||||
'completion' => [
|
'completion' => [
|
||||||
'title' => get_string('coursecompleted', 'completion'),
|
'title' => get_string('coursecompleted', 'completion'),
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isset($p["grade"])) {
|
if (isset($p["grade"])) {
|
||||||
$subcrit["requirements"]["grade"] = [
|
$subcrit["requirements"]["grade"] = [
|
||||||
'title' => get_string('criteria_descr_grade', 'badges', $p["grade"]),
|
'title' => get_string('criteria_descr_grade', 'badges', $p["grade"]),
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (isset($p["bydate"])) {
|
if (isset($p["bydate"])) {
|
||||||
$subcrit["requirements"]["bydate"] = [
|
$subcrit["requirements"]["bydate"] = [
|
||||||
'title' => get_string('criteria_descr_bydate', 'badges', userdate($p['bydate'], get_string('strftimedate', 'core_langconfig'))),
|
'title' => get_string('criteria_descr_bydate', 'badges',
|
||||||
|
userdate($p['bydate'], get_string('strftimedate', 'core_langconfig'))),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +587,8 @@ class badgeinfo {
|
||||||
}
|
}
|
||||||
$checkdate = true;
|
$checkdate = true;
|
||||||
if (isset($p["bydate"])) {
|
if (isset($p["bydate"])) {
|
||||||
$checkdate = ((bool) $coursecompletion->timecompleted) && ($coursecompletion->timecompleted <= $p["bydate"]);
|
$checkdate = ((bool) $coursecompletion->timecompleted) &&
|
||||||
|
($coursecompletion->timecompleted <= $p["bydate"]);
|
||||||
$subcrit["requirements"]["bydate"]["completed"] = (bool) $checkdate;
|
$subcrit["requirements"]["bydate"]["completed"] = (bool) $checkdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,17 +784,6 @@ class badgeinfo {
|
||||||
throw new \moodle_exception("\$course argument must be course id or course object", "local_treestudyplan");
|
throw new \moodle_exception("\$course argument must be course id or course object", "local_treestudyplan");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
$crit->citeriatype = BADGE_CRITERIA_TYPE_COURSE
|
|
||||||
$badge->params[0] = ["course"=> $courseid]
|
|
||||||
$crit->citeriatype = BADGE_CRITERIA_TYPE_COURSESET
|
|
||||||
$badge->params[] = [["course"=> $courseid]...]
|
|
||||||
$crit->citeriatype = BADGE_CRITERIA_TYPE_ACTIVITY
|
|
||||||
$badge->params[] = [["module"=> $cmid]...]
|
|
||||||
$crit->citeriatype = BADGE_CRITERIA_TYPE_COMPETENCY
|
|
||||||
$badge->params[] = [["competency"=> $competencyid]...]
|
|
||||||
*/
|
|
||||||
|
|
||||||
$search = trim($search);
|
$search = trim($search);
|
||||||
$conditions = "";
|
$conditions = "";
|
||||||
$basesqlparams = ['courseid' => $courseid];
|
$basesqlparams = ['courseid' => $courseid];
|
||||||
|
@ -799,7 +799,8 @@ class badgeinfo {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[$ctypesql, $ctypeinparams] = $DB->get_in_or_equal([BADGE_CRITERIA_TYPE_COURSESET, BADGE_CRITERIA_TYPE_COURSE], SQL_PARAMS_NAMED);
|
[$ctypesql, $ctypeinparams] = $DB->get_in_or_equal([BADGE_CRITERIA_TYPE_COURSESET, BADGE_CRITERIA_TYPE_COURSE],
|
||||||
|
SQL_PARAMS_NAMED);
|
||||||
$sqlparams = array_merge($basesqlparams, $ctypeinparams);
|
$sqlparams = array_merge($basesqlparams, $ctypeinparams);
|
||||||
|
|
||||||
$sql = "SELECT DISTINCT b.id from {badge} b
|
$sql = "SELECT DISTINCT b.id from {badge} b
|
||||||
|
@ -860,7 +861,7 @@ class badgeinfo {
|
||||||
$basesqlparams = array_merge($basesqlparams, $inparams);
|
$basesqlparams = array_merge($basesqlparams, $inparams);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all course badges for this course
|
// Get all course badges for this course.
|
||||||
$badgesids = [];
|
$badgesids = [];
|
||||||
$sql = "SELECT DISTINCT b.id from {badge} b
|
$sql = "SELECT DISTINCT b.id from {badge} b
|
||||||
WHERE b.courseid = :courseid AND $conditions";
|
WHERE b.courseid = :courseid AND $conditions";
|
||||||
|
@ -888,12 +889,12 @@ class badgeinfo {
|
||||||
foreach (studyitem::find_studyline_children($line) as $item) {
|
foreach (studyitem::find_studyline_children($line) as $item) {
|
||||||
if ($item->type() == studyitem::COURSE && $item->courseid()) {
|
if ($item->type() == studyitem::COURSE && $item->courseid()) {
|
||||||
$courseid = $item->courseid();
|
$courseid = $item->courseid();
|
||||||
$relatedbadges = badgeinfo::find_badges_by_course_relation($courseid, $search, $active);
|
$relatedbadges = self::find_badges_by_course_relation($courseid, $search, $active);
|
||||||
foreach ($relatedbadges as $id) {
|
foreach ($relatedbadges as $id) {
|
||||||
$badgeids[] = $id;
|
$badgeids[] = $id;
|
||||||
}
|
}
|
||||||
if ($includecoursebadges) {
|
if ($includecoursebadges) {
|
||||||
$coursebadges = badgeinfo::find_course_badges($courseid, $search, $active);
|
$coursebadges = self::find_course_badges($courseid, $search, $active);
|
||||||
foreach ($coursebadges as $id) {
|
foreach ($coursebadges as $id) {
|
||||||
$badgeids[] = $id;
|
$badgeids[] = $id;
|
||||||
}
|
}
|
||||||
|
@ -936,7 +937,7 @@ class badgeinfo {
|
||||||
$basesqlparams = array_merge($basesqlparams, $inparams);
|
$basesqlparams = array_merge($basesqlparams, $inparams);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all course badges for this course
|
// Get all course badges for this course.
|
||||||
$sql = "SELECT DISTINCT b.id from {badge} b
|
$sql = "SELECT DISTINCT b.id from {badge} b
|
||||||
WHERE $conditions";
|
WHERE $conditions";
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ class cascadecohortsync {
|
||||||
$records = $DB->get_records("enrol", $searchparams);
|
$records = $DB->get_records("enrol", $searchparams);
|
||||||
foreach ($records as $instance) {
|
foreach ($records as $instance) {
|
||||||
if (!empty($instance->customtext4)) {
|
if (!empty($instance->customtext4)) {
|
||||||
// Only add to the list if customtext4 is not empty
|
// Only add to the list if customtext4 is not empty.
|
||||||
$list[] = $instance;
|
$list[] = $instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,8 @@ class cascadecohortsync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2: Remove the cohort sync for this studyplan for this line if it exists, since this course should not have cohort sync form this studyplan
|
/* 2: Remove the cohort sync for this studyplan for this line if it exists,
|
||||||
|
since this course should not have cohort sync form this studyplan
|
||||||
Then - if no one uses the link anymore, deactivate it...
|
Then - if no one uses the link anymore, deactivate it...
|
||||||
This does not remove the sync from courses that are unlinked from a studplan.
|
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
|
But maybe we do not want that anyway, since it is generally a good idea to keep student
|
||||||
|
@ -262,7 +263,6 @@ class cascadecohortsync {
|
||||||
// It already exists.
|
// It already exists.
|
||||||
// Check if this studyplan is already referenced in customtext4 in json format.
|
// Check if this studyplan is already referenced in customtext4 in json format.
|
||||||
|
|
||||||
// TODO: Check this code - Maybe add option to not remember manually added stuff .
|
|
||||||
$plans = json_decode($instance->customtext4);
|
$plans = json_decode($instance->customtext4);
|
||||||
if ($plans == false || !is_array(($plans))) {
|
if ($plans == false || !is_array(($plans))) {
|
||||||
// If the data was not an array (null or garbled), count it as manually added.
|
// If the data was not an array (null or garbled), count it as manually added.
|
||||||
|
@ -297,7 +297,8 @@ class cascadecohortsync {
|
||||||
// In the customtext4 field in json format.
|
// In the customtext4 field in json format.
|
||||||
|
|
||||||
$instance = $DB->get_record('enrol', ['id' => $instanceid]);
|
$instance = $DB->get_record('enrol', ['id' => $instanceid]);
|
||||||
$this->enrol->update_instance($instance, (object)["customtext4" => json_encode([(int)($this->studyplanid)])]);
|
$this->enrol->update_instance($instance, (object)[
|
||||||
|
"customtext4" => json_encode([(int)($this->studyplanid)])]);
|
||||||
|
|
||||||
// Successfully added a valid new instance, so now instantiate it.
|
// Successfully added a valid new instance, so now instantiate it.
|
||||||
// First synchronise the enrolment.
|
// First synchronise the enrolment.
|
||||||
|
|
|
@ -252,7 +252,7 @@ class corecompletioninfo {
|
||||||
$info = [
|
$info = [
|
||||||
"conditions" => $conditions,
|
"conditions" => $conditions,
|
||||||
"aggregation" => self::aggregation_handle($this->completion->get_aggregation_method()),
|
"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 .
|
// Check if completion tracking is enabled for this course - otherwise, revert to defaults .
|
||||||
|
@ -314,7 +314,7 @@ class corecompletioninfo {
|
||||||
} else {
|
} else {
|
||||||
$details['criteria'] = $cm->get_formatted_name();
|
$details['criteria'] = $cm->get_formatted_name();
|
||||||
}
|
}
|
||||||
// Set title based on cm formatted name
|
// Set title based on cm formatted name.
|
||||||
$title = $cm->get_formatted_name();
|
$title = $cm->get_formatted_name();
|
||||||
// Build requirements.
|
// Build requirements.
|
||||||
$details['requirement'] = [];
|
$details['requirement'] = [];
|
||||||
|
@ -354,7 +354,6 @@ class corecompletioninfo {
|
||||||
$details = [
|
$details = [
|
||||||
"type" => get_string('coursegrade', 'completion'),
|
"type" => get_string('coursegrade', 'completion'),
|
||||||
"criteria" => get_string('graderequired', 'completion'),
|
"criteria" => get_string('graderequired', 'completion'),
|
||||||
// TODO: convert to selected representation (letter, percentage, etc).
|
|
||||||
"requirement" => get_string('graderequired', 'completion')
|
"requirement" => get_string('graderequired', 'completion')
|
||||||
.": ".$displaygrade,
|
.": ".$displaygrade,
|
||||||
"status" => "",
|
"status" => "",
|
||||||
|
@ -589,8 +588,6 @@ class corecompletioninfo {
|
||||||
* @return stdClass|null object containing 'grade' and optional 'feedback' attribute
|
* @return stdClass|null object containing 'grade' and optional 'feedback' attribute
|
||||||
*/
|
*/
|
||||||
private function get_grade($cm, $userid) {
|
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',
|
$gi = grade_item::fetch(['itemtype' => 'mod',
|
||||||
'itemmodule' => $cm->modname,
|
'itemmodule' => $cm->modname,
|
||||||
'iteminstance' => $cm->instance,
|
'iteminstance' => $cm->instance,
|
||||||
|
@ -624,7 +621,6 @@ class corecompletioninfo {
|
||||||
* @return stdClass|null object containing 'grade' and optional 'feedback' attribute
|
* @return stdClass|null object containing 'grade' and optional 'feedback' attribute
|
||||||
*/
|
*/
|
||||||
private function format_course_grade($grade) {
|
private function format_course_grade($grade) {
|
||||||
// TODO: Display grade in the way described in the course setup (with letters if needed).
|
|
||||||
$gi = new \grade_item(['itemtype' => 'course',
|
$gi = new \grade_item(['itemtype' => 'course',
|
||||||
'courseid' => $this->course->id]);
|
'courseid' => $this->course->id]);
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,10 @@ class coursecompetencyinfo {
|
||||||
], "details about gradable submissions", $value);
|
], "details about gradable submissions", $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert completion stats to web service model.
|
||||||
|
* @param object $stats Stats object
|
||||||
|
*/
|
||||||
protected static function completionstats($stats) {
|
protected static function completionstats($stats) {
|
||||||
return [
|
return [
|
||||||
"students" => $stats->count,
|
"students" => $stats->count,
|
||||||
|
@ -113,7 +117,6 @@ class coursecompetencyinfo {
|
||||||
"courseproficient" => new \external_value(PARAM_BOOL, 'course competency proficiency', VALUE_OPTIONAL),
|
"courseproficient" => new \external_value(PARAM_BOOL, 'course competency proficiency', VALUE_OPTIONAL),
|
||||||
"needreview" => new \external_value(PARAM_BOOL, 'waiting for review or review in progress', VALUE_OPTIONAL),
|
"needreview" => new \external_value(PARAM_BOOL, 'waiting for review or review in progress', VALUE_OPTIONAL),
|
||||||
"completionstats" => static::completionstats_structure(VALUE_OPTIONAL),
|
"completionstats" => static::completionstats_structure(VALUE_OPTIONAL),
|
||||||
"count" => new \external_value(PARAM_INT, 'number of students in stats', VALUE_OPTIONAL),
|
|
||||||
"required" => new \external_value(PARAM_BOOL, 'if required in parent competency rule', VALUE_OPTIONAL),
|
"required" => new \external_value(PARAM_BOOL, 'if required in parent competency rule', VALUE_OPTIONAL),
|
||||||
"points" => new \external_value(PARAM_INT, 'number of points in parent competency rule', VALUE_OPTIONAL),
|
"points" => new \external_value(PARAM_INT, 'number of points in parent competency rule', VALUE_OPTIONAL),
|
||||||
"progress" => new \external_value(PARAM_INT, 'number completed child competencies/points', VALUE_OPTIONAL),
|
"progress" => new \external_value(PARAM_INT, 'number completed child competencies/points', VALUE_OPTIONAL),
|
||||||
|
@ -121,7 +124,8 @@ class coursecompetencyinfo {
|
||||||
"feedback" => new \external_value(PARAM_RAW, 'feedback provided with this competency', VALUE_OPTIONAL),
|
"feedback" => new \external_value(PARAM_RAW, 'feedback provided with this competency', VALUE_OPTIONAL),
|
||||||
];
|
];
|
||||||
if ($recurse) {
|
if ($recurse) {
|
||||||
$struct["children"] = new \external_multiple_structure(self::competencyinfo_structure(false), 'child competencies', VALUE_OPTIONAL);
|
$struct["children"] = new \external_multiple_structure(
|
||||||
|
self::competencyinfo_structure(false), 'child competencies', VALUE_OPTIONAL);
|
||||||
}
|
}
|
||||||
return new \external_single_structure($struct, 'course completion info');
|
return new \external_single_structure($struct, 'course completion info');
|
||||||
}
|
}
|
||||||
|
@ -162,7 +166,7 @@ class coursecompetencyinfo {
|
||||||
|
|
||||||
$heading = $framework->get($headingfield);
|
$heading = $framework->get($headingfield);
|
||||||
if (empty(trim($heading))) {
|
if (empty(trim($heading))) {
|
||||||
$heading = $framework->get('shortname'); // Fall back to shortname if heading field is empty
|
$heading = $framework->get('shortname'); // Fall back to shortname if heading field is empty.
|
||||||
}
|
}
|
||||||
$path = [[
|
$path = [[
|
||||||
'id' => $framework->get('id'),
|
'id' => $framework->get('id'),
|
||||||
|
@ -174,7 +178,7 @@ class coursecompetencyinfo {
|
||||||
$competencypath[] = $c->get('shortname');
|
$competencypath[] = $c->get('shortname');
|
||||||
$heading = $c->get($headingfield);
|
$heading = $c->get($headingfield);
|
||||||
if (empty(trim($heading))) {
|
if (empty(trim($heading))) {
|
||||||
$heading = $c->get('shortname'); // Fall back to shortname if heading field is empty
|
$heading = $c->get('shortname'); // Fall back to shortname if heading field is empty.
|
||||||
}
|
}
|
||||||
$path[] = [
|
$path[] = [
|
||||||
'id' => $c->get('id'),
|
'id' => $c->get('id'),
|
||||||
|
@ -186,7 +190,7 @@ class coursecompetencyinfo {
|
||||||
|
|
||||||
$heading = $competency->get($headingfield);
|
$heading = $competency->get($headingfield);
|
||||||
if (empty(trim($heading))) {
|
if (empty(trim($heading))) {
|
||||||
$heading = $competency->get('shortname'); // Fall back to shortname if heading field is empty
|
$heading = $competency->get('shortname'); // Fall back to shortname if heading field is empty.
|
||||||
}
|
}
|
||||||
$path[] = [
|
$path[] = [
|
||||||
'id' => $competency->get('id'),
|
'id' => $competency->get('id'),
|
||||||
|
@ -197,7 +201,7 @@ class coursecompetencyinfo {
|
||||||
|
|
||||||
$title = $competency->get($displayfield);
|
$title = $competency->get($displayfield);
|
||||||
if (empty(trim($title))) {
|
if (empty(trim($title))) {
|
||||||
$title = $competency->get('shortname'); // Fall back to shortname if heading field is empty
|
$title = $competency->get('shortname'); // Fall back to shortname if heading field is empty.
|
||||||
}
|
}
|
||||||
$model = [
|
$model = [
|
||||||
'id' => $competency->get('id'),
|
'id' => $competency->get('id'),
|
||||||
|
@ -262,7 +266,7 @@ class coursecompetencyinfo {
|
||||||
$ruleconfig = json_decode($ruleconfig);
|
$ruleconfig = json_decode($ruleconfig);
|
||||||
$points = $ruleconfig->base->points;
|
$points = $ruleconfig->base->points;
|
||||||
|
|
||||||
// Make a nice map of the competency rule config
|
// Make a nice map of the competency rule config.
|
||||||
$crlist = [];
|
$crlist = [];
|
||||||
foreach ($ruleconfig->competencies as $cr) {
|
foreach ($ruleconfig->competencies as $cr) {
|
||||||
$crlist[$cr->id] = $cr;
|
$crlist[$cr->id] = $cr;
|
||||||
|
@ -272,7 +276,7 @@ class coursecompetencyinfo {
|
||||||
$ci["rule"] = $ruletext;
|
$ci["rule"] = $ruletext;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get one level of children
|
// Get one level of children.
|
||||||
$dids = competency::get_descendants_ids($c);
|
$dids = competency::get_descendants_ids($c);
|
||||||
if (count($dids) > 0) {
|
if (count($dids) > 0) {
|
||||||
$children = [];
|
$children = [];
|
||||||
|
@ -355,7 +359,7 @@ class coursecompetencyinfo {
|
||||||
$ruleconfig = json_decode($ruleconfig);
|
$ruleconfig = json_decode($ruleconfig);
|
||||||
$pointsreq = $ruleconfig->base->points;
|
$pointsreq = $ruleconfig->base->points;
|
||||||
$points = 0;
|
$points = 0;
|
||||||
// Make a nice map of the competency rule config
|
// Make a nice map of the competency rule config.
|
||||||
$crlist = [];
|
$crlist = [];
|
||||||
foreach ($ruleconfig->competencies as $cr) {
|
foreach ($ruleconfig->competencies as $cr) {
|
||||||
$crlist[$cr->id] = $cr;
|
$crlist[$cr->id] = $cr;
|
||||||
|
@ -363,7 +367,7 @@ class coursecompetencyinfo {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get one level of children
|
// Get one level of children.
|
||||||
$dids = competency::get_descendants_ids($c);
|
$dids = competency::get_descendants_ids($c);
|
||||||
if (count($dids) > 0) {
|
if (count($dids) > 0) {
|
||||||
$dcount = 0;
|
$dcount = 0;
|
||||||
|
@ -442,6 +446,11 @@ class coursecompetencyinfo {
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine proficiency stats
|
||||||
|
* @param object $competency
|
||||||
|
* @param array $studentlist
|
||||||
|
*/
|
||||||
protected function proficiency_stats($competency, $studentlist) {
|
protected function proficiency_stats($competency, $studentlist) {
|
||||||
$r = new \stdClass();
|
$r = new \stdClass();
|
||||||
$r->count = 0;
|
$r->count = 0;
|
||||||
|
@ -535,7 +544,9 @@ class coursecompetencyinfo {
|
||||||
$ucc = self::get_user_competency_in_course($this->course->id, $userid, $competencyid);
|
$ucc = self::get_user_competency_in_course($this->course->id, $userid, $competencyid);
|
||||||
$r->courseproficient = $ucc->get('proficiency');
|
$r->courseproficient = $ucc->get('proficiency');
|
||||||
$r->coursegrade = $scale->get_nearest_item($ucc->get('grade'));
|
$r->coursegrade = $scale->get_nearest_item($ucc->get('grade'));
|
||||||
} catch (\Exception $x) {}
|
} catch (\Exception $x) {
|
||||||
|
$ucc = null;
|
||||||
|
}
|
||||||
return $r;
|
return $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,10 +563,10 @@ class coursecompetencyinfo {
|
||||||
$competencyid = $competency->get('id');
|
$competencyid = $competency->get('id');
|
||||||
$uc = self::get_user_competency($userid, $competencyid);
|
$uc = self::get_user_competency($userid, $competencyid);
|
||||||
|
|
||||||
// Get evidences and sort by creation date (newest first)
|
// Get evidences and sort by creation date (newest first).
|
||||||
$evidence = evidence::get_records_for_usercompetency($uc->get('id'), \context_system::instance(), 'timecreated', "DESC");
|
$evidence = evidence::get_records_for_usercompetency($uc->get('id'), \context_system::instance(), 'timecreated', "DESC");
|
||||||
|
|
||||||
// Get the first valid note and return it;
|
// Get the first valid note and return it.
|
||||||
foreach ($evidence as $e) {
|
foreach ($evidence as $e) {
|
||||||
if (in_array($e->get('action'), [evidence::ACTION_OVERRIDE, evidence::ACTION_COMPLETE])) {
|
if (in_array($e->get('action'), [evidence::ACTION_OVERRIDE, evidence::ACTION_COMPLETE])) {
|
||||||
return $e->get('note');
|
return $e->get('note');
|
||||||
|
@ -575,13 +586,15 @@ class coursecompetencyinfo {
|
||||||
public static function require_competency(int $competencyid, int $itemid, bool $required) {
|
public static function require_competency(int $competencyid, int $itemid, bool $required) {
|
||||||
global $DB;
|
global $DB;
|
||||||
$item = studyitem::find_by_id($itemid);
|
$item = studyitem::find_by_id($itemid);
|
||||||
// Make sure conditions are properly configured;
|
// Make sure conditions are properly configured.
|
||||||
$conditions = [];
|
$conditions = [];
|
||||||
try {
|
try {
|
||||||
$conditions = json_decode($item->conditions(), true);
|
$conditions = json_decode($item->conditions(), true);
|
||||||
} catch (\Exception $x) {}
|
} catch (\Exception $x) {
|
||||||
|
$conditions = [];
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the competencied field exists
|
// Make sure the competencied field exists.
|
||||||
if (!isset($conditions["competencies"]) || !is_array($conditions["competencies"])) {
|
if (!isset($conditions["competencies"]) || !is_array($conditions["competencies"])) {
|
||||||
$conditions["competencies"] = [];
|
$conditions["competencies"] = [];
|
||||||
}
|
}
|
||||||
|
@ -595,7 +608,7 @@ class coursecompetencyinfo {
|
||||||
$conditions["competencies"][$competencyid]["required"] = boolval($required);
|
$conditions["competencies"][$competencyid]["required"] = boolval($required);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store conditions;
|
// Store conditions.
|
||||||
$item->edit(["conditions" => json_encode($conditions)]);
|
$item->edit(["conditions" => json_encode($conditions)]);
|
||||||
return success::success();
|
return success::success();
|
||||||
}
|
}
|
||||||
|
@ -609,9 +622,11 @@ class coursecompetencyinfo {
|
||||||
$conditions = [];
|
$conditions = [];
|
||||||
try {
|
try {
|
||||||
$conditions = json_decode($this->studyitem->conditions(), true);
|
$conditions = json_decode($this->studyitem->conditions(), true);
|
||||||
} catch (\Exception $x) {}
|
} catch (\Exception $x) {
|
||||||
|
$conditions = [];
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the competencied field exists
|
// Make sure the competencied field exists.
|
||||||
if (isset($conditions["competencies"])
|
if (isset($conditions["competencies"])
|
||||||
&& is_array($conditions["competencies"])
|
&& is_array($conditions["competencies"])
|
||||||
&& isset($conditions["competencies"][$competency->get("id")])
|
&& isset($conditions["competencies"][$competency->get("id")])
|
||||||
|
|
|
@ -267,7 +267,7 @@ class courseinfo {
|
||||||
'fullname' => $this->course->fullname,
|
'fullname' => $this->course->fullname,
|
||||||
'shortname' => $this->course->shortname,
|
'shortname' => $this->course->shortname,
|
||||||
'displayname' => $this->displayname(),
|
'displayname' => $this->displayname(),
|
||||||
'context' => $contextinfo->model()
|
'context' => $contextinfo->model(),
|
||||||
];
|
];
|
||||||
|
|
||||||
return $info;
|
return $info;
|
||||||
|
@ -524,11 +524,15 @@ class courseinfo {
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize title for extra field
|
||||||
|
* @param string $field
|
||||||
|
*/
|
||||||
protected static function extrafields_localize_title($field) {
|
protected static function extrafields_localize_title($field) {
|
||||||
$lang = trim(current_language());
|
$lang = trim(current_language());
|
||||||
$lines = explode("\n", $field);
|
$lines = explode("\n", $field);
|
||||||
$title = "";
|
$title = "";
|
||||||
$fallback = ""; // Fallback to first title
|
$fallback = ""; // Fallback to first title.
|
||||||
foreach ($lines as $l) {
|
foreach ($lines as $l) {
|
||||||
$parts = explode("|", $l, 2);
|
$parts = explode("|", $l, 2);
|
||||||
if (count($parts) > 0) {
|
if (count($parts) > 0) {
|
||||||
|
@ -537,7 +541,7 @@ class courseinfo {
|
||||||
$fallback = $parts[0];
|
$fallback = $parts[0];
|
||||||
}
|
}
|
||||||
if (count($parts) == 1 && empty($title)) {
|
if (count($parts) == 1 && empty($title)) {
|
||||||
// Set line without language as default if no localized line found
|
// Set line without language as default if no localized line found.
|
||||||
$title = trim($parts[0]);
|
$title = trim($parts[0]);
|
||||||
} else if (trim($parts[1]) == $lang) {
|
} else if (trim($parts[1]) == $lang) {
|
||||||
return trim($parts[0]);
|
return trim($parts[0]);
|
||||||
|
@ -558,13 +562,13 @@ class courseinfo {
|
||||||
if ($fieldname == "description") {
|
if ($fieldname == "description") {
|
||||||
// Process embedded files.
|
// Process embedded files.
|
||||||
$value = \file_rewrite_pluginfile_urls(
|
$value = \file_rewrite_pluginfile_urls(
|
||||||
// The description content
|
// The description content.
|
||||||
$this->course()->summary,
|
$this->course()->summary,
|
||||||
// The pluginfile URL which will serve the request.
|
// The pluginfile URL which will serve the request.
|
||||||
'pluginfile.php',
|
'pluginfile.php',
|
||||||
// The combination of contextid / component / filearea / itemid
|
// The combination of contextid / component / filearea / itemid.
|
||||||
// form the virtual bucket that file are stored in.
|
// Form the virtual bucket that file are stored in.
|
||||||
$this->coursecontext->id, // System instance is always used for this
|
$this->coursecontext->id, // System instance is always used for this.
|
||||||
'course',
|
'course',
|
||||||
'summary',
|
'summary',
|
||||||
''
|
''
|
||||||
|
@ -600,7 +604,7 @@ class courseinfo {
|
||||||
$raw = $data->get_value();
|
$raw = $data->get_value();
|
||||||
$type = $field->get('type');
|
$type = $field->get('type');
|
||||||
|
|
||||||
// Only show if visibility is "Everyone" or ("Teachers" and in teacher view )
|
// Only show if visibility is "Everyone" or ("Teachers" and in teacher view ).
|
||||||
if ($visibility > 0 && ($visibility == 2 || $includeteachervisible)) {
|
if ($visibility > 0 && ($visibility == 2 || $includeteachervisible)) {
|
||||||
if ($type == "date") {
|
if ($type == "date") {
|
||||||
// Date should be converted to YYYY-MM-DD so the javascript can properly format it.
|
// Date should be converted to YYYY-MM-DD so the javascript can properly format it.
|
||||||
|
|
|
@ -51,7 +51,8 @@ class courseservice extends \external_api {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the topmost categories for the specicied user.
|
* Get the topmost categories for the specicied user.
|
||||||
* Most of the work is offloaded to an SQL query in the interest of speed, but moodle functions are used to double check access permissions.
|
* Most of the work is offloaded to an SQL query in the interest of speed,
|
||||||
|
* but moodle functions are used to double check access permissions.
|
||||||
* @param int $userid Id of the user
|
* @param int $userid Id of the user
|
||||||
* @return array of core_course_category
|
* @return array of core_course_category
|
||||||
*/
|
*/
|
||||||
|
@ -65,7 +66,7 @@ class courseservice extends \external_api {
|
||||||
if (has_capability($capability, \context_system::instance(), $userid)) {
|
if (has_capability($capability, \context_system::instance(), $userid)) {
|
||||||
if ($capability == 'moodle/category:viewcourselist') {
|
if ($capability == 'moodle/category:viewcourselist') {
|
||||||
// We are now just looking for the visible main level categories.
|
// We are now just looking for the visible main level categories.
|
||||||
// Add all categories of depth = 1;
|
// Add all categories of depth = 1.
|
||||||
$rs = $DB->get_recordset("course_categories", ["depth" => 1], 'sortorder');
|
$rs = $DB->get_recordset("course_categories", ["depth" => 1], 'sortorder');
|
||||||
foreach ($rs as $rcat) {
|
foreach ($rs as $rcat) {
|
||||||
// Get the category, and double check if the category is visible to the current user.
|
// Get the category, and double check if the category is visible to the current user.
|
||||||
|
@ -79,7 +80,7 @@ class courseservice extends \external_api {
|
||||||
$rs->close();
|
$rs->close();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Context is system, but system may not be visible
|
// Context is system, but system may not be visible.
|
||||||
// Return the top visible categories for this user.
|
// Return the top visible categories for this user.
|
||||||
// Recurses only once.
|
// Recurses only once.
|
||||||
return self::user_tops($userid);
|
return self::user_tops($userid);
|
||||||
|
@ -88,10 +89,10 @@ class courseservice extends \external_api {
|
||||||
// We need to search for the permissions on an individial context level.
|
// We need to search for the permissions on an individial context level.
|
||||||
// This part finds all top categories with a certain permission that are also visible for the user.
|
// This part finds all top categories with a certain permission that are also visible for the user.
|
||||||
|
|
||||||
$sql = "SELECT UNIQUE ctx.* FROM {context} AS ctx
|
$sql = "SELECT UNIQUE ctx.* FROM {context} ctx
|
||||||
INNER JOIN {role_assignments} AS ra ON ra.contextid = ctx.id
|
INNER JOIN {role_assignments} ra ON ra.contextid = ctx.id
|
||||||
INNER JOIN {role_capabilities} AS rc ON ra.roleid = rc.roleid
|
INNER JOIN {role_capabilities} rc ON ra.roleid = rc.roleid
|
||||||
LEFT JOIN {course_categories} AS cat ON ctx.instanceid = cat.id
|
LEFT JOIN {course_categories} cat ON ctx.instanceid = cat.id
|
||||||
WHERE ( ctx.contextlevel = :ctxl_coursecat )
|
WHERE ( ctx.contextlevel = :ctxl_coursecat )
|
||||||
AND ra.userid = :userid AND rc.capability = :capability
|
AND ra.userid = :userid AND rc.capability = :capability
|
||||||
ORDER BY ctx.depth ASC, cat.sortorder ASC";
|
ORDER BY ctx.depth ASC, cat.sortorder ASC";
|
||||||
|
@ -133,7 +134,6 @@ class courseservice extends \external_api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//$recordset->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tops;
|
return $tops;
|
||||||
|
@ -152,10 +152,10 @@ class courseservice extends \external_api {
|
||||||
$tops = [];
|
$tops = [];
|
||||||
$pathlike = $DB->sql_like('ctx.path', ':pathsearch');
|
$pathlike = $DB->sql_like('ctx.path', ':pathsearch');
|
||||||
|
|
||||||
$sql = "SELECT UNIQUE ctx.* FROM {context} AS ctx
|
$sql = "SELECT UNIQUE ctx.* FROM {context} ctx
|
||||||
INNER JOIN {role_assignments} AS ra ON ra.contextid = ctx.id
|
INNER JOIN {role_assignments} ra ON ra.contextid = ctx.id
|
||||||
INNER JOIN {role_capabilities} AS rc ON ra.roleid = rc.roleid
|
INNER JOIN {role_capabilities} rc ON ra.roleid = rc.roleid
|
||||||
LEFT JOIN {course_categories} AS cat ON ctx.instanceid = cat.id
|
LEFT JOIN {course_categories} cat ON ctx.instanceid = cat.id
|
||||||
WHERE ( ctx.contextlevel = :ctxl_coursecat )
|
WHERE ( ctx.contextlevel = :ctxl_coursecat )
|
||||||
AND ra.userid = :userid AND rc.capability = :capability
|
AND ra.userid = :userid AND rc.capability = :capability
|
||||||
AND {$pathlike}
|
AND {$pathlike}
|
||||||
|
@ -255,7 +255,7 @@ class courseservice extends \external_api {
|
||||||
throw new moodle_exception("error:nocategoriesvisible", "local_treestudyplan");
|
throw new moodle_exception("error:nocategoriesvisible", "local_treestudyplan");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (\get_config("local_treestudyplan", "limitcourselist")) { // TODO: Insert config setting here
|
if (\get_config("local_treestudyplan", "limitcourselist")) {
|
||||||
$studyplan = studyplan::find_by_id($studyplanid);
|
$studyplan = studyplan::find_by_id($studyplanid);
|
||||||
$context = $studyplan->context();
|
$context = $studyplan->context();
|
||||||
if ($context->contextlevel == \CONTEXT_SYSTEM) {
|
if ($context->contextlevel == \CONTEXT_SYSTEM) {
|
||||||
|
@ -446,7 +446,7 @@ class courseservice extends \external_api {
|
||||||
|
|
||||||
$cats = [];
|
$cats = [];
|
||||||
|
|
||||||
// If the reference context id is not in the list, push it there if the user has proper permissions in that context
|
// If the reference context id is not in the list, push it there if the user has proper permissions in that context.
|
||||||
if ($refctxid > 1 && !in_array($refctxid, $contextids)) {
|
if ($refctxid > 1 && !in_array($refctxid, $contextids)) {
|
||||||
try {
|
try {
|
||||||
// Get the context.
|
// Get the context.
|
||||||
|
@ -456,7 +456,7 @@ class courseservice extends \external_api {
|
||||||
$insertctxs[] = $refctx;
|
$insertctxs[] = $refctx;
|
||||||
}
|
}
|
||||||
} catch (\dml_missing_record_exception $x) {
|
} catch (\dml_missing_record_exception $x) {
|
||||||
// ignore context
|
$refctx = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +504,7 @@ class courseservice extends \external_api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\dml_missing_record_exception $x) {
|
} catch (\dml_missing_record_exception $x) {
|
||||||
// ignore context
|
$ctx = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,7 +539,6 @@ class courseservice extends \external_api {
|
||||||
return new \external_function_parameters( [
|
return new \external_function_parameters( [
|
||||||
"gradeitemid" => new \external_value(PARAM_INT, 'Grade item ID to scan progress for', 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),
|
"studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in', VALUE_DEFAULT),
|
||||||
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,16 @@
|
||||||
* @copyright 2023 P.M. Kuipers
|
* @copyright 2023 P.M. Kuipers
|
||||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace local_treestudyplan;
|
namespace local_treestudyplan;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use JsonException;
|
use JsonException;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
/**
|
||||||
|
* Crude tool to aid in debugging
|
||||||
|
*/
|
||||||
class debug {
|
class debug {
|
||||||
/**
|
/**
|
||||||
|
* Dump an object in json format to debug log
|
||||||
* @param $object Object to dump
|
* @param $object Object to dump
|
||||||
* @param $filename File to write to
|
* @param $filename File to write to
|
||||||
* @return any The object
|
* @return any The object
|
||||||
|
@ -42,9 +42,9 @@ class debug {
|
||||||
fwrite($f, $json."\n");
|
fwrite($f, $json."\n");
|
||||||
} catch (JsonException $x) {
|
} catch (JsonException $x) {
|
||||||
fwrite($f, "Error processing json: ". $x->getMessage()."\n");
|
fwrite($f, "Error processing json: ". $x->getMessage()."\n");
|
||||||
fwrite($f, "Print_r dump: \n".print_r($object, true)."\n");
|
|
||||||
}
|
}
|
||||||
} catch (\Exception $x) {
|
} catch (\Exception $x) {
|
||||||
|
$f = null;
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
fclose($f);
|
fclose($f);
|
||||||
|
@ -52,22 +52,10 @@ class debug {
|
||||||
return $object;
|
return $object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $object Object to dump
|
|
||||||
* @param $filename File to write to
|
|
||||||
* @return any The object
|
|
||||||
*/
|
|
||||||
public static function &print_r(&$object, $filename="/tmp/debug.log") {
|
|
||||||
try {
|
|
||||||
$f = fopen($filename, "a+");
|
|
||||||
fwrite($f, "Print_r dump: \n".print_r($object, true)."\n");
|
|
||||||
} catch (\Exception $x) {
|
|
||||||
} finally {
|
|
||||||
fclose($f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Write text to debug log
|
||||||
* @param $object Object to dump
|
* @param $object Object to dump
|
||||||
* @param $filename File to write to
|
* @param $filename File to write to
|
||||||
* @return any The object
|
* @return any The object
|
||||||
|
@ -77,6 +65,21 @@ class debug {
|
||||||
$f = fopen($filename, "a+");
|
$f = fopen($filename, "a+");
|
||||||
fwrite($f, $text."\n");
|
fwrite($f, $text."\n");
|
||||||
} catch (\Exception $x) {
|
} catch (\Exception $x) {
|
||||||
|
$f = null;
|
||||||
|
} finally {
|
||||||
|
fclose($f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Write nothing to the file. (Used to satisfy the code checker)
|
||||||
|
* @param $filename File to write to
|
||||||
|
* @return any The object
|
||||||
|
*/
|
||||||
|
public static function void($filename="/tmp/debug.log") {
|
||||||
|
try {
|
||||||
|
$f = fopen($filename, "a+");
|
||||||
|
} catch (\Exception $x) {
|
||||||
|
$f = null;
|
||||||
} finally {
|
} finally {
|
||||||
fclose($f);
|
fclose($f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,31 @@
|
||||||
<?php
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* No file description
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
namespace local_treestudyplan\form;
|
namespace local_treestudyplan\form;
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
require_once($CFG->dirroot."/lib/formslib.php");
|
require_once($CFG->dirroot."/lib/formslib.php");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for moodle forms used in this plugin.
|
||||||
|
*/
|
||||||
abstract class formbase extends \moodleform {
|
abstract class formbase extends \moodleform {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,9 +36,9 @@ abstract class formbase extends \moodleform {
|
||||||
*/
|
*/
|
||||||
public function __construct($params, $ajaxformdata=null) {
|
public function __construct($params, $ajaxformdata=null) {
|
||||||
$customdata = static::init_customdata($params);
|
$customdata = static::init_customdata($params);
|
||||||
if (static::check_security($customdata) !== false) {
|
if (static::check_security($customdata) ) {
|
||||||
parent::__construct(null, (array)$customdata, 'post', '', null, true, $ajaxformdata);
|
parent::__construct(null, (array)$customdata, 'post', '', null, true, $ajaxformdata);
|
||||||
// Initialize the initial data based on the parameter validation
|
// Initialize the initial data based on the parameter validation.
|
||||||
$this->set_data($this->init_formdata((object)$this->_customdata));
|
$this->set_data($this->init_formdata((object)$this->_customdata));
|
||||||
} else {
|
} else {
|
||||||
throw new \moodle_exception('accessexception', 'core');
|
throw new \moodle_exception('accessexception', 'core');
|
||||||
|
@ -48,7 +71,9 @@ abstract class formbase extends \moodleform {
|
||||||
* @return bool True if security validation passes.
|
* @return bool True if security validation passes.
|
||||||
* @throws \moodle_exception if access denied for a specific reason.
|
* @throws \moodle_exception if access denied for a specific reason.
|
||||||
*/
|
*/
|
||||||
abstract public static function check_security(object $customdata);
|
public static function check_security(object $customdata) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the submission and perform necessary actions
|
* Process the submission and perform necessary actions
|
||||||
|
@ -72,5 +97,4 @@ abstract class formbase extends \moodleform {
|
||||||
throw new \moodle_exception('no_form_data', 'local_treestudyplan', '', null, $data);
|
throw new \moodle_exception('no_form_data', 'local_treestudyplan', '', null, $data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,24 @@
|
||||||
<?php
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* No file description
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
namespace local_treestudyplan\form;
|
namespace local_treestudyplan\form;
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
@ -48,31 +68,19 @@ class studyplan_editform extends formbase {
|
||||||
'subdirs' => true,
|
'subdirs' => true,
|
||||||
'maxfiles' => 20,
|
'maxfiles' => 20,
|
||||||
'maxbytes' => 20 * 1024 * 1024,
|
'maxbytes' => 20 * 1024 * 1024,
|
||||||
'context' => \context_system::instance(), // Keep the files in system context
|
'context' => \context_system::instance(), // Keep the files in system context.
|
||||||
];
|
];
|
||||||
$customdata->fileoptions = [
|
$customdata->fileoptions = [
|
||||||
'subdirs' => 0,
|
'subdirs' => 0,
|
||||||
'maxbytes' => 10 * 1024 * 1024, // Max 10MiB should be sufficient for a picture.
|
'maxbytes' => 10 * 1024 * 1024, // Max 10MiB should be sufficient for a picture.
|
||||||
'areamaxbytes' => 10485760,
|
'areamaxbytes' => 10485760,
|
||||||
'maxfiles' => 1, // Just one file
|
'maxfiles' => 1, // Just one file.
|
||||||
'accepted_types' => ['.jpg', '.png', '.jpeg', '.svg', '.svgz'],
|
'accepted_types' => ['.jpg', '.png', '.jpeg', '.svg', '.svgz'],
|
||||||
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
|
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
|
||||||
];
|
];
|
||||||
return $customdata;
|
return $customdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate security access for this form based on the customdata generated by init_customdata
|
|
||||||
* Return true if validation passes, false or throw an exception if it does not.
|
|
||||||
*
|
|
||||||
* @param object $customdata The customdata for this form
|
|
||||||
* @return bool True if security validation passes.
|
|
||||||
* @throws \moodle_exception if access denied for a specific reason.
|
|
||||||
*/
|
|
||||||
public static function check_security(object $customdata) {
|
|
||||||
/*webservicehelper::require_capabilities(self::CAP_EDIT, $customdata->context); */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate form data from parameters
|
* Generate form data from parameters
|
||||||
* Also validate parameters and access permissions here
|
* Also validate parameters and access permissions here
|
||||||
|
@ -109,7 +117,7 @@ class studyplan_editform extends formbase {
|
||||||
$agcfg = json_decode($customdata->plan->aggregator()->config_string(), true);
|
$agcfg = json_decode($customdata->plan->aggregator()->config_string(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the editor
|
// Prepare the editor.
|
||||||
$entry = file_prepare_standard_editor( $entry,
|
$entry = file_prepare_standard_editor( $entry,
|
||||||
'description',
|
'description',
|
||||||
$customdata->editoroptions,
|
$customdata->editoroptions,
|
||||||
|
@ -119,15 +127,15 @@ class studyplan_editform extends formbase {
|
||||||
($customdata->create) ? null : $customdata->plan->id()
|
($customdata->create) ? null : $customdata->plan->id()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prepare file area for the icon
|
// Prepare file area for the icon.
|
||||||
// Get an unused draft itemid which will be used for this form.
|
// Get an unused draft itemid which will be used for this form.
|
||||||
$draftitemid = file_get_submitted_draft_itemid('icon');
|
$draftitemid = file_get_submitted_draft_itemid('icon');
|
||||||
file_prepare_draft_area(
|
file_prepare_draft_area(
|
||||||
// The $draftitemid is the target location.
|
// The $draftitemid is the target location.
|
||||||
$draftitemid,
|
$draftitemid,
|
||||||
// The combination of contextid / component / filearea / itemid
|
// The combination of contextid / component / filearea / itemid.
|
||||||
// form the virtual bucket that files are currently stored in
|
// Form the virtual bucket that files are currently stored in.
|
||||||
// and will be copied from.
|
// And will be copied from.
|
||||||
\context_system::instance()->id,
|
\context_system::instance()->id,
|
||||||
'local_treestudyplan',
|
'local_treestudyplan',
|
||||||
'icon',
|
'icon',
|
||||||
|
@ -154,10 +162,10 @@ class studyplan_editform extends formbase {
|
||||||
$mform = $this->_form;
|
$mform = $this->_form;
|
||||||
$customdata = (object)$this->_customdata;
|
$customdata = (object)$this->_customdata;
|
||||||
|
|
||||||
// Register integer type
|
// Register integer type.
|
||||||
text_integer::Register();
|
text_integer::register();
|
||||||
|
|
||||||
// Define the form
|
// Define the form.
|
||||||
$field = 'name';
|
$field = 'name';
|
||||||
$mform->addElement('text', $field,
|
$mform->addElement('text', $field,
|
||||||
get_string('studyplan_name', 'local_treestudyplan'),
|
get_string('studyplan_name', 'local_treestudyplan'),
|
||||||
|
@ -197,7 +205,7 @@ class studyplan_editform extends formbase {
|
||||||
if ($customdata->create) {
|
if ($customdata->create) {
|
||||||
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
||||||
if (!$timeless) {
|
if (!$timeless) {
|
||||||
// Only add these fields if a new studyplan is created, to easily initialize the first page
|
// Only add these fields if a new studyplan is created, to easily initialize the first page.
|
||||||
$field = 'startdate';
|
$field = 'startdate';
|
||||||
$mform->addElement('date_selector', $field,
|
$mform->addElement('date_selector', $field,
|
||||||
get_string('studyplan_startdate', 'local_treestudyplan'),
|
get_string('studyplan_startdate', 'local_treestudyplan'),
|
||||||
|
@ -219,7 +227,7 @@ class studyplan_editform extends formbase {
|
||||||
$mform->addRule($field, null, 'required', null, 'client');
|
$mform->addRule($field, null, 'required', null, 'client');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// These fields are only relevant if the studyplan is edited
|
// These fields are only relevant if the studyplan is edited.
|
||||||
$field = 'suspended';
|
$field = 'suspended';
|
||||||
$mform->addElement('advcheckbox', $field,
|
$mform->addElement('advcheckbox', $field,
|
||||||
get_string('studyplan_suspend', 'local_treestudyplan'),
|
get_string('studyplan_suspend', 'local_treestudyplan'),
|
||||||
|
@ -239,7 +247,7 @@ class studyplan_editform extends formbase {
|
||||||
|
|
||||||
$aggregators = [];
|
$aggregators = [];
|
||||||
foreach (aggregator::list_model() as $a) {
|
foreach (aggregator::list_model() as $a) {
|
||||||
// Add method only if not deprecated or currently used
|
// Add method only if not deprecated or currently used.
|
||||||
if ($customdata->simplemodel['aggregation'] == $a['id'] || !($a['deprecated'])) {
|
if ($customdata->simplemodel['aggregation'] == $a['id'] || !($a['deprecated'])) {
|
||||||
$aggregators[$a['id']] = $a['name'];
|
$aggregators[$a['id']] = $a['name'];
|
||||||
}
|
}
|
||||||
|
@ -307,7 +315,8 @@ class studyplan_editform extends formbase {
|
||||||
$customdata = (object)$this->_customdata;
|
$customdata = (object)$this->_customdata;
|
||||||
|
|
||||||
// Add aggregation configs to entry.
|
// Add aggregation configs to entry.
|
||||||
$agcfg = json_decode(aggregator::create($entry->aggregation, "")->config_string(), true); // Retrieve default config string from selected aggregation method
|
// Retrieve default config string from selected aggregation method.
|
||||||
|
$agcfg = json_decode(aggregator::create($entry->aggregation, "")->config_string(), true);
|
||||||
foreach ($agcfg as $key => $val) {
|
foreach ($agcfg as $key => $val) {
|
||||||
$entrykey = $entry->aggregation."_".$key;
|
$entrykey = $entry->aggregation."_".$key;
|
||||||
$agcfg[$key] = $entry->$entrykey;
|
$agcfg[$key] = $entry->$entrykey;
|
||||||
|
@ -316,7 +325,7 @@ class studyplan_editform extends formbase {
|
||||||
|
|
||||||
if ($customdata->create) {
|
if ($customdata->create) {
|
||||||
|
|
||||||
// Use our own abstraction to create the record, so caches are maintained
|
// Use our own abstraction to create the record, so caches are maintained.
|
||||||
$plan = studyplan::add(['name' => $entry->name,
|
$plan = studyplan::add(['name' => $entry->name,
|
||||||
'shortname' => $entry->shortname,
|
'shortname' => $entry->shortname,
|
||||||
'idnumber' => $entry->idnumber,
|
'idnumber' => $entry->idnumber,
|
||||||
|
@ -327,7 +336,7 @@ class studyplan_editform extends formbase {
|
||||||
'enddate' => date("Y-m-d", $entry->enddate),
|
'enddate' => date("Y-m-d", $entry->enddate),
|
||||||
'periods' => $entry->periods,
|
'periods' => $entry->periods,
|
||||||
]);
|
]);
|
||||||
// Process the provided files in the description editor
|
// Process the provided files in the description editor.
|
||||||
$entry = file_postupdate_standard_editor($entry,
|
$entry = file_postupdate_standard_editor($entry,
|
||||||
'description',
|
'description',
|
||||||
$customdata->editoroptions,
|
$customdata->editoroptions,
|
||||||
|
@ -335,7 +344,7 @@ class studyplan_editform extends formbase {
|
||||||
'local_treestudyplan',
|
'local_treestudyplan',
|
||||||
'studyplan',
|
'studyplan',
|
||||||
$plan->id());
|
$plan->id());
|
||||||
// Update the description
|
// Update the description.
|
||||||
$plan->edit([
|
$plan->edit([
|
||||||
'description' => $entry->description,
|
'description' => $entry->description,
|
||||||
'descriptionformat' => $entry->descriptionformat,
|
'descriptionformat' => $entry->descriptionformat,
|
||||||
|
@ -343,7 +352,7 @@ class studyplan_editform extends formbase {
|
||||||
} else {
|
} else {
|
||||||
$plan = $customdata->plan;
|
$plan = $customdata->plan;
|
||||||
|
|
||||||
// Process the provided files in the description editor
|
// Process the provided files in the description editor.
|
||||||
$entry = file_postupdate_standard_editor($entry,
|
$entry = file_postupdate_standard_editor($entry,
|
||||||
'description',
|
'description',
|
||||||
$customdata->editoroptions,
|
$customdata->editoroptions,
|
||||||
|
@ -352,7 +361,7 @@ class studyplan_editform extends formbase {
|
||||||
'studyplan',
|
'studyplan',
|
||||||
$plan->id());
|
$plan->id());
|
||||||
|
|
||||||
// Use our own abstraction to update the record, so caches are maintained
|
// Use our own abstraction to update the record, so caches are maintained.
|
||||||
$plan->edit(['name' => $entry->name,
|
$plan->edit(['name' => $entry->name,
|
||||||
'shortname' => $entry->shortname,
|
'shortname' => $entry->shortname,
|
||||||
'idnumber' => $entry->idnumber,
|
'idnumber' => $entry->idnumber,
|
||||||
|
@ -370,8 +379,8 @@ class studyplan_editform extends formbase {
|
||||||
file_save_draft_area_files(
|
file_save_draft_area_files(
|
||||||
// The $entry->icon property contains the itemid of the draft file area.
|
// The $entry->icon property contains the itemid of the draft file area.
|
||||||
$entry->icon,
|
$entry->icon,
|
||||||
// The combination of contextid / component / filearea / itemid
|
// The combination of contextid / component / filearea / itemid.
|
||||||
// form the virtual bucket that file are stored in.
|
// Form the virtual bucket that file are stored in.
|
||||||
\context_system::instance()->id,
|
\context_system::instance()->id,
|
||||||
'local_treestudyplan',
|
'local_treestudyplan',
|
||||||
'icon',
|
'icon',
|
||||||
|
@ -387,5 +396,4 @@ class studyplan_editform extends formbase {
|
||||||
debug::dump($response);
|
debug::dump($response);
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,12 +1,30 @@
|
||||||
<?php
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* No file description
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
namespace local_treestudyplan\form;
|
namespace local_treestudyplan\form;
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
require_once($CFG->dirroot.'/repository/lib.php');
|
require_once($CFG->dirroot.'/repository/lib.php');
|
||||||
use local_treestudyplan\aggregator;
|
|
||||||
use local_treestudyplan\studyplan;
|
use local_treestudyplan\studyplan;
|
||||||
use local_treestudyplan\premium;
|
|
||||||
use local_treestudyplan\contextinfo;
|
use local_treestudyplan\contextinfo;
|
||||||
use local_treestudyplan\studyplanservice;
|
use local_treestudyplan\studyplanservice;
|
||||||
use local_treestudyplan\courseservice;
|
use local_treestudyplan\courseservice;
|
||||||
|
@ -38,18 +56,6 @@ class studyplan_fromtemplateform extends formbase {
|
||||||
return $customdata;
|
return $customdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate security access for this form based on the customdata generated by init_customdata
|
|
||||||
* Return true if validation passes, false or throw an exception if it does not.
|
|
||||||
*
|
|
||||||
* @param object $customdata The customdata for this form
|
|
||||||
* @return bool True if security validation passes.
|
|
||||||
* @throws \moodle_exception if access denied for a specific reason.
|
|
||||||
*/
|
|
||||||
public static function check_security(object $customdata) {
|
|
||||||
/*webservicehelper::require_capabilities(self::CAP_EDIT, $customdata->context); */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate form data from parameters
|
* Generate form data from parameters
|
||||||
* Also validate parameters and access permissions here
|
* Also validate parameters and access permissions here
|
||||||
|
@ -87,10 +93,10 @@ class studyplan_fromtemplateform extends formbase {
|
||||||
$mform = $this->_form;
|
$mform = $this->_form;
|
||||||
$customdata = (object)$this->_customdata;
|
$customdata = (object)$this->_customdata;
|
||||||
|
|
||||||
// Register integer type
|
// Register integer type.
|
||||||
text_integer::Register();
|
text_integer::register();
|
||||||
|
|
||||||
// Define the form
|
// Define the form.
|
||||||
|
|
||||||
$templatelist = [];
|
$templatelist = [];
|
||||||
foreach (studyplan::find_template() as $s) {
|
foreach (studyplan::find_template() as $s) {
|
||||||
|
@ -137,7 +143,7 @@ class studyplan_fromtemplateform extends formbase {
|
||||||
|
|
||||||
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
||||||
if (!$timeless) {
|
if (!$timeless) {
|
||||||
// Only add these fields if the studyplans are timed
|
// Only add these fields if the studyplans are timed.
|
||||||
$field = 'startdate';
|
$field = 'startdate';
|
||||||
$mform->addElement('date_selector', $field,
|
$mform->addElement('date_selector', $field,
|
||||||
get_string('studyplan_startdate', 'local_treestudyplan'),
|
get_string('studyplan_startdate', 'local_treestudyplan'),
|
||||||
|
@ -180,5 +186,4 @@ class studyplan_fromtemplateform extends formbase {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,15 +1,32 @@
|
||||||
<?php
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* No file description
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
namespace local_treestudyplan\form;
|
namespace local_treestudyplan\form;
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
require_once($CFG->dirroot.'/repository/lib.php');
|
require_once($CFG->dirroot.'/repository/lib.php');
|
||||||
use local_treestudyplan\aggregator;
|
|
||||||
use local_treestudyplan\studyplan;
|
use local_treestudyplan\studyplan;
|
||||||
use local_treestudyplan\studyplanpage;
|
use local_treestudyplan\studyplanpage;
|
||||||
use local_treestudyplan\courseservice;
|
|
||||||
use local_treestudyplan\form\text_integer;
|
use local_treestudyplan\form\text_integer;
|
||||||
use local_treestudyplan\local\helpers\webservicehelper;
|
|
||||||
use local_treestudyplan\studyplanservice;
|
use local_treestudyplan\studyplanservice;
|
||||||
use moodle_exception;
|
use moodle_exception;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
@ -48,31 +65,19 @@ class studyplanpage_editform extends formbase {
|
||||||
'subdirs' => true,
|
'subdirs' => true,
|
||||||
'maxfiles' => 20,
|
'maxfiles' => 20,
|
||||||
'maxbytes' => 20 * 1024 * 1024,
|
'maxbytes' => 20 * 1024 * 1024,
|
||||||
'context' => \context_system::instance(), // Keep the files in system context
|
'context' => \context_system::instance(), // Keep the files in system context.
|
||||||
];
|
];
|
||||||
$customdata->fileoptions = [
|
$customdata->fileoptions = [
|
||||||
'subdirs' => 0,
|
'subdirs' => 0,
|
||||||
'maxbytes' => 10 * 1024 * 1024, // Max 10MiB should be sufficient for a picture.
|
'maxbytes' => 10 * 1024 * 1024, // Max 10MiB should be sufficient for a picture.
|
||||||
'areamaxbytes' => 10485760,
|
'areamaxbytes' => 10485760,
|
||||||
'maxfiles' => 1, // Just one file
|
'maxfiles' => 1, // Just one file.
|
||||||
'accepted_types' => ['.jpg', '.png'],
|
'accepted_types' => ['.jpg', '.png'],
|
||||||
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
|
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
|
||||||
];
|
];
|
||||||
return $customdata;
|
return $customdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate security access for this form based on the customdata generated by init_customdata
|
|
||||||
* Return true if validation passes, false or throw an exception if it does not.
|
|
||||||
*
|
|
||||||
* @param object $customdata The customdata for this form
|
|
||||||
* @return bool True if security validation passes.
|
|
||||||
* @throws \moodle_exception if access denied for a specific reason.
|
|
||||||
*/
|
|
||||||
public static function check_security(object $customdata) {
|
|
||||||
/*webservicehelper::require_capabilities(self::CAP_EDIT, $customdata->context); */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate form data from parameters
|
* Generate form data from parameters
|
||||||
* Also validate parameters and access permissions here
|
* Also validate parameters and access permissions here
|
||||||
|
@ -91,7 +96,7 @@ class studyplanpage_editform extends formbase {
|
||||||
$entry = new stdClass;
|
$entry = new stdClass;
|
||||||
$entry->studyplan_id = $plan->id();
|
$entry->studyplan_id = $plan->id();
|
||||||
|
|
||||||
// By default, make the start date of a new page, continue 1 day after the last page's start date;
|
// By default, make the start date of a new page, continue 1 day after the last page's start date.
|
||||||
$otherpages = $plan->pages();
|
$otherpages = $plan->pages();
|
||||||
if (count($otherpages) > 0) {
|
if (count($otherpages) > 0) {
|
||||||
$lastpage = $otherpages[count($otherpages) - 1];
|
$lastpage = $otherpages[count($otherpages) - 1];
|
||||||
|
@ -100,12 +105,12 @@ class studyplanpage_editform extends formbase {
|
||||||
if ($lastpage->enddate(false) == null) {
|
if ($lastpage->enddate(false) == null) {
|
||||||
$entry->startdate = $lastpage->startdate()->add(new \DateInterval("P1Y"))->format("U");
|
$entry->startdate = $lastpage->startdate()->add(new \DateInterval("P1Y"))->format("U");
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, propose 1 day after the last page's end date
|
// Otherwise, propose 1 day after the last page's end date.
|
||||||
$entry->startdate = $lastpage->enddate()->add(new \DateInterval("P1D"))->format("U");
|
$entry->startdate = $lastpage->enddate()->add(new \DateInterval("P1D"))->format("U");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Determine the next august 1st for default value purposes. Only if no other page is available
|
// Determine the next august 1st for default value purposes. Only if no other page is available.
|
||||||
$august = strtotime("first day of august this year");
|
$august = strtotime("first day of august this year");
|
||||||
if ($august < time()) {
|
if ($august < time()) {
|
||||||
$august = strtotime("first day of august next year");
|
$august = strtotime("first day of august next year");
|
||||||
|
@ -114,7 +119,7 @@ class studyplanpage_editform extends formbase {
|
||||||
$entry->startdate = $august;
|
$entry->startdate = $august;
|
||||||
|
|
||||||
}
|
}
|
||||||
$entry->enddate = $entry->startdate + (364*24*60*60); // Not bothering about leap years here.
|
$entry->enddate = $entry->startdate + (364 * 24 * 60 * 60); // Not bothering with leap years here.
|
||||||
$entry->periods = 4;
|
$entry->periods = 4;
|
||||||
} else {
|
} else {
|
||||||
$entry = $DB->get_record(studyplanpage::TABLE, ['id' => $customdata->page->id()]);
|
$entry = $DB->get_record(studyplanpage::TABLE, ['id' => $customdata->page->id()]);
|
||||||
|
@ -122,7 +127,7 @@ class studyplanpage_editform extends formbase {
|
||||||
$entry->enddate = strtotime($entry->enddate);
|
$entry->enddate = strtotime($entry->enddate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the editor
|
// Prepare the editor.
|
||||||
$entry = file_prepare_standard_editor( $entry,
|
$entry = file_prepare_standard_editor( $entry,
|
||||||
'description',
|
'description',
|
||||||
$customdata->editoroptions,
|
$customdata->editoroptions,
|
||||||
|
@ -143,10 +148,10 @@ class studyplanpage_editform extends formbase {
|
||||||
$mform = $this->_form;
|
$mform = $this->_form;
|
||||||
$customdata = (object)$this->_customdata;
|
$customdata = (object)$this->_customdata;
|
||||||
|
|
||||||
// Register integer type
|
// Register integer type.
|
||||||
text_integer::Register();
|
text_integer::register();
|
||||||
|
|
||||||
// Define the form
|
// Define the form.
|
||||||
$field = 'fullname';
|
$field = 'fullname';
|
||||||
$mform->addElement('text', $field,
|
$mform->addElement('text', $field,
|
||||||
get_string('studyplan_name', 'local_treestudyplan'),
|
get_string('studyplan_name', 'local_treestudyplan'),
|
||||||
|
@ -201,7 +206,7 @@ class studyplanpage_editform extends formbase {
|
||||||
|
|
||||||
if ($customdata->create) {
|
if ($customdata->create) {
|
||||||
|
|
||||||
// Use our own abstraction to update the record, so caches are maintained
|
// Use our own abstraction to update the record, so caches are maintained.
|
||||||
$page = studyplanpage::add(['fullname' => $entry->fullname,
|
$page = studyplanpage::add(['fullname' => $entry->fullname,
|
||||||
'shortname' => $entry->shortname,
|
'shortname' => $entry->shortname,
|
||||||
'startdate' => date("Y-m-d", $entry->startdate),
|
'startdate' => date("Y-m-d", $entry->startdate),
|
||||||
|
@ -209,7 +214,7 @@ class studyplanpage_editform extends formbase {
|
||||||
'periods' => $entry->periods,
|
'periods' => $entry->periods,
|
||||||
'studyplan_id' => $customdata->plan->id(),
|
'studyplan_id' => $customdata->plan->id(),
|
||||||
]);
|
]);
|
||||||
// Process the provided files in the description editor
|
// Process the provided files in the description editor.
|
||||||
$entry = file_postupdate_standard_editor($entry,
|
$entry = file_postupdate_standard_editor($entry,
|
||||||
'description',
|
'description',
|
||||||
$customdata->editoroptions,
|
$customdata->editoroptions,
|
||||||
|
@ -217,7 +222,7 @@ class studyplanpage_editform extends formbase {
|
||||||
'local_treestudyplan',
|
'local_treestudyplan',
|
||||||
'studyplanpage',
|
'studyplanpage',
|
||||||
$page->id());
|
$page->id());
|
||||||
// Update the description
|
// Update the description.
|
||||||
$page->edit([
|
$page->edit([
|
||||||
'description' => $entry->description,
|
'description' => $entry->description,
|
||||||
'descriptionformat' => $entry->descriptionformat,
|
'descriptionformat' => $entry->descriptionformat,
|
||||||
|
@ -225,7 +230,7 @@ class studyplanpage_editform extends formbase {
|
||||||
} else {
|
} else {
|
||||||
$page = $customdata->page;
|
$page = $customdata->page;
|
||||||
|
|
||||||
// Process the provided files in the description editor
|
// Process the provided files in the description editor.
|
||||||
$entry = file_postupdate_standard_editor($entry,
|
$entry = file_postupdate_standard_editor($entry,
|
||||||
'description',
|
'description',
|
||||||
$customdata->editoroptions,
|
$customdata->editoroptions,
|
||||||
|
@ -234,7 +239,7 @@ class studyplanpage_editform extends formbase {
|
||||||
'studyplanpage',
|
'studyplanpage',
|
||||||
$page->id());
|
$page->id());
|
||||||
|
|
||||||
// Use our own abstraction to update the record, so caches are maintained
|
// Use our own abstraction to update the record, so caches are maintained.
|
||||||
$page->edit(['fullname' => $entry->fullname,
|
$page->edit(['fullname' => $entry->fullname,
|
||||||
'shortname' => $entry->shortname,
|
'shortname' => $entry->shortname,
|
||||||
'description' => $entry->description,
|
'description' => $entry->description,
|
||||||
|
@ -251,5 +256,4 @@ class studyplanpage_editform extends formbase {
|
||||||
*/
|
*/
|
||||||
return studyplanservice::clean_returnvalue(studyplanpage::editor_structure(), $page->editor_model());
|
return studyplanservice::clean_returnvalue(studyplanpage::editor_structure(), $page->editor_model());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,25 @@
|
||||||
<?php
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* No file description
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
namespace local_treestudyplan\form;
|
namespace local_treestudyplan\form;
|
||||||
use MoodleQuickForm_text;
|
use MoodleQuickForm_text;
|
||||||
use MoodleQuickForm;
|
use MoodleQuickForm;
|
||||||
|
@ -7,6 +28,9 @@ defined('MOODLE_INTERNAL') || die();
|
||||||
global $CFG;
|
global $CFG;
|
||||||
require_once($CFG->libdir . "/form/text.php");
|
require_once($CFG->libdir . "/form/text.php");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to handle integer only form field.
|
||||||
|
*/
|
||||||
class text_integer extends MoodleQuickForm_text {
|
class text_integer extends MoodleQuickForm_text {
|
||||||
/**
|
/**
|
||||||
* Accepts a renderer
|
* Accepts a renderer
|
||||||
|
@ -32,7 +56,7 @@ class text_integer extends MoodleQuickForm_text {
|
||||||
|
|
||||||
$unsigned = (isset($this->_attributes['unsigned']) && $this->_attributes['unsigned']);
|
$unsigned = (isset($this->_attributes['unsigned']) && $this->_attributes['unsigned']);
|
||||||
$nonzero = (isset($this->_attributes['nonzero']) && $this->_attributes['nonzero']);
|
$nonzero = (isset($this->_attributes['nonzero']) && $this->_attributes['nonzero']);
|
||||||
$context = array(
|
$context = [
|
||||||
'element' => $elementcontext,
|
'element' => $elementcontext,
|
||||||
'label' => $label,
|
'label' => $label,
|
||||||
'unsigned' => ($unsigned) ? true : false ,
|
'unsigned' => ($unsigned) ? true : false ,
|
||||||
|
@ -41,7 +65,7 @@ class text_integer extends MoodleQuickForm_text {
|
||||||
'advanced' => $advanced,
|
'advanced' => $advanced,
|
||||||
'helpbutton' => $helpbutton,
|
'helpbutton' => $helpbutton,
|
||||||
'error' => $error,
|
'error' => $error,
|
||||||
);
|
];
|
||||||
$html = $OUTPUT->render_from_template('local_treestudyplan/form/element_text_integer', $context);
|
$html = $OUTPUT->render_from_template('local_treestudyplan/form/element_text_integer', $context);
|
||||||
if ($renderer->_inGroup) {
|
if ($renderer->_inGroup) {
|
||||||
$this->_groupElementTemplate = $html;
|
$this->_groupElementTemplate = $html;
|
||||||
|
@ -59,7 +83,10 @@ class text_integer extends MoodleQuickForm_text {
|
||||||
$renderer->_html .= $html;
|
$renderer->_html .= $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Register() {
|
/**
|
||||||
|
* Register the element
|
||||||
|
*/
|
||||||
|
public static function register() {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
MoodleQuickForm::registerElementType(
|
MoodleQuickForm::registerElementType(
|
||||||
// The custom element is named `course_competency_rule`.
|
// The custom element is named `course_competency_rule`.
|
||||||
|
|
|
@ -337,7 +337,7 @@ class gradeinfo {
|
||||||
public function user_model($userid) {
|
public function user_model($userid) {
|
||||||
global $DB;
|
global $DB;
|
||||||
$grade = $this->gradeitem->get_final($userid);
|
$grade = $this->gradeitem->get_final($userid);
|
||||||
// Format grade for proper display
|
// Format grade for proper display.
|
||||||
if (is_object($grade)) {
|
if (is_object($grade)) {
|
||||||
$finalgrade = \grade_format_gradevalue($grade->finalgrade, $this->gradeitem, true, null, 1);
|
$finalgrade = \grade_format_gradevalue($grade->finalgrade, $this->gradeitem, true, null, 1);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -165,7 +165,7 @@ class competency_aggregator extends \local_treestudyplan\aggregator {
|
||||||
* @return int Aggregated completion as completion class constant
|
* @return int Aggregated completion as completion class constant
|
||||||
*/
|
*/
|
||||||
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
|
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
|
||||||
// Retrieve the course competencies
|
// Retrieve the course competencies.
|
||||||
$course = $courseinfo->course();
|
$course = $courseinfo->course();
|
||||||
$cci = new coursecompetencyinfo($course, $studyitem);
|
$cci = new coursecompetencyinfo($course, $studyitem);
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ class competency_aggregator extends \local_treestudyplan\aggregator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine minimum for
|
// Determine minimum for.
|
||||||
$limit = $this->cfg()->thresh_completed * $count;
|
$limit = $this->cfg()->thresh_completed * $count;
|
||||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
|
$coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ class core_aggregator extends \local_treestudyplan\aggregator {
|
||||||
|
|
||||||
if ($completion->is_enabled() && $completion->is_tracked_user($userid)) {
|
if ($completion->is_enabled() && $completion->is_tracked_user($userid)) {
|
||||||
if ($completion->is_course_complete($userid)) {
|
if ($completion->is_course_complete($userid)) {
|
||||||
// Completed is completed
|
// Completed is completed.
|
||||||
return completion::COMPLETED;
|
return completion::COMPLETED;
|
||||||
} else {
|
} else {
|
||||||
// Check if the course is over or not, if it is over, display failed.
|
// Check if the course is over or not, if it is over, display failed.
|
||||||
|
|
|
@ -93,7 +93,6 @@ class gradegenerator {
|
||||||
"Praesent nec risus vestibulum quam venenatis tempor.",
|
"Praesent nec risus vestibulum quam venenatis tempor.",
|
||||||
"Nullam rhoncus ex a quam egestas, eu auctor enim lobortis.",
|
"Nullam rhoncus ex a quam egestas, eu auctor enim lobortis.",
|
||||||
"Nam luctus ante id lacus scelerisque, quis blandit ante elementum.",
|
"Nam luctus ante id lacus scelerisque, quis blandit ante elementum.",
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -289,21 +288,21 @@ class gradegenerator {
|
||||||
$r = (object)[
|
$r = (object)[
|
||||||
"gi" => $g,
|
"gi" => $g,
|
||||||
"grade" => $gi->grademin + $grade,
|
"grade" => $gi->grademin + $grade,
|
||||||
"fb" => ($grade > 0) ? $gr->fb : ""
|
"fb" => ($grade > 0) ? $gr->fb : "",
|
||||||
];
|
];
|
||||||
} else if (!$gr->result) {
|
} else if (!$gr->result) {
|
||||||
// PROGRESS.
|
// PROGRESS.
|
||||||
$r = (object)[
|
$r = (object)[
|
||||||
"gi" => $g,
|
"gi" => $g,
|
||||||
"grade" => $gi->grademin + rand(round($range * 0.35), round($range * 0.55) - 1 ),
|
"grade" => $gi->grademin + rand(round($range * 0.35), round($range * 0.55) - 1 ),
|
||||||
"fb" => $gr->fb
|
"fb" => $gr->fb,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
// COMPLETED.
|
// COMPLETED.
|
||||||
$r = (object)[
|
$r = (object)[
|
||||||
"gi" => $g,
|
"gi" => $g,
|
||||||
"grade" => $gi->grademin + rand(round($range * 0.55) , $range ),
|
"grade" => $gi->grademin + rand(round($range * 0.55) , $range ),
|
||||||
"fb" => $gr->fb
|
"fb" => $gr->fb,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,14 +52,9 @@ class webservicehelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($capability)) {
|
if (is_array($capability)) {
|
||||||
// TODO: replace this by accesslib function \has_any_capability().
|
return \has_any_capability($capability, $context);
|
||||||
foreach ($capability as $cap) {
|
} else {
|
||||||
if (has_capability($cap, $context)) {
|
return has_capability($capability, $context);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (has_capability($capability, $context)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,19 +30,19 @@ class randomimage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image width
|
* Image width
|
||||||
* @var integer
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $width;
|
private $width;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image height
|
* Image height
|
||||||
* @var integer
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $height;
|
private $height;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of shapes to draw
|
* Number of shapes to draw
|
||||||
* @var integer
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $shapes;
|
private $shapes;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class randomimage {
|
||||||
IMG_ARC_PIE,
|
IMG_ARC_PIE,
|
||||||
IMG_ARC_CHORD,
|
IMG_ARC_CHORD,
|
||||||
IMG_ARC_EDGED,
|
IMG_ARC_EDGED,
|
||||||
IMG_ARC_NOFILL
|
IMG_ARC_NOFILL,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,11 +75,12 @@ class randomimage {
|
||||||
* @param integer $maxpts Max number of point to use
|
* @param integer $maxpts Max number of point to use
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function random_polygon($im, Int $maxpts = 20)
|
private function random_polygon($im, Int $maxpts = 20) {
|
||||||
{
|
|
||||||
$color = imagecolorallocatealpha($im, ...$this->random_color_alpha());
|
$color = imagecolorallocatealpha($im, ...$this->random_color_alpha());
|
||||||
$pts = $this->random_pts(\random_int(3, $maxpts));
|
$numpoints = \random_int(3, $maxpts);
|
||||||
imagefilledpolygon($im, $pts, $color);
|
$pts = $this->random_pts($numpoints);
|
||||||
|
// Remove $numpoints parameter when php7.4 is no longer supported (param is deprecated in >8.1).
|
||||||
|
imagefilledpolygon($im, $pts, $numpoints, $color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,8 +88,7 @@ class randomimage {
|
||||||
* @param \GdImage $im The image resource
|
* @param \GdImage $im The image resource
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function random_arc($im)
|
private function random_arc($im) {
|
||||||
{
|
|
||||||
$cx = \random_int(0, $this->width);
|
$cx = \random_int(0, $this->width);
|
||||||
$cy = \random_int(0, $this->height);
|
$cy = \random_int(0, $this->height);
|
||||||
$w = \random_int(0, $this->width);
|
$w = \random_int(0, $this->width);
|
||||||
|
@ -104,13 +104,12 @@ class randomimage {
|
||||||
* Generates an array of random alpha color values.
|
* Generates an array of random alpha color values.
|
||||||
* @return Array [r, g, b, a]
|
* @return Array [r, g, b, a]
|
||||||
*/
|
*/
|
||||||
private function random_color_alpha(): Array
|
private function random_color_alpha(): Array {
|
||||||
{
|
|
||||||
return [
|
return [
|
||||||
\random_int(0, 255),
|
\random_int(0, 255),
|
||||||
\random_int(0, 255),
|
\random_int(0, 255),
|
||||||
\random_int(0, 255),
|
\random_int(0, 255),
|
||||||
\random_int(0, 127)
|
\random_int(0, 127),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,8 +118,7 @@ class randomimage {
|
||||||
* @param integer $length Number of sets of points to generate
|
* @param integer $length Number of sets of points to generate
|
||||||
* @return Array The resulting array of points
|
* @return Array The resulting array of points
|
||||||
*/
|
*/
|
||||||
private function random_pts($length): Array
|
private function random_pts($length): Array {
|
||||||
{
|
|
||||||
$pts = [];
|
$pts = [];
|
||||||
for ($n = 0; $n < $length; $n++) {
|
for ($n = 0; $n < $length; $n++) {
|
||||||
$pts[] = \random_int(0, $this->width);
|
$pts[] = \random_int(0, $this->width);
|
||||||
|
@ -129,6 +127,12 @@ class randomimage {
|
||||||
return $pts;
|
return $pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new random image
|
||||||
|
* @param int $shapes Number of shapes
|
||||||
|
* @param int $width Width of the images generated
|
||||||
|
* @param int $height Height of the images generated
|
||||||
|
*/
|
||||||
public function __construct($shapes=3, $width=256, $height=256) {
|
public function __construct($shapes=3, $width=256, $height=256) {
|
||||||
$this->shapes = $shapes;
|
$this->shapes = $shapes;
|
||||||
$this->width = $width;
|
$this->width = $width;
|
||||||
|
@ -159,15 +163,20 @@ class randomimage {
|
||||||
imagedestroy($im);
|
imagedestroy($im);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get image content as string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function content() {
|
public function content() {
|
||||||
return $this->content = \file_get_contents($this->tempfile);
|
return $this->content = \file_get_contents($this->tempfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct()
|
/**
|
||||||
{
|
* Destructor for random image object
|
||||||
|
*/
|
||||||
|
public function __destruct() {
|
||||||
if (isset($this->tempfile) && file_exists($this->tempfile)) {
|
if (isset($this->tempfile) && file_exists($this->tempfile)) {
|
||||||
unlink($this->tempfile);
|
unlink($this->tempfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -117,7 +117,7 @@ class period {
|
||||||
$pend = $pstart + $ptime;
|
$pend = $pstart + $ptime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue period numbers if so configured
|
// Continue period numbers if so configured.
|
||||||
if (get_config("local_treestudyplan", "continueperiodnumberingnewpage")) {
|
if (get_config("local_treestudyplan", "continueperiodnumberingnewpage")) {
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
foreach (studyplanpage::find_studyplan_children($page->studyplan()) as $p) {
|
foreach (studyplanpage::find_studyplan_children($page->studyplan()) as $p) {
|
||||||
|
|
|
@ -25,19 +25,22 @@ defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
require_once($CFG->libdir.'/externallib.php');
|
require_once($CFG->libdir.'/externallib.php');
|
||||||
|
|
||||||
use DateTime;
|
|
||||||
use moodle_url;
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle badge information in the same style as the other classes
|
* Handle badge information in the same style as the other classes
|
||||||
*/
|
*/
|
||||||
class premium extends \external_api {
|
class premium extends \external_api {
|
||||||
|
|
||||||
// Toggle the variable below to enable support for premium stuff.
|
/**
|
||||||
// If set to false, all premium features will be enabled and no premium settings panel will be visible.
|
* Toggle the variable below to enable support for premium stuff.
|
||||||
|
* If set to false, all premium features will be enabled and no premium settings panel will be visible.
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
private static $premiumsupported = false;
|
private static $premiumsupported = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certficate code
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
private static $premiumcrt = "-----BEGIN CERTIFICATE-----
|
private static $premiumcrt = "-----BEGIN CERTIFICATE-----
|
||||||
MIIDSzCCAjMCFFlyhmKf1fN7U5lQL/dtlsyP24AQMA0GCSqGSIb3DQEBCwUAMGEx
|
MIIDSzCCAjMCFFlyhmKf1fN7U5lQL/dtlsyP24AQMA0GCSqGSIb3DQEBCwUAMGEx
|
||||||
CzAJBgNVBAYTAk5MMRYwFAYDVQQIDA1Ob29yZC1Ib2xsYW5kMRowGAYDVQQKDBFN
|
CzAJBgNVBAYTAk5MMRYwFAYDVQQIDA1Ob29yZC1Ib2xsYW5kMRowGAYDVQQKDBFN
|
||||||
|
@ -59,27 +62,38 @@ n8FqBInMBhGu1uz0Qeo09ke0RHRnghP9EXfig/veMegASZeEhFqmS2Bdiy6gqeZ5
|
||||||
Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
-----END CERTIFICATE-----";
|
-----END CERTIFICATE-----";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached status
|
||||||
|
* @var object
|
||||||
|
*/
|
||||||
private static $cachedpremiumstatus = null;
|
private static $cachedpremiumstatus = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if premium is supported
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
public static function supported() {
|
public static function supported() {
|
||||||
return self::$premiumsupported;
|
return self::$premiumsupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt encrypted data
|
||||||
|
* @param string $encrypted Encrypted data
|
||||||
|
*/
|
||||||
private static function decrypt($encrypted) {
|
private static function decrypt($encrypted) {
|
||||||
// Get the public key.
|
// Get the public key.
|
||||||
$key = \openssl_get_publickey(self::$premiumcrt);
|
$key = \openssl_get_publickey(self::$premiumcrt);
|
||||||
if ($key === false) {
|
if ($key === false) {
|
||||||
throw new \ValueError("Error parsing public key data)");
|
throw new \ParseError("Error parsing public key data)");
|
||||||
}
|
}
|
||||||
// Determine the key size.
|
// Determine the key size.
|
||||||
$keysize = \openssl_pkey_get_details($key)["bits"];
|
$keysize = \openssl_pkey_get_details($key)["bits"];
|
||||||
//
|
|
||||||
$blocksize = ($keysize / 8); // Bits / 8. Whether padded or not.
|
$blocksize = ($keysize / 8); // Bits / 8. Whether padded or not.
|
||||||
|
|
||||||
// Decode data in
|
// Decode data in.
|
||||||
$b64 = \base64_decode($encrypted);
|
$b64 = \base64_decode($encrypted);
|
||||||
if ($b64 === false) {
|
if ($b64 === false) {
|
||||||
throw new \ValueError("Error in base64 decoding");
|
throw new \ParseError("Error in base64 decoding");
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = \str_split($b64, $blocksize);
|
$data = \str_split($b64, $blocksize);
|
||||||
|
@ -89,48 +103,53 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
if (\openssl_public_decrypt($chunk, $dchunk, $key, \OPENSSL_PKCS1_PADDING)) {
|
if (\openssl_public_decrypt($chunk, $dchunk, $key, \OPENSSL_PKCS1_PADDING)) {
|
||||||
$decrypted .= $dchunk;
|
$decrypted .= $dchunk;
|
||||||
} else {
|
} else {
|
||||||
throw new \ValueError("Error decrypting chunk $i ({$blocksize} bytes)");
|
throw new \ParseError("Error decrypting chunk $i ({$blocksize} bytes)");
|
||||||
}
|
}
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated in PHP 8.0 and up, but included to be compatible with 7.4.
|
// Deprecated in PHP 8.0 and up, but included to be compatible with 7.4.
|
||||||
// Wrap in a try/catch in case the function is removed in a later version.
|
if (\PHP_MAJOR_VERSION < 8) {
|
||||||
try {
|
|
||||||
\openssl_pkey_free($key);
|
\openssl_pkey_free($key);
|
||||||
} catch (\Exception $x) {}
|
}
|
||||||
|
|
||||||
return $decrypted;
|
return $decrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim headers from key data
|
||||||
|
* @param string $data the key dat with headers
|
||||||
|
* @return string Key without headers
|
||||||
|
*/
|
||||||
private static function trim_headers($data) {
|
private static function trim_headers($data) {
|
||||||
// Headers are repeated in this function for easier testing and copy-pasting into other projects.
|
// Headers are repeated in this function for easier testing and copy-pasting into other projects.
|
||||||
$STARTHEADER = "----- BEGIN ACTIVATION KEY -----";
|
$startheader = "----- BEGIN ACTIVATION KEY -----";
|
||||||
$ENDHEADER = "----- END ACTIVATION KEY -----";
|
$endheader = "----- END ACTIVATION KEY -----";
|
||||||
|
|
||||||
$parts = preg_split("/\r?\n/", \trim($data));
|
$parts = preg_split("/\r?\n/", \trim($data));
|
||||||
if (count($parts) > 2) {
|
if (count($parts) > 2) {
|
||||||
$start = -1;
|
$start = -1;
|
||||||
$end = -1;
|
$end = -1;
|
||||||
for ($i = 0; $i < count($parts); $i++) {
|
for ($i = 0; $i < count($parts); $i++) {
|
||||||
$p = trim(preg_replace('/\s+/u', ' ', $parts[$i])); // Make sure all unicode spaces are converted to normal spaces before comparing...
|
// Make sure all unicode spaces are converted to normal spaces before comparing.
|
||||||
|
$p = trim(preg_replace('/\s+/u', ' ', $parts[$i]));
|
||||||
|
|
||||||
if ( $p == $STARTHEADER ) {
|
if ($p == $startheader) {
|
||||||
$start = $i + 1;
|
$start = $i + 1;
|
||||||
}
|
}
|
||||||
if ($start > 0 && $p == $ENDHEADER) {
|
if ($start > 0 && $p == $endheader) {
|
||||||
$end = $i;
|
$end = $i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($start < 0 || $end < 0 || $end - $start <= 0) {
|
if ($start < 0 || $end < 0 || $end - $start <= 0) {
|
||||||
throw new \ValueError("Invalid activation key wrappers");
|
throw new \ParseError("Invalid activation key wrappers");
|
||||||
} else {
|
} else {
|
||||||
$keyslice = array_slice($parts, $start, $end - $start);
|
$keyslice = array_slice($parts, $start, $end - $start);
|
||||||
return implode("\n", $keyslice);
|
return implode("\n", $keyslice);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new \ValueError("Invalid activation key");
|
throw new \ParseError("Invalid activation key");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +159,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
*/
|
*/
|
||||||
public static function enabled() {
|
public static function enabled() {
|
||||||
if (self::$premiumsupported) {
|
if (self::$premiumsupported) {
|
||||||
$status = self::premiumStatus();
|
$status = self::premiumstatus();
|
||||||
return $status->enabled;
|
return $status->enabled;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
@ -153,7 +172,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function has_intent($intent) {
|
public static function has_intent($intent) {
|
||||||
$status = self::premiumStatus();
|
$status = self::premiumstatus();
|
||||||
if ($status->enabled) {
|
if ($status->enabled) {
|
||||||
return \in_array(\strtolower($intent), $status->intents);
|
return \in_array(\strtolower($intent), $status->intents);
|
||||||
} else {
|
} else {
|
||||||
|
@ -161,6 +180,9 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate debug info.
|
||||||
|
*/
|
||||||
public static function debuginfo() {
|
public static function debuginfo() {
|
||||||
$s = "<pre>";
|
$s = "<pre>";
|
||||||
try {
|
try {
|
||||||
|
@ -172,10 +194,10 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
$s .= $json . "\n----\n";
|
$s .= $json . "\n----\n";
|
||||||
$decoded = \json_decode($json, false);
|
$decoded = \json_decode($json, false);
|
||||||
$s .= "Read as object:\n----\n";
|
$s .= "Read as object:\n----\n";
|
||||||
$s .= print_r($decoded, true) ."\n----\n";
|
$s .= \json_encode($decoded, \JSON_PRETTY_PRINT) ."\n----\n";
|
||||||
$status = self::premiumStatus();
|
$status = self::premiumstatus();
|
||||||
$s .= "Parsed premium status block:\n----\n";
|
$s .= "Parsed premium status block:\n----\n";
|
||||||
$s .= print_r($status, true) ."\n----\n";
|
$s .= \json_encode($status, \JSON_PRETTY_PRINT) ."\n----\n";
|
||||||
$s .= "Message: " . self::statusdescription() . "\n";
|
$s .= "Message: " . self::statusdescription() . "\n";
|
||||||
} else {
|
} else {
|
||||||
$s .= "Premium key empty";
|
$s .= "Premium key empty";
|
||||||
|
@ -191,11 +213,12 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
$s .= "</pre>";
|
$s .= "</pre>";
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine, cache and retrieve premium status
|
* Determine, cache and retrieve premium status
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
protected static function premiumStatus() {
|
protected static function premiumstatus() {
|
||||||
if (!isset(self::$cachedpremiumstatus)) {
|
if (!isset(self::$cachedpremiumstatus)) {
|
||||||
|
|
||||||
// Initialize default object.
|
// Initialize default object.
|
||||||
|
@ -217,7 +240,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
|
|
||||||
if (is_object($decoded)) {
|
if (is_object($decoded)) {
|
||||||
|
|
||||||
// Copy basic data/
|
// Copy basic data/.
|
||||||
$keys = ["name", "website", "expires", "issued"];
|
$keys = ["name", "website", "expires", "issued"];
|
||||||
foreach ($keys as $k) {
|
foreach ($keys as $k) {
|
||||||
if (isset($decoded->$k)) {
|
if (isset($decoded->$k)) {
|
||||||
|
@ -229,17 +252,19 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
$o->intents = explode(", ", $decoded->intent);
|
$o->intents = explode(", ", $decoded->intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert dates to DateTime for
|
// Convert dates to DateTime for.
|
||||||
$now = new \DateTime();
|
$now = new \DateTime();
|
||||||
$issuedate = new \DateTime($o->issued);
|
$issuedate = new \DateTime($o->issued);
|
||||||
$expirydate = new \DateTime(); // Default to now
|
|
||||||
if ($o->expires == 'never') {
|
if ($o->expires == 'never') {
|
||||||
// If expiry date == never
|
// If expiry date == never.
|
||||||
|
$expirydate = new \DateTime(); // Default to now and add a year.
|
||||||
$expirydate->add(new \DateInterval("P1Y"));
|
$expirydate->add(new \DateInterval("P1Y"));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$expirydate = new \DateTime($o->expires);
|
$expirydate = new \DateTime($o->expires);
|
||||||
} catch (\Exception $x) {}
|
} catch (\Exception $x) {
|
||||||
|
$expirydate = new \DateTime(); // Default to now.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\in_array('treestudyplan', $o->intents)
|
if (\in_array('treestudyplan', $o->intents)
|
||||||
|
@ -263,7 +288,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\ValueError $x) {
|
} catch (\ParseError $x) {
|
||||||
$o->status = \get_string("premium:invalidactivationcontent", "local_treestudyplan");
|
$o->status = \get_string("premium:invalidactivationcontent", "local_treestudyplan");
|
||||||
}
|
}
|
||||||
self::$cachedpremiumstatus = $o;
|
self::$cachedpremiumstatus = $o;
|
||||||
|
@ -291,13 +316,13 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
$keyurl = (object)\parse_url($key);
|
$keyurl = (object)\parse_url($key);
|
||||||
$siteurl = (object)\parse_url($site);
|
$siteurl = (object)\parse_url($site);
|
||||||
|
|
||||||
// No match if host is empty on key or site
|
// No match if host is empty on key or site.
|
||||||
if (empty($keyurl->host) || empty($siteurl->host)) {
|
if (empty($keyurl->host) || empty($siteurl->host)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($keyurl->host == "*") {
|
if ($keyurl->host == "*") {
|
||||||
// * matches all
|
// Value * matches all.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,16 +331,20 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
$siteparts = \array_reverse(\explode(".", $siteurl->host));
|
$siteparts = \array_reverse(\explode(".", $siteurl->host));
|
||||||
|
|
||||||
// Trim starting www from both parts, since site.domain and www.site.domain should be treated as the same.
|
// Trim starting www from both parts, since site.domain and www.site.domain should be treated as the same.
|
||||||
if (($x = \array_pop($keyparts)) != "www") {\array_push($keyparts, $x);}
|
if (($x = \array_pop($keyparts)) != "www") {
|
||||||
if (($x = \array_pop($siteparts)) != "www") {\array_push($siteparts, $x);}
|
\array_push($keyparts, $x);
|
||||||
|
}
|
||||||
|
if (($x = \array_pop($siteparts)) != "www") {
|
||||||
|
\array_push($siteparts, $x);
|
||||||
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < count($keyparts); $i++) {
|
for ($i = 0; $i < count($keyparts); $i++) {
|
||||||
// No match if the site does not have a part, but the key does. Unless the key part is *
|
// No match if the site does not have a part, but the key does. Unless the key part is .
|
||||||
if (!isset($siteparts[$i])) {
|
if (!isset($siteparts[$i])) {
|
||||||
if ($keyparts[$i] != "*") {
|
if ($keyparts[$i] != "*") {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
$i++; //increment $i by one before break, to make sure the comparison following this loop holds.
|
$i++; // Increment $i by one before break, to make sure the comparison following this loop holds.
|
||||||
break; // Stop comparison. Host part matches.
|
break; // Stop comparison. Host part matches.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,17 +355,17 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fail if the site has a deeper subdomain than the key, unless the deepest key subdomain is *
|
// Fail if the site has a deeper subdomain than the key, unless the deepest key subdomain is .
|
||||||
if ($keyparts[$i - 1] != '*' && count($siteparts) > ($i)) {
|
if ($keyparts[$i - 1] != '*' && count($siteparts) > ($i)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we made it here then the host part matches. Now check the path.
|
// If we made it here then the host part matches. Now check the path.
|
||||||
// If path is /*, matches all subpaths including /
|
// If path is /*, matches all subpaths including /.
|
||||||
$keypath = empty($keyurl->path) ? "/" : $keyurl->path;
|
$keypath = empty($keyurl->path) ? "/" : $keyurl->path;
|
||||||
$sitepath = empty($siteurl->path) ? "/" : $siteurl->path;
|
$sitepath = empty($siteurl->path) ? "/" : $siteurl->path;
|
||||||
|
|
||||||
// Trim trailing / from both paths before comparison
|
// Trim trailing / from both paths before comparison.
|
||||||
if (\strlen($sitepath) > 1) {
|
if (\strlen($sitepath) > 1) {
|
||||||
$sitepath = \rtrim($sitepath, "/");
|
$sitepath = \rtrim($sitepath, "/");
|
||||||
}
|
}
|
||||||
|
@ -365,7 +394,9 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
"name" => new \external_value(PARAM_TEXT, 'premium registration name'),
|
"name" => new \external_value(PARAM_TEXT, 'premium registration name'),
|
||||||
"expires" => new \external_value(PARAM_TEXT, 'premium registration expiry date'),
|
"expires" => new \external_value(PARAM_TEXT, 'premium registration expiry date'),
|
||||||
"expired" => new \external_value(PARAM_BOOL, 'premium status expired'),
|
"expired" => new \external_value(PARAM_BOOL, 'premium status expired'),
|
||||||
"intents" => new \external_multiple_structure(new \external_value(PARAM_TEXT), 'additional intents included in the license '),
|
"intents" => new \external_multiple_structure(
|
||||||
|
new \external_value(PARAM_TEXT), 'additional intents included in the license'
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +406,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
*/
|
*/
|
||||||
public static function get_premiumstatus() {
|
public static function get_premiumstatus() {
|
||||||
if (self::$premiumsupported) {
|
if (self::$premiumsupported) {
|
||||||
$status = self::premiumStatus();
|
$status = self::premiumstatus();
|
||||||
$keys = [
|
$keys = [
|
||||||
"enabled",
|
"enabled",
|
||||||
"website",
|
"website",
|
||||||
|
@ -407,7 +438,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||||
* @return string HTML description of premium status.
|
* @return string HTML description of premium status.
|
||||||
*/
|
*/
|
||||||
public static function statusdescription() {
|
public static function statusdescription() {
|
||||||
$status = self::premiumStatus();
|
$status = self::premiumstatus();
|
||||||
|
|
||||||
$msg = new \stdClass;
|
$msg = new \stdClass;
|
||||||
$msg->name = $status->name;
|
$msg->name = $status->name;
|
||||||
|
|
|
@ -40,6 +40,9 @@ class reportservice extends \external_api {
|
||||||
*/
|
*/
|
||||||
const CAP_VIEW = "local/treestudyplan:viewuserreports";
|
const CAP_VIEW = "local/treestudyplan:viewuserreports";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter description for webservice function
|
||||||
|
*/
|
||||||
public static function get_report_structure_parameters(): \external_function_parameters {
|
public static function get_report_structure_parameters(): \external_function_parameters {
|
||||||
return new \external_function_parameters([
|
return new \external_function_parameters([
|
||||||
"pageid" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
"pageid" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
||||||
|
@ -83,7 +86,7 @@ class reportservice extends \external_api {
|
||||||
$firstperiod = 1;
|
$firstperiod = 1;
|
||||||
}
|
}
|
||||||
if (empty($lastperiod)) {
|
if (empty($lastperiod)) {
|
||||||
// Index for periods starts at 1, so the period count is same as length
|
// Index for periods starts at 1, so the period count is same as length.
|
||||||
$lastperiod = $page->periods();
|
$lastperiod = $page->periods();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +98,7 @@ class reportservice extends \external_api {
|
||||||
"periods" => [],
|
"periods" => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
// Find all relevant parts in this order
|
// Find all relevant parts in this order.
|
||||||
|
|
||||||
for ($i = $firstperiod; $i <= $lastperiod; $i++) {
|
for ($i = $firstperiod; $i <= $lastperiod; $i++) {
|
||||||
$period = period::find($page, $i);
|
$period = period::find($page, $i);
|
||||||
|
@ -127,13 +130,15 @@ class reportservice extends \external_api {
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter description for webservice function
|
||||||
|
*/
|
||||||
public static function get_report_data_parameters(): \external_function_parameters {
|
public static function get_report_data_parameters(): \external_function_parameters {
|
||||||
return new \external_function_parameters([
|
return new \external_function_parameters([
|
||||||
"pageid" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
"pageid" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
||||||
"userid" => new \external_value(PARAM_INT, 'id of user'),
|
"userid" => new \external_value(PARAM_INT, 'id of user'),
|
||||||
"firstperiod" => new \external_value(PARAM_INT, 'first period to include in report', VALUE_DEFAULT),
|
"firstperiod" => new \external_value(PARAM_INT, 'first period to include in report', VALUE_DEFAULT),
|
||||||
"lastperiod" => new \external_value(PARAM_INT, 'last period to include in report', VALUE_DEFAULT),
|
"lastperiod" => new \external_value(PARAM_INT, 'last period to include in report', VALUE_DEFAULT),
|
||||||
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +149,13 @@ class reportservice extends \external_api {
|
||||||
return new \external_multiple_structure(studyitem::user_structure(), "Information per studyitem");
|
return new \external_multiple_structure(studyitem::user_structure(), "Information per studyitem");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webservice function get_report data
|
||||||
|
* @param int $pageid
|
||||||
|
* @param int $userid
|
||||||
|
* @param int|null $firsteperiod
|
||||||
|
* @param int|null $lastperiod
|
||||||
|
*/
|
||||||
public static function get_report_data($pageid, $userid, $firstperiod=null, $lastperiod=null) {
|
public static function get_report_data($pageid, $userid, $firstperiod=null, $lastperiod=null) {
|
||||||
$page = studyplanpage::find_by_id($pageid);
|
$page = studyplanpage::find_by_id($pageid);
|
||||||
webservicehelper::require_capabilities(self::CAP_VIEW, $page->studyplan()->context());
|
webservicehelper::require_capabilities(self::CAP_VIEW, $page->studyplan()->context());
|
||||||
|
@ -156,14 +168,11 @@ class reportservice extends \external_api {
|
||||||
$firstperiod = 1;
|
$firstperiod = 1;
|
||||||
}
|
}
|
||||||
if (empty($lastperiod)) {
|
if (empty($lastperiod)) {
|
||||||
// Index for periods starts at 1, so the period count is same as length
|
// Index for periods starts at 1, so the period count is same as length.
|
||||||
$lastperiod = $page->periods();
|
$lastperiod = $page->periods();
|
||||||
}
|
}
|
||||||
|
|
||||||
$model = [];
|
$model = [];
|
||||||
|
|
||||||
//TODO: Build report structure
|
|
||||||
|
|
||||||
for ($i = $firstperiod; $i <= $lastperiod; $i++) {
|
for ($i = $firstperiod; $i <= $lastperiod; $i++) {
|
||||||
$lines = studyline::find_page_children($page);
|
$lines = studyline::find_page_children($page);
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
|
@ -175,11 +184,12 @@ class reportservice extends \external_api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $model;
|
return $model;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter description for webservice function
|
||||||
|
*/
|
||||||
public static function get_report_details_parameters(): \external_function_parameters {
|
public static function get_report_details_parameters(): \external_function_parameters {
|
||||||
return new \external_function_parameters([
|
return new \external_function_parameters([
|
||||||
"itemid" => new \external_value(PARAM_INT, 'id of studyitem'),
|
"itemid" => new \external_value(PARAM_INT, 'id of studyitem'),
|
||||||
|
|
|
@ -222,7 +222,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not validate the context in this instance to avoid requiring logins when this is not wanted
|
// Do not validate the context in this instance to avoid requiring logins when this is not wanted.
|
||||||
|
|
||||||
$userid = $invite->user_id;
|
$userid = $invite->user_id;
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not validate the context in this instance to avoid requiring logins when this is not wanted
|
// Do not validate the context in this instance to avoid requiring logins when this is not wanted.
|
||||||
|
|
||||||
$studyplan = studyplan::find_by_id($studyplanid);
|
$studyplan = studyplan::find_by_id($studyplanid);
|
||||||
$userid = $invite->user_id;
|
$userid = $invite->user_id;
|
||||||
|
@ -341,7 +341,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not validate the context in this instance to avoid requiring logins when this is not wanted
|
// Do not validate the context in this instance to avoid requiring logins when this is not wanted.
|
||||||
|
|
||||||
$page = studyplanpage::find_by_id($pageid);
|
$page = studyplanpage::find_by_id($pageid);
|
||||||
$studyplan = $page->studyplan();
|
$studyplan = $page->studyplan();
|
||||||
|
@ -556,7 +556,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
|
|
||||||
\external_api::validate_context(\context_system::instance());
|
\external_api::validate_context(\context_system::instance());
|
||||||
|
|
||||||
// Return empty list if coach role not enabled
|
// Return empty list if coach role not enabled.
|
||||||
if (! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
if (! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -570,7 +570,9 @@ class studentstudyplanservice extends \external_api {
|
||||||
if (has_capability(self::CAP_COACH, $studyplan->context(), $USER)) {
|
if (has_capability(self::CAP_COACH, $studyplan->context(), $USER)) {
|
||||||
$list[] = $studyplan->simple_model_coach();
|
$list[] = $studyplan->simple_model_coach();
|
||||||
}
|
}
|
||||||
} catch(\Exception $x) {}
|
} catch (\Exception $x) {
|
||||||
|
$studyplan = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
@ -694,7 +696,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
$o->enrol($userid);
|
$o->enrol($userid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger immediate cohort synchronization for this line only
|
// Trigger immediate cohort synchronization for this line only.
|
||||||
autocohortsync::syncline($o);
|
autocohortsync::syncline($o);
|
||||||
|
|
||||||
return $o->enrol_info_model($userid);
|
return $o->enrol_info_model($userid);
|
||||||
|
|
|
@ -245,7 +245,9 @@ class studyline {
|
||||||
try {
|
try {
|
||||||
$role = $DB->get_record('role', ["id" => $roleid], "*", MUST_EXIST);
|
$role = $DB->get_record('role', ["id" => $roleid], "*", MUST_EXIST);
|
||||||
$list[] = $role;
|
$list[] = $role;
|
||||||
} catch(\Exception $x) {}
|
} catch (\Exception $x) {
|
||||||
|
$role = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +306,7 @@ class studyline {
|
||||||
self::SLOTSET_FILTER => new \external_multiple_structure(
|
self::SLOTSET_FILTER => new \external_multiple_structure(
|
||||||
studyitem::editor_structure(), 'filter items'),
|
studyitem::editor_structure(), 'filter items'),
|
||||||
])
|
])
|
||||||
)
|
),
|
||||||
], "Study line editor structure", $value);
|
], "Study line editor structure", $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,7 +554,7 @@ class studyline {
|
||||||
self::SLOTSET_FILTER => new \external_multiple_structure(
|
self::SLOTSET_FILTER => new \external_multiple_structure(
|
||||||
studyitem::user_structure(), 'filter items'),
|
studyitem::user_structure(), 'filter items'),
|
||||||
])
|
])
|
||||||
)
|
),
|
||||||
], 'Studyline with user info', $value);
|
], 'Studyline with user info', $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,7 +608,8 @@ class studyline {
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the users enrolled in this studyline if relevant
|
/**
|
||||||
|
* Get the users enrolled in this studyline if relevant
|
||||||
* @return array of Userids linked
|
* @return array of Userids linked
|
||||||
*/
|
*/
|
||||||
public function get_enrolled_userids() {
|
public function get_enrolled_userids() {
|
||||||
|
@ -632,7 +635,7 @@ class studyline {
|
||||||
public function isenrolled($userid) {
|
public function isenrolled($userid) {
|
||||||
global $DB;
|
global $DB;
|
||||||
if ($this->r->enrollable == self::ENROLLABLE_NONE) {
|
if ($this->r->enrollable == self::ENROLLABLE_NONE) {
|
||||||
return true; // If student cannot enrol, the student always is enrolled
|
return true; // If student cannot enrol, the student always is enrolled.
|
||||||
} else {
|
} else {
|
||||||
$r = $DB->get_record('local_treestudyplan_lineuser', [
|
$r = $DB->get_record('local_treestudyplan_lineuser', [
|
||||||
'line_id' => $this->id(),
|
'line_id' => $this->id(),
|
||||||
|
@ -721,7 +724,7 @@ class studyline {
|
||||||
// Registration already exists, check if enrolled is true and update accordingly.
|
// Registration already exists, check if enrolled is true and update accordingly.
|
||||||
if (boolval($r->enrolled)) {
|
if (boolval($r->enrolled)) {
|
||||||
$r->enrolled = 0;
|
$r->enrolled = 0;
|
||||||
$r->timeenrolled = time(); // Regi
|
$r->timeenrolled = time(); // Regi.
|
||||||
$r->enrolledby = $USER->id;
|
$r->enrolledby = $USER->id;
|
||||||
$DB->update_record("local_treestudyplan_lineuser", $r);
|
$DB->update_record("local_treestudyplan_lineuser", $r);
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,10 @@ class studyplan {
|
||||||
return $date;
|
return $date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine studyplan icon
|
||||||
|
* @return string Url of icon
|
||||||
|
*/
|
||||||
private function icon() {
|
private function icon() {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
$fs = \get_file_storage();
|
$fs = \get_file_storage();
|
||||||
|
@ -183,8 +187,14 @@ class studyplan {
|
||||||
// Fall back to the standard (ugly) default image.
|
// Fall back to the standard (ugly) default image.
|
||||||
$url = new \moodle_url($CFG->wwwroot . "/local/treestudyplan/pix/default_icon.png");
|
$url = new \moodle_url($CFG->wwwroot . "/local/treestudyplan/pix/default_icon.png");
|
||||||
} else {
|
} else {
|
||||||
$url = \moodle_url::make_pluginfile_url(\context_system::instance()->id, 'local_treestudyplan', 'defaulticon', 0, "/",
|
$url = \moodle_url::make_pluginfile_url(
|
||||||
$defaulticon);
|
\context_system::instance()->id,
|
||||||
|
'local_treestudyplan',
|
||||||
|
'defaulticon',
|
||||||
|
0,
|
||||||
|
"/",
|
||||||
|
$defaulticon
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -201,9 +211,9 @@ class studyplan {
|
||||||
// The pluginfile URL which will serve the request.
|
// The pluginfile URL which will serve the request.
|
||||||
'pluginfile.php',
|
'pluginfile.php',
|
||||||
|
|
||||||
// The combination of contextid / component / filearea / itemid
|
// The combination of contextid / component / filearea / itemid.
|
||||||
// form the virtual bucket that file are stored in.
|
// Form the virtual bucket that file are stored in.
|
||||||
\context_system::instance()->id, // System instance is always used for this
|
\context_system::instance()->id, // System instance is always used for this.
|
||||||
'local_treestudyplan',
|
'local_treestudyplan',
|
||||||
'studyplan',
|
'studyplan',
|
||||||
$this->id
|
$this->id
|
||||||
|
@ -266,7 +276,11 @@ class studyplan {
|
||||||
"aggregation_info" => aggregator::basic_structure(),
|
"aggregation_info" => aggregator::basic_structure(),
|
||||||
"pages" => new \external_multiple_structure(studyplanpage::simple_structure(), 'pages'),
|
"pages" => new \external_multiple_structure(studyplanpage::simple_structure(), 'pages'),
|
||||||
"progress" => new \external_value(PARAM_FLOAT, "fraction of completed modules", VALUE_OPTIONAL),
|
"progress" => new \external_value(PARAM_FLOAT, "fraction of completed modules", VALUE_OPTIONAL),
|
||||||
"amteaching" => new \external_value(PARAM_BOOL, "Current user is teaching one or more courses in this studyplan", VALUE_OPTIONAL),
|
"amteaching" => new \external_value(
|
||||||
|
PARAM_BOOL,
|
||||||
|
"Current user is teaching one or more courses in this studyplan",
|
||||||
|
VALUE_OPTIONAL
|
||||||
|
),
|
||||||
"suspended" => new \external_value(PARAM_BOOL, 'if studyplan is suspended', VALUE_OPTIONAL),
|
"suspended" => new \external_value(PARAM_BOOL, 'if studyplan is suspended', VALUE_OPTIONAL),
|
||||||
], 'Basic studyplan info', $value);
|
], 'Basic studyplan info', $value);
|
||||||
}
|
}
|
||||||
|
@ -408,7 +422,8 @@ class studyplan {
|
||||||
public static function add($fields, $bare = false): self {
|
public static function add($fields, $bare = false): self {
|
||||||
global $CFG, $DB;
|
global $CFG, $DB;
|
||||||
|
|
||||||
$addable = ['name', 'shortname', 'description', 'descriptionformat', 'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
|
$addable = ['name', 'shortname', 'description', 'descriptionformat',
|
||||||
|
'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
|
||||||
$info = ['enddate' => null, "template" => 0, "suspended" => 0];
|
$info = ['enddate' => null, "template" => 0, "suspended" => 0];
|
||||||
foreach ($addable as $f) {
|
foreach ($addable as $f) {
|
||||||
if (array_key_exists($f, $fields)) {
|
if (array_key_exists($f, $fields)) {
|
||||||
|
@ -418,8 +433,8 @@ class studyplan {
|
||||||
$id = $DB->insert_record(self::TABLE, $info);
|
$id = $DB->insert_record(self::TABLE, $info);
|
||||||
$plan = self::find_by_id($id); // Make sure the new studyplan is immediately cached.
|
$plan = self::find_by_id($id); // Make sure the new studyplan is immediately cached.
|
||||||
|
|
||||||
// Add a single page and initialize it with placeholder data
|
// Add a single page and initialize it with placeholder data.
|
||||||
// This makes it easier to create a new study plan
|
// This makes it easier to create a new study plan.
|
||||||
// On import, adding an empty page messes things up , so we have an option to skip this....
|
// On import, adding an empty page messes things up , so we have an option to skip this....
|
||||||
if (!$bare) {
|
if (!$bare) {
|
||||||
|
|
||||||
|
@ -459,7 +474,7 @@ class studyplan {
|
||||||
'aggregation',
|
'aggregation',
|
||||||
'aggregation_config',
|
'aggregation_config',
|
||||||
'suspended',
|
'suspended',
|
||||||
'template'
|
'template',
|
||||||
];
|
];
|
||||||
$info = ['id' => $this->id ];
|
$info = ['id' => $this->id ];
|
||||||
foreach ($editable as $f) {
|
foreach ($editable as $f) {
|
||||||
|
@ -501,7 +516,7 @@ class studyplan {
|
||||||
$DB->delete_records("local_treestudyplan_coach", ["studyplan_id" => $this->id]);
|
$DB->delete_records("local_treestudyplan_coach", ["studyplan_id" => $this->id]);
|
||||||
$DB->delete_records("local_treestudyplan_cohort", ["studyplan_id" => $this->id]);
|
$DB->delete_records("local_treestudyplan_cohort", ["studyplan_id" => $this->id]);
|
||||||
$DB->delete_records("local_treestudyplan_user", ["studyplan_id" => $this->id]);
|
$DB->delete_records("local_treestudyplan_user", ["studyplan_id" => $this->id]);
|
||||||
//
|
|
||||||
$DB->delete_records('local_treestudyplan', ['id' => $this->id]);
|
$DB->delete_records('local_treestudyplan', ['id' => $this->id]);
|
||||||
return success::success();
|
return success::success();
|
||||||
}
|
}
|
||||||
|
@ -768,7 +783,8 @@ class studyplan {
|
||||||
return $this->linkeduserids;
|
return $this->linkeduserids;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if this studyplan is linked to a particular user
|
/**
|
||||||
|
* 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) {
|
public function has_linked_user($user) {
|
||||||
|
@ -785,12 +801,13 @@ class studyplan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if this studyplan is linked to a particular user
|
/**
|
||||||
|
* Check if this studyplan is linked to a particular user
|
||||||
* @param bool|stdClass|null $user The userid or user record of the user Leave empty to check current user.
|
* @param bool|stdClass|null $user The userid or user record of the user Leave empty to check current user.
|
||||||
*/
|
*/
|
||||||
public function is_coach($user=null) {
|
public function is_coach($user=null) {
|
||||||
global $DB, $USER
|
global $DB, $USER;
|
||||||
;
|
|
||||||
if (! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
if (! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
||||||
// If coach role is not available, return false immediately.
|
// If coach role is not available, return false immediately.
|
||||||
return false;
|
return false;
|
||||||
|
@ -845,7 +862,7 @@ class studyplan {
|
||||||
$prg = $p->scanuserprogress($userid);
|
$prg = $p->scanuserprogress($userid);
|
||||||
$progress += $prg;
|
$progress += $prg;
|
||||||
}
|
}
|
||||||
// Now average it out over the amount of pages
|
// Now average it out over the amount of pages.
|
||||||
if (count($pages) > 0) {
|
if (count($pages) > 0) {
|
||||||
return $progress / count($pages);
|
return $progress / count($pages);
|
||||||
} else {
|
} else {
|
||||||
|
@ -912,7 +929,7 @@ class studyplan {
|
||||||
'description' => $this->r->description,
|
'description' => $this->r->description,
|
||||||
'descriptionformat' => $this->r->descriptionformat,
|
'descriptionformat' => $this->r->descriptionformat,
|
||||||
'aggregation' => $this->r->aggregation,
|
'aggregation' => $this->r->aggregation,
|
||||||
'aggregation_config' => $this->r->aggregation_config
|
'aggregation_config' => $this->r->aggregation_config,
|
||||||
], true);
|
], true);
|
||||||
|
|
||||||
// Copy any files related to this studyplan.
|
// Copy any files related to this studyplan.
|
||||||
|
@ -981,7 +998,7 @@ class studyplan {
|
||||||
$json = json_encode([
|
$json = json_encode([
|
||||||
"type" => "studyplan",
|
"type" => "studyplan",
|
||||||
"version" => 2.0,
|
"version" => 2.0,
|
||||||
"studyplan" => $model
|
"studyplan" => $model,
|
||||||
], \JSON_PRETTY_PRINT);
|
], \JSON_PRETTY_PRINT);
|
||||||
return [ "format" => "application/json", "content" => $json];
|
return [ "format" => "application/json", "content" => $json];
|
||||||
}
|
}
|
||||||
|
@ -1094,11 +1111,11 @@ class studyplan {
|
||||||
// Create a new plan, based on the given parameters - this is the import studyplan part.
|
// Create a new plan, based on the given parameters - this is the import studyplan part.
|
||||||
$plan = self::add( $planmodel, true);
|
$plan = self::add( $planmodel, true);
|
||||||
|
|
||||||
// Import the files
|
// Import the files.
|
||||||
if (isset( $planmodel['files']) && is_array( $planmodel['files'])) {
|
if (isset( $planmodel['files']) && is_array( $planmodel['files'])) {
|
||||||
$plan->import_files( $planmodel['files'], "studyplan");
|
$plan->import_files( $planmodel['files'], "studyplan");
|
||||||
}
|
}
|
||||||
// Import the icon
|
// Import the icon.
|
||||||
if (isset( $planmodel['iconfiles']) && is_array( $planmodel['iconfiles'])) {
|
if (isset( $planmodel['iconfiles']) && is_array( $planmodel['iconfiles'])) {
|
||||||
$plan->import_files( $planmodel['iconfiles'], "icon");
|
$plan->import_files( $planmodel['iconfiles'], "icon");
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,9 +154,9 @@ class studyplanpage {
|
||||||
// The pluginfile URL which will serve the request.
|
// The pluginfile URL which will serve the request.
|
||||||
'pluginfile.php',
|
'pluginfile.php',
|
||||||
|
|
||||||
// The combination of contextid / component / filearea / itemid
|
// The combination of contextid / component / filearea / itemid.
|
||||||
// form the virtual bucket that file are stored in.
|
// Form the virtual bucket that file are stored in.
|
||||||
\context_system::instance()->id, // System instance is always used for this
|
\context_system::instance()->id, // System instance is always used for this.
|
||||||
'local_treestudyplan',
|
'local_treestudyplan',
|
||||||
'studyplanpage',
|
'studyplanpage',
|
||||||
$this->id
|
$this->id
|
||||||
|
@ -164,6 +164,10 @@ class studyplanpage {
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine studyplan timing
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function timing() {
|
public function timing() {
|
||||||
$now = new \DateTime();
|
$now = new \DateTime();
|
||||||
if ($now > $this->startdate()) {
|
if ($now > $this->startdate()) {
|
||||||
|
@ -292,7 +296,7 @@ class studyplanpage {
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $DB->insert_record(self::TABLE, $info);
|
$id = $DB->insert_record(self::TABLE, $info);
|
||||||
// Refresh the page cache for the studyplan
|
// Refresh the page cache for the studyplan.
|
||||||
$plan->pages(true);
|
$plan->pages(true);
|
||||||
$page = self::find_by_id($id); // Make sure the new page is immediately cached.
|
$page = self::find_by_id($id); // Make sure the new page is immediately cached.
|
||||||
|
|
||||||
|
@ -301,7 +305,7 @@ class studyplanpage {
|
||||||
if (get_config("local_treestudyplan", "copystudylinesnewpage")) {
|
if (get_config("local_treestudyplan", "copystudylinesnewpage")) {
|
||||||
$templatepage = null;
|
$templatepage = null;
|
||||||
$templatelines = [];
|
$templatelines = [];
|
||||||
// find the latest a page with lines in the plan
|
// Find the latest a page with lines in the plan.
|
||||||
$pages = $plan->pages(true);
|
$pages = $plan->pages(true);
|
||||||
foreach ($pages as $p) {
|
foreach ($pages as $p) {
|
||||||
if ($p->id() != $id) {
|
if ($p->id() != $id) {
|
||||||
|
@ -322,7 +326,7 @@ class studyplanpage {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count(studyline::find_page_children($page)) == 0) {
|
if (count(studyline::find_page_children($page)) == 0) {
|
||||||
// Add an empty study line if there are currently no study lines
|
// Add an empty study line if there are currently no study lines.
|
||||||
$lineinfo = ['page_id' => $id,
|
$lineinfo = ['page_id' => $id,
|
||||||
'name' => get_string("default_line_name", "local_treestudyplan"),
|
'name' => get_string("default_line_name", "local_treestudyplan"),
|
||||||
'shortname' => get_string("default_line_shortname", "local_treestudyplan"),
|
'shortname' => get_string("default_line_shortname", "local_treestudyplan"),
|
||||||
|
@ -585,7 +589,7 @@ class studyplanpage {
|
||||||
$json = json_encode([
|
$json = json_encode([
|
||||||
"type" => "studyplanpage",
|
"type" => "studyplanpage",
|
||||||
"version" => 2.0,
|
"version" => 2.0,
|
||||||
"page" => $model
|
"page" => $model,
|
||||||
], \JSON_PRETTY_PRINT);
|
], \JSON_PRETTY_PRINT);
|
||||||
return [ "format" => "application/json", "content" => $json];
|
return [ "format" => "application/json", "content" => $json];
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ class studyplanservice extends \external_api {
|
||||||
webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $studyplan->context());
|
webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $studyplan->context());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If suspended, only allow the mapping if the user has edit rights
|
// If suspended, only allow the mapping if the user has edit rights.
|
||||||
if (!has_capability(self::CAP_EDIT, $studyplan->context()) && $studyplan->suspended()) {
|
if (!has_capability(self::CAP_EDIT, $studyplan->context()) && $studyplan->suspended()) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -902,7 +902,6 @@ class studyplanservice extends \external_api {
|
||||||
*/
|
*/
|
||||||
public static function list_badges_parameters(): \external_function_parameters {
|
public static function list_badges_parameters(): \external_function_parameters {
|
||||||
return new \external_function_parameters( [
|
return new \external_function_parameters( [
|
||||||
|
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,12 +921,9 @@ class studyplanservice extends \external_api {
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
$badges = badges_get_badges(BADGE_TYPE_SITE, "timemodified");
|
$badges = badges_get_badges(BADGE_TYPE_SITE, "timemodified");
|
||||||
|
|
||||||
foreach ($badges as $badge) {
|
foreach ($badges as $badge) {
|
||||||
// TODO: Add config option to list only active badges.
|
|
||||||
$result[] = (new badgeinfo($badge))->editor_model();
|
$result[] = (new badgeinfo($badge))->editor_model();
|
||||||
|
|
||||||
// TODO: Include course badges somehow... Just site badges is not enough.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
|
|
||||||
|
@ -962,7 +958,7 @@ class studyplanservice extends \external_api {
|
||||||
*/
|
*/
|
||||||
public static function search_badges($search, $active=false) {
|
public static function search_badges($search, $active=false) {
|
||||||
$systemcontext = webservicehelper::system_context();
|
$systemcontext = webservicehelper::system_context();
|
||||||
// Check system permission to
|
// Check system permission to.
|
||||||
webservicehelper::require_capabilities("moodle/badges:viewbadges", $systemcontext);
|
webservicehelper::require_capabilities("moodle/badges:viewbadges", $systemcontext);
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
|
@ -1115,7 +1111,8 @@ class studyplanservice extends \external_api {
|
||||||
$studyplan = $item->studyline()->studyplan();
|
$studyplan = $item->studyline()->studyplan();
|
||||||
if (($studyplan->is_coach() && !$studyplan->suspended())
|
if (($studyplan->is_coach() && !$studyplan->suspended())
|
||||||
|| has_capability('local/treestudyplan:editstudyplan', $studyplan->context())
|
|| has_capability('local/treestudyplan:editstudyplan', $studyplan->context())
|
||||||
|| ($coursecontext && is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables'))) {
|
|| ($coursecontext && is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables'))
|
||||||
|
) {
|
||||||
return coursecompetencyinfo::require_competency($competencyid, $itemid, $required)->model();
|
return coursecompetencyinfo::require_competency($competencyid, $itemid, $required)->model();
|
||||||
} else {
|
} else {
|
||||||
return success::fail("Access denied")->model();
|
return success::fail("Access denied")->model();
|
||||||
|
@ -2081,7 +2078,7 @@ class studyplanservice extends \external_api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger immediate cohort synchronization for this line only
|
// Trigger immediate cohort synchronization for this line only.
|
||||||
autocohortsync::syncline($o);
|
autocohortsync::syncline($o);
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
@ -2132,7 +2129,7 @@ class studyplanservice extends \external_api {
|
||||||
$list[] = self::student_enrol_status_model($userid, $o);
|
$list[] = self::student_enrol_status_model($userid, $o);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger immediate cohort synchronization for this line only
|
// Trigger immediate cohort synchronization for this line only.
|
||||||
autocohortsync::syncline($o);
|
autocohortsync::syncline($o);
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
@ -2195,7 +2192,6 @@ class studyplanservice extends \external_api {
|
||||||
*/
|
*/
|
||||||
public static function count_templates_parameters(): \external_function_parameters {
|
public static function count_templates_parameters(): \external_function_parameters {
|
||||||
return new \external_function_parameters( [
|
return new \external_function_parameters( [
|
||||||
|
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ class autocohortsync extends \core\task\scheduled_task {
|
||||||
$userenroller = new cascadeusersync($plan);
|
$userenroller = new cascadeusersync($plan);
|
||||||
$userenroller->sync($line);
|
$userenroller->sync($line);
|
||||||
}
|
}
|
||||||
// Leave the csync required flag for the next auto update
|
// Leave the csync required flag for the next auto update.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,6 @@ class teachingfinder {
|
||||||
$now = time();
|
$now = time();
|
||||||
foreach ($list as $pageid) {
|
foreach ($list as $pageid) {
|
||||||
// Retrieve the studyplan id from the page.
|
// 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" => $pageid]);
|
$planid = $DB->get_field("local_treestudyplan_page", "studyplan_id", ["id" => $pageid]);
|
||||||
$DB->insert_record(self::TABLE, ["teacher_id" => $userid, "studyplan_id" => $planid, "update_time" => $now]);
|
$DB->insert_record(self::TABLE, ["teacher_id" => $userid, "studyplan_id" => $planid, "update_time" => $now]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,12 @@ class utilityservice extends \external_api {
|
||||||
*/
|
*/
|
||||||
const CAP_VIEW = "local/treestudyplan:viewuserreports";
|
const CAP_VIEW = "local/treestudyplan:viewuserreports";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load moodle form
|
||||||
|
* @param string $formname
|
||||||
|
* @param string $params
|
||||||
|
* @param string $ajaxformdata
|
||||||
|
*/
|
||||||
protected static function load_mform($formname, $params, $ajaxformdata = null) {
|
protected static function load_mform($formname, $params, $ajaxformdata = null) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
/* We don't need to load the form php file (class autoloading will handle that)
|
/* We don't need to load the form php file (class autoloading will handle that)
|
||||||
|
@ -121,7 +127,7 @@ class utilityservice extends \external_api {
|
||||||
return new \external_single_structure(
|
return new \external_single_structure(
|
||||||
[
|
[
|
||||||
'html' => new \external_value(PARAM_RAW, 'HTML fragment.'),
|
'html' => new \external_value(PARAM_RAW, 'HTML fragment.'),
|
||||||
'javascript' => new \external_value(PARAM_RAW, 'JavaScript fragment')
|
'javascript' => new \external_value(PARAM_RAW, 'JavaScript fragment'),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +163,7 @@ class utilityservice extends \external_api {
|
||||||
$ajaxformdata = [];
|
$ajaxformdata = [];
|
||||||
parse_str($formdata, $ajaxformdata);
|
parse_str($formdata, $ajaxformdata);
|
||||||
|
|
||||||
// Load the form, provide submitted form data and perform security checks
|
// Load the form, provide submitted form data and perform security checks.
|
||||||
$mform = self::load_mform($formname, $params, $ajaxformdata);
|
$mform = self::load_mform($formname, $params, $ajaxformdata);
|
||||||
|
|
||||||
$return = $mform->process_submission();
|
$return = $mform->process_submission();
|
||||||
|
@ -193,7 +199,7 @@ class utilityservice extends \external_api {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"hivizdropslots" => get_config("local_treestudyplan", "hivizdropslots"),
|
"hivizdropslots" => get_config("local_treestudyplan", "hivizdropslots"),
|
||||||
"toolboxleft" => get_config("local_treestudyplan", "toolboxleft")
|
"toolboxleft" => get_config("local_treestudyplan", "toolboxleft"),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ Options:
|
||||||
|
|
||||||
list($options, $unrecognised) = cli_get_params([
|
list($options, $unrecognised) = cli_get_params([
|
||||||
'help' => false,
|
'help' => false,
|
||||||
'amount' => null
|
'amount' => null,
|
||||||
], [
|
], [
|
||||||
'h' => 'help',
|
'h' => 'help',
|
||||||
'a' => 'amount',
|
'a' => 'amount',
|
||||||
|
@ -56,7 +56,7 @@ if ($options['help']) {
|
||||||
cli_writeln($usage);
|
cli_writeln($usage);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
print_r($options);
|
|
||||||
if (empty($options['amount']) || !is_numeric($options['amount'])) {
|
if (empty($options['amount']) || !is_numeric($options['amount'])) {
|
||||||
cli_error('Missing mandatory integer argument "amount"', 2);
|
cli_error('Missing mandatory integer argument "amount"', 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ list($options, $unrecognised) = cli_get_params([
|
||||||
'help' => false,
|
'help' => false,
|
||||||
'studyplan' => null,
|
'studyplan' => null,
|
||||||
'all' => false,
|
'all' => false,
|
||||||
'file' => "/tmp/generategrades.json"
|
'file' => "/tmp/generategrades.json",
|
||||||
], [
|
], [
|
||||||
'h' => 'help',
|
'h' => 'help',
|
||||||
's' => 'studyplan',
|
's' => 'studyplan',
|
||||||
|
|
|
@ -47,13 +47,13 @@ list($options, $unrecognised) = cli_get_params([
|
||||||
'dryrun' => false,
|
'dryrun' => false,
|
||||||
'studyplan' => null,
|
'studyplan' => null,
|
||||||
'all' => false,
|
'all' => false,
|
||||||
'file' => "/tmp/generategrades.json"
|
'file' => "/tmp/generategrades.json",
|
||||||
], [
|
], [
|
||||||
'h' => 'help',
|
'h' => 'help',
|
||||||
's' => 'studyplan',
|
's' => 'studyplan',
|
||||||
'a' => 'all',
|
'a' => 'all',
|
||||||
'f' => 'file',
|
'f' => 'file',
|
||||||
'd' => 'dryrun'
|
'd' => 'dryrun',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($unrecognised) {
|
if ($unrecognised) {
|
||||||
|
@ -150,7 +150,10 @@ foreach ($plans as $plan) {
|
||||||
$ug = $a->get_user_grade($u->id, true);
|
$ug = $a->get_user_grade($u->id, true);
|
||||||
$ug->grade = grade_floatval($gg->grade);
|
$ug->grade = grade_floatval($gg->grade);
|
||||||
$ug->grader = $USER->id;
|
$ug->grader = $USER->id;
|
||||||
$ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
|
$ug->feedbacktext = nl2br(htmlspecialchars(
|
||||||
|
$gg->fb,
|
||||||
|
ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401
|
||||||
|
));
|
||||||
$ug->feedbackformat = FORMAT_HTML;
|
$ug->feedbackformat = FORMAT_HTML;
|
||||||
|
|
||||||
if (!$options["dryrun"]) {
|
if (!$options["dryrun"]) {
|
||||||
|
|
22
coach.php
22
coach.php
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||||
/**
|
/**
|
||||||
* View study plans - teacher view and student view
|
* Entry point for Coach
|
||||||
* @package local_treestudyplan
|
* @package local_treestudyplan
|
||||||
* @copyright 2023 P.M. Kuipers
|
* @copyright 2023 P.M. Kuipers
|
||||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
@ -50,7 +50,6 @@ $PAGE->requires->js_call_amd('local_treestudyplan/page-coach', 'init', []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut function to provide translations
|
* Shortcut function to provide translations
|
||||||
*
|
|
||||||
* @param mixed $str Translation key
|
* @param mixed $str Translation key
|
||||||
* @param null|string[] $param Parameters to pass to translation
|
* @param null|string[] $param Parameters to pass to translation
|
||||||
* @param string $plugin Location to search for translation strings
|
* @param string $plugin Location to search for translation strings
|
||||||
|
@ -61,7 +60,14 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
||||||
}
|
}
|
||||||
|
|
||||||
print $OUTPUT->header();
|
print $OUTPUT->header();
|
||||||
?>
|
$text = (object)[
|
||||||
|
'studyplan_select' => t("studyplan_select"),
|
||||||
|
'coacheditmode' => t("coacheditmode"),
|
||||||
|
'showoverview' => t("showoverview"),
|
||||||
|
'studyplan_noneselected' => t("studyplan_noneselected"),
|
||||||
|
];
|
||||||
|
|
||||||
|
print <<<END
|
||||||
<div id='root' class="t-studyplan-limit-width">
|
<div id='root' class="t-studyplan-limit-width">
|
||||||
<div class='vue-loader' v-show='false'>
|
<div class='vue-loader' v-show='false'>
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
@ -73,7 +79,7 @@ print $OUTPUT->header();
|
||||||
<template v-if="displayedstudyplan">
|
<template v-if="displayedstudyplan">
|
||||||
<a href='#' @click.prevent='closeStudyplan'
|
<a href='#' @click.prevent='closeStudyplan'
|
||||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
||||||
<span><?php t("studyplan_select"); ?></span>
|
<span>{$text->studyplan_select}</span>
|
||||||
<s-studyplan-details
|
<s-studyplan-details
|
||||||
v-model="displayedstudyplan"
|
v-model="displayedstudyplan"
|
||||||
v-if="displayedstudyplan.description"
|
v-if="displayedstudyplan.description"
|
||||||
|
@ -93,14 +99,14 @@ print $OUTPUT->header();
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
|
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
|
||||||
<template #defaultlabel><span class='text-primary'><?php t("coacheditmode"); ?></span></template>
|
<template #defaultlabel><span class='text-primary'>{$text->coacheditmode}</span></template>
|
||||||
</s-prevnext-selector>
|
</s-prevnext-selector>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class='t-studyplan-container'>
|
<div class='t-studyplan-container'>
|
||||||
<h2 v-if='displayedstudyplan&& selectedstudent'
|
<h2 v-if='displayedstudyplan&& selectedstudent'
|
||||||
>{{selectedstudent.firstname}} {{selectedstudent.lastname}} - {{displayedstudyplan.name}}</h2>
|
>{{selectedstudent.firstname}} {{selectedstudent.lastname}} - {{displayedstudyplan.name}}</h2>
|
||||||
<h2 v-else-if='displayedstudyplan'><?php t("showoverview"); ?> - {{displayedstudyplan.name}}</h2>
|
<h2 v-else-if='displayedstudyplan'>{$text->showoverview} - {{displayedstudyplan.name}}</h2>
|
||||||
<template v-if="!loadingstudyplan && displayedstudyplan">
|
<template v-if="!loadingstudyplan && displayedstudyplan">
|
||||||
<r-studyplan v-if="selectedstudent"
|
<r-studyplan v-if="selectedstudent"
|
||||||
v-model='displayedstudyplan'
|
v-model='displayedstudyplan'
|
||||||
|
@ -116,7 +122,7 @@ print $OUTPUT->header();
|
||||||
<span class="sr-only">Loading...</span>
|
<span class="sr-only">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class='t-studyplan-notselected'>
|
<div v-else class='t-studyplan-notselected'>
|
||||||
<p><?php t("studyplan_noneselected"); ?></p>
|
<p>{$text->studyplan_noneselected}</p>
|
||||||
<b-card-group deck>
|
<b-card-group deck>
|
||||||
<s-studyplan-card
|
<s-studyplan-card
|
||||||
v-for='(studyplan, planindex) in studyplans'
|
v-for='(studyplan, planindex) in studyplans'
|
||||||
|
@ -130,6 +136,6 @@ print $OUTPUT->header();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
END;
|
||||||
|
|
||||||
print $OUTPUT->footer();
|
print $OUTPUT->footer();
|
||||||
|
|
|
@ -27,63 +27,62 @@ $capabilities = [
|
||||||
'riskbitmask' => RISK_DATALOSS ,
|
'riskbitmask' => RISK_DATALOSS ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'manager' => CAP_ALLOW
|
'manager' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local/treestudyplan:forcescales' => [
|
'local/treestudyplan:forcescales' => [
|
||||||
'riskbitmask' => RISK_DATALOSS ,
|
'riskbitmask' => RISK_DATALOSS ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'manager' => CAP_ALLOW
|
'manager' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local/treestudyplan:selectowngradables' => [
|
'local/treestudyplan:selectowngradables' => [
|
||||||
'riskbitmask' => RISK_DATALOSS ,
|
'riskbitmask' => RISK_DATALOSS ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'teacher' => CAP_ALLOW
|
'teacher' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local/treestudyplan:configure' => [
|
'local/treestudyplan:configure' => [
|
||||||
'riskbitmask' => RISK_DATALOSS ,
|
'riskbitmask' => RISK_DATALOSS ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'manager' => CAP_ALLOW
|
'manager' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local/treestudyplan:viewuserreports' => [
|
'local/treestudyplan:viewuserreports' => [
|
||||||
'riskbitmask' => RISK_PERSONAL ,
|
'riskbitmask' => RISK_PERSONAL ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'manager' => CAP_ALLOW
|
'manager' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local/treestudyplan:lineunenrol' => [
|
'local/treestudyplan:lineunenrol' => [
|
||||||
'riskbitmask' => RISK_PERSONAL ,
|
'riskbitmask' => RISK_PERSONAL ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'manager' => CAP_ALLOW
|
'manager' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'local/treestudyplan:coach' => [
|
'local/treestudyplan:coach' => [
|
||||||
'riskbitmask' => RISK_PERSONAL ,
|
'riskbitmask' => RISK_PERSONAL ,
|
||||||
'captype' => 'write',
|
'captype' => 'write',
|
||||||
'contextlevel' => CONTEXT_SYSTEM,
|
'contextlevel' => CONTEXT_SYSTEM,
|
||||||
'archetypes' => array(
|
'archetypes' => [
|
||||||
'manager' => CAP_ALLOW
|
'manager' => CAP_ALLOW,
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$services = [
|
$services = [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$functions = [
|
$functions = [
|
||||||
|
@ -832,5 +831,4 @@ $functions = [
|
||||||
'capabilities' => '',
|
'capabilities' => '',
|
||||||
'loginrequired' => true,
|
'loginrequired' => true,
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -49,5 +49,4 @@ $tasks = [
|
||||||
'month' => '*',
|
'month' => '*',
|
||||||
'dayofweek' => '*',
|
'dayofweek' => '*',
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -394,7 +394,7 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
||||||
"fullname" => "Period {$pnum}",
|
"fullname" => "Period {$pnum}",
|
||||||
"shortname" => "P{$pnum}",
|
"shortname" => "P{$pnum}",
|
||||||
"startdate" => date("Y-m-d", $pstart),
|
"startdate" => date("Y-m-d", $pstart),
|
||||||
"enddate" => date("Y-m-d", $pend)
|
"enddate" => date("Y-m-d", $pend),
|
||||||
];
|
];
|
||||||
$DB->insert_record("local_treestudyplan_period", $o);
|
$DB->insert_record("local_treestudyplan_period", $o);
|
||||||
}
|
}
|
||||||
|
@ -532,7 +532,6 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
||||||
$dbman->add_field($table, $field);
|
$dbman->add_field($table, $field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Treestudyplan savepoint reached.
|
// Treestudyplan savepoint reached.
|
||||||
upgrade_plugin_savepoint(true, 2024022502, 'local', 'treestudyplan');
|
upgrade_plugin_savepoint(true, 2024022502, 'local', 'treestudyplan');
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,9 @@ require_login();
|
||||||
print $OUTPUT->header();
|
print $OUTPUT->header();
|
||||||
|
|
||||||
if (!empty($add)) {
|
if (!empty($add)) {
|
||||||
$data = array(
|
$data = [
|
||||||
'add' => 1,
|
'add' => 1,
|
||||||
);
|
];
|
||||||
|
|
||||||
} else if (!empty($update)) {
|
} else if (!empty($update)) {
|
||||||
$data = $DB->get_record("local_treestudyplan_invit", ['id' => $update]);
|
$data = $DB->get_record("local_treestudyplan_invit", ['id' => $update]);
|
||||||
|
|
|
@ -64,7 +64,6 @@ $ci = new contextinfo($studyplancontext);
|
||||||
$contextname = $ci->pathstr();
|
$contextname = $ci->pathstr();
|
||||||
|
|
||||||
$PAGE->set_pagelayout('base');
|
$PAGE->set_pagelayout('base');
|
||||||
//$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);
|
$PAGE->set_heading($contextname);
|
||||||
|
|
||||||
|
@ -72,20 +71,30 @@ if ($studyplancontext->id > 1) {
|
||||||
navigation_node::override_active_url(new moodle_url('/course/index.php', ['categoryid' => $categoryid ]));
|
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'));
|
||||||
|
|
||||||
// Coursecat context
|
// Coursecat context.
|
||||||
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later
|
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later.
|
||||||
} else {
|
} else {
|
||||||
// System context
|
// System context.
|
||||||
$cat = \core_course_category::top();
|
$cat = \core_course_category::top();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$cat->is_uservisible()) {
|
if (!$cat->is_uservisible()) {
|
||||||
|
|
||||||
throw new \moodle_exception("error:cannotviewcategory", "local_treestudyplan", "/local/treestudyplan/edit_plan.php", $contextname);
|
throw new \moodle_exception(
|
||||||
|
"error:cannotviewcategory",
|
||||||
|
"local_treestudyplan",
|
||||||
|
"/local/treestudyplan/edit_plan.php",
|
||||||
|
$contextname
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_capability('local/treestudyplan:editstudyplan', $studyplancontext)) {
|
if (!has_capability('local/treestudyplan:editstudyplan', $studyplancontext)) {
|
||||||
throw new \moodle_exception("error:nostudyplaneditaccess", "local_treestudyplan", "/local/treestudyplan/edit_plan.php", $contextname);
|
throw new \moodle_exception(
|
||||||
|
"error:nostudyplaneditaccess",
|
||||||
|
"local_treestudyplan",
|
||||||
|
"/local/treestudyplan/edit_plan.php",
|
||||||
|
$contextname
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load javascripts and specific css.
|
// Load javascripts and specific css.
|
||||||
|
@ -110,8 +119,13 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
||||||
}
|
}
|
||||||
|
|
||||||
print $OUTPUT->header();
|
print $OUTPUT->header();
|
||||||
|
$text = (object)[
|
||||||
|
'loading' => t("loading", null, "core"),
|
||||||
|
'studyplan_select' => t("studyplan_select"),
|
||||||
|
'studyplan_noneselected' => t("studyplan_noneselected"),
|
||||||
|
];
|
||||||
|
|
||||||
?>
|
print <<<END
|
||||||
<div id='root' class="t-studyplan-limit-width">
|
<div id='root' class="t-studyplan-limit-width">
|
||||||
<div class='vue-loader' v-show='false'>
|
<div class='vue-loader' v-show='false'>
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
@ -120,26 +134,27 @@ print $OUTPUT->header();
|
||||||
</div>
|
</div>
|
||||||
<div v-cloak>
|
<div v-cloak>
|
||||||
<div v-if='!activestudyplan && usedcontexts && !loadingstudyplan' class='ml-3 mb-3 s-context-selector'>
|
<div v-if='!activestudyplan && usedcontexts && !loadingstudyplan' class='ml-3 mb-3 s-context-selector'>
|
||||||
<b-form-select text='<?php print($contextname);?>' :value="contextid" @change='switchContext'
|
<b-form-select text='{$contextname}' :value="contextid" @change='switchContext'
|
||||||
:class="(!(usedcontexts.length)) ? 'text-primary' : ''">
|
:class="(!(usedcontexts.length)) ? 'text-primary' : ''">
|
||||||
<b-form-select-option v-if='!(usedcontexts.length)' :value="contextid"
|
<b-form-select-option v-if='!(usedcontexts.length)' :value="contextid"
|
||||||
:class="'text-primary'">
|
:class="'text-primary'">
|
||||||
<span><?php t("loading", null, "core"); ?>...</span></b-form-select-option>
|
<span>{$text->loading}...</span></b-form-select-option>
|
||||||
<b-form-select-option v-for='ctx in usedcontexts' :key='ctx.id' :value="ctx.context_id"
|
<b-form-select-option v-for='ctx in usedcontexts' :key='ctx.id' :value="ctx.context_id"
|
||||||
:class="(ctx.studyplancount > 0) ? 'font-weight-bold' : ''"
|
:class="(ctx.studyplancount > 0) ? 'font-weight-bold' : ''"
|
||||||
><span v-for="(p, i) in ctx.category.path"
|
><span v-for="(p, i) in ctx.category.path"
|
||||||
><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</span>
|
><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</span>
|
||||||
</b-form-select-option>
|
</b-form-select-option>
|
||||||
</b-form-select>
|
</b-form-select>
|
||||||
<div v-if="!(usedcontexts.length)" style="position: relative; top: 0.3rem; width: 1.2rem; height: 1.2rem; font-size: 0.7rem;"
|
<div v-if="!(usedcontexts.length)"
|
||||||
|
style="position: relative; top: 0.3rem; width: 1.2rem; height: 1.2rem; font-size: 0.7rem;"
|
||||||
class="spinner-border text-primary" role="status"></div>
|
class="spinner-border text-primary" role="status"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<h3 v-else><?php print $contextname; ?></h3>
|
<h3 v-else>{$contextname}</h3>
|
||||||
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
||||||
<a href='#' v-if='activestudyplan' @click.prevent='closeStudyplan'
|
<a href='#' v-if='activestudyplan' @click.prevent='closeStudyplan'
|
||||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
||||||
<span v-if='activestudyplan'><?php t("studyplan_select"); ?></span>
|
<span v-if='activestudyplan'>{$text->studyplan_select}</span>
|
||||||
<b-form-select v-if='activestudyplan' lazy :text='dropdown_title' :value='activestudyplan.id' @change="selectStudyplan">
|
<b-form-select v-if='activestudyplan' lazy :text='dropdown_title' :value='activestudyplan.id' @change="selectStudyplan">
|
||||||
<b-form-select-option
|
<b-form-select-option
|
||||||
v-for='(studyplan, planindex) in studyplans'
|
v-for='(studyplan, planindex) in studyplans'
|
||||||
|
@ -181,7 +196,7 @@ print $OUTPUT->header();
|
||||||
<span class="sr-only">Loading...</span>
|
<span class="sr-only">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class='t-studyplan-notselected'>
|
<div v-else class='t-studyplan-notselected'>
|
||||||
<p><?php t("studyplan_noneselected"); ?></p>
|
<p>{$text->studyplan_noneselected}</p>
|
||||||
<b-card-group deck>
|
<b-card-group deck>
|
||||||
<s-studyplan-card
|
<s-studyplan-card
|
||||||
v-for='(studyplan, planindex) in studyplans'
|
v-for='(studyplan, planindex) in studyplans'
|
||||||
|
@ -193,7 +208,11 @@ print $OUTPUT->header();
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span class='s-studyplan-card-title-buttons'>
|
<span class='s-studyplan-card-title-buttons'>
|
||||||
<t-studyplan-edit v-model="studyplans[planindex]" @moved="movedStudyplan" @input="refreshTemplateCount"></t-studyplan-edit>
|
<t-studyplan-edit
|
||||||
|
v-model="studyplans[planindex]"
|
||||||
|
@moved="movedStudyplan"
|
||||||
|
@input="refreshTemplateCount"
|
||||||
|
></t-studyplan-edit>
|
||||||
<t-studyplan-associate v-model="studyplans[planindex]"></t-studyplan-associate>
|
<t-studyplan-associate v-model="studyplans[planindex]"></t-studyplan-associate>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -203,6 +222,5 @@ print $OUTPUT->header();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
END;
|
||||||
|
|
||||||
print $OUTPUT->footer();
|
print $OUTPUT->footer();
|
||||||
|
|
11
invited.php
11
invited.php
|
@ -90,19 +90,18 @@ if (!get_config("local_treestudyplan", "enableplansharing")) {
|
||||||
$PAGE->set_heading(get_string('report_invited', 'local_treestudyplan', "{$student->firstname} {$student->lastname}"));
|
$PAGE->set_heading(get_string('report_invited', 'local_treestudyplan', "{$student->firstname} {$student->lastname}"));
|
||||||
|
|
||||||
print $OUTPUT->header();
|
print $OUTPUT->header();
|
||||||
?>
|
print "
|
||||||
<div id='root'>
|
<div id='root'>
|
||||||
<div class='vue-loader' v-show='false'>
|
<div class='vue-loader' v-show='false'>
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class='spinner-border text-primary' role='status'>
|
||||||
<span class="sr-only">Loading...</span>
|
<span class='sr-only'>Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-cloak>
|
<div v-cloak>
|
||||||
<r-report type="invited" :invitekey="invitekey"></r-report>
|
<r-report type='invited' :invitekey='invitekey'></r-report>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
";
|
||||||
<?php
|
|
||||||
|
|
||||||
print $OUTPUT->footer();
|
print $OUTPUT->footer();
|
||||||
}
|
}
|
||||||
|
|
20
lib.php
20
lib.php
|
@ -56,7 +56,7 @@ function local_treestudyplan_autofill_customusermenuitems() {
|
||||||
"/local/treestudyplan/edit-plan.php" => ["included" => false, "strkey" => "link_editplan"],
|
"/local/treestudyplan/edit-plan.php" => ["included" => false, "strkey" => "link_editplan"],
|
||||||
];
|
];
|
||||||
if ((premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
if ((premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
||||||
// Also include the coach role if enabled
|
// Also include the coach role if enabled.
|
||||||
$navitems["/local/treestudyplan/coach.php"] = ["included" => false, "strkey" => "link_coach"];
|
$navitems["/local/treestudyplan/coach.php"] = ["included" => false, "strkey" => "link_coach"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ function local_treestudyplan_autofill_customusermenuitems() {
|
||||||
$link, // Link.
|
$link, // Link.
|
||||||
'', // Tooltip.
|
'', // Tooltip.
|
||||||
$lang, // Language code.
|
$lang, // Language code.
|
||||||
" #Automatically added by studyplan plugin. See setting 'primary_nav_autofill' to disable this"
|
" #Automatically added by studyplan plugin. See setting 'primary_nav_autofill' to disable this",
|
||||||
]);
|
]);
|
||||||
$custommenuitems = trim($custommenuitems)."\n".$line;
|
$custommenuitems = trim($custommenuitems)."\n".$line;
|
||||||
}
|
}
|
||||||
|
@ -198,8 +198,8 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
|
||||||
$hideprimaryhrefs[] = "/local/treestudyplan/edit-plan.php";
|
$hideprimaryhrefs[] = "/local/treestudyplan/edit-plan.php";
|
||||||
}
|
}
|
||||||
|
|
||||||
$coachsql = "SELECT COUNT('id') FROM {local_treestudyplan_coach} as c
|
$coachsql = "SELECT COUNT('id') FROM {local_treestudyplan_coach} c
|
||||||
INNER JOIN {local_treestudyplan} AS t ON c.studyplan_id = t.id
|
INNER JOIN {local_treestudyplan} t ON c.studyplan_id = t.id
|
||||||
WHERE c.user_id = :user_id";
|
WHERE c.user_id = :user_id";
|
||||||
|
|
||||||
if ((premium::enabled() && \get_config("local_treestudyplan", "enablecoach")) &&
|
if ((premium::enabled() && \get_config("local_treestudyplan", "enablecoach")) &&
|
||||||
|
@ -450,13 +450,13 @@ function local_treestudyplan_pluginfile(
|
||||||
|
|
||||||
// Studyplan icons and description images are not secret, so don't overdo it on access control...
|
// Studyplan icons and description images are not secret, so don't overdo it on access control...
|
||||||
if (true) {
|
if (true) {
|
||||||
// Extract the filename / filepath from the $args array
|
// Extract the filename / filepath from the $args array.
|
||||||
$filename = array_pop($args); // The last item in the $args array.
|
$filename = array_pop($args); // The last item in the $args array.
|
||||||
if (empty($args)) {
|
if (empty($args)) {
|
||||||
// $args is empty => the path is '/'.
|
// Var $args is empty => the path is '/'.
|
||||||
$filepath = '/';
|
$filepath = '/';
|
||||||
} else {
|
} else {
|
||||||
// $args contains the remaining elements of the filepath.
|
// Var $args contains the remaining elements of the filepath.
|
||||||
$filepath = '/' . implode('/', $args) . '/';
|
$filepath = '/' . implode('/', $args) . '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,13 +478,13 @@ function local_treestudyplan_pluginfile(
|
||||||
// Fetch the itemid from the path.
|
// Fetch the itemid from the path.
|
||||||
$itemid = array_shift($args);
|
$itemid = array_shift($args);
|
||||||
|
|
||||||
// Extract the filename / filepath from the $args array
|
// Extract the filename / filepath from the $args array.
|
||||||
$filename = array_pop($args); // The last item in the $args array.
|
$filename = array_pop($args); // The last item in the $args array.
|
||||||
if (empty($args)) {
|
if (empty($args)) {
|
||||||
// $args is empty => the path is '/'.
|
// Var $args is empty => the path is '/'.
|
||||||
$filepath = '/';
|
$filepath = '/';
|
||||||
} else {
|
} else {
|
||||||
// $args contains the remaining elements of the filepath.
|
// Var $args contains the remaining elements of the filepath.
|
||||||
$filepath = '/' . implode('/', $args) . '/';
|
$filepath = '/' . implode('/', $args) . '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ require_login();
|
||||||
$PAGE->set_pagelayout('base');
|
$PAGE->set_pagelayout('base');
|
||||||
$PAGE->set_context($systemcontext);
|
$PAGE->set_context($systemcontext);
|
||||||
|
|
||||||
//$teachermode = has_capability("local/treestudyplan:viewuserreports", $systemcontext);
|
|
||||||
$amteaching = teachingfinder::is_teaching($USER->id);
|
$amteaching = teachingfinder::is_teaching($USER->id);
|
||||||
$haveplans = studyplan::exist_for_user($USER->id);
|
$haveplans = studyplan::exist_for_user($USER->id);
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@
|
||||||
require_once("../../config.php");
|
require_once("../../config.php");
|
||||||
|
|
||||||
use local_treestudyplan\contextinfo;
|
use local_treestudyplan\contextinfo;
|
||||||
use local_treestudyplan\courseservice;
|
|
||||||
use local_treestudyplan\studyplan;
|
|
||||||
use local_treestudyplan\studyplanpage;
|
use local_treestudyplan\studyplanpage;
|
||||||
use local_treestudyplan\premium;
|
use local_treestudyplan\premium;
|
||||||
|
|
||||||
|
@ -42,8 +40,8 @@ $context = $studyplan->context();
|
||||||
$ci = new contextinfo($context);
|
$ci = new contextinfo($context);
|
||||||
$contextname = $ci->pathstr();
|
$contextname = $ci->pathstr();
|
||||||
|
|
||||||
$firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show
|
$firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show.
|
||||||
$lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show
|
$lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show.
|
||||||
|
|
||||||
$PAGE->set_pagelayout('report');
|
$PAGE->set_pagelayout('report');
|
||||||
$PAGE->set_title(get_string('studyplan_report', 'local_treestudyplan'));
|
$PAGE->set_title(get_string('studyplan_report', 'local_treestudyplan'));
|
||||||
|
@ -62,7 +60,11 @@ $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/boot
|
||||||
if ($CFG->debugdeveloper) {
|
if ($CFG->debugdeveloper) {
|
||||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
|
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
|
||||||
}
|
}
|
||||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-result-overview', 'init', [$studyplan->id(), $page->id(), $firstperiod, $lastperiod]);
|
$PAGE->requires->js_call_amd(
|
||||||
|
'local_treestudyplan/page-result-overview',
|
||||||
|
'init',
|
||||||
|
[$studyplan->id(), $page->id(), $firstperiod, $lastperiod]
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut function to provide translations
|
* Shortcut function to provide translations
|
||||||
|
@ -81,30 +83,30 @@ print "<h3><b>{$contextname}</b> / {$studyplan->name()}</h3>";
|
||||||
if ($studyplan->name() != $page->fullname()) {
|
if ($studyplan->name() != $page->fullname()) {
|
||||||
print "<h4>{$page->fullname()}</h4>";
|
print "<h4>{$page->fullname()}</h4>";
|
||||||
}
|
}
|
||||||
?>
|
print <<<END
|
||||||
<div id='root'>
|
<div id='root'>
|
||||||
<div class='vue-loader' v-show='false'>
|
<div class='vue-loader' v-show='false'>
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class='spinner-border text-primary' role='status'>
|
||||||
<span class="sr-only">Loading...</span>
|
<span class='sr-only'>Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-cloak>
|
<div v-cloak>
|
||||||
<div class='vue-loader' v-if="!structure">
|
<div class='vue-loader' v-if='!structure'>
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class='spinner-border text-primary' role='status'>
|
||||||
<span class="sr-only">Loading...</span>
|
<span class='sr-only'>Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class='container q-pageperiodselection'>
|
<div class='container q-pageperiodselection'>
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class="col-sm-2 q-label">{{text.page}}</div>
|
<div class='col-sm-2 q-label'>{{text.page}}</div>
|
||||||
<div class="col-sm-6">
|
<div class='col-sm-6'>
|
||||||
<select class="custom-select" :value="page.id" @change="selectedPage">
|
<select class='custom-select' :value='page.id' @change='selectedPage'>
|
||||||
<template v-if="studyplan">
|
<template v-if='studyplan'>
|
||||||
<option v-for="p in studyplan.pages"
|
<option v-for='p in studyplan.pages'
|
||||||
:key="p.id"
|
:key='p.id'
|
||||||
:value="p.id"
|
:value='p.id'
|
||||||
:selected="(page.id == p.id)?true:false"
|
:selected='(page.id == p.id) ? true : false'
|
||||||
>{{p.fullname}}</option>
|
>{{p.fullname}}</option>
|
||||||
</template>
|
</template>
|
||||||
<option v-else disabled>{{text.loading}}</option>
|
<option v-else disabled>{{text.loading}}</option>
|
||||||
|
@ -112,27 +114,27 @@ if ($studyplan->name() != $page->fullname()) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class="col-sm-2 q-label" >{{text.period}} ({{text.from}} / {{text.to}})</div>
|
<div class='col-sm-2 q-label' >{{text.period}} ({{text.from}} / {{text.to}})</div>
|
||||||
<div class="col-sm-2">
|
<div class='col-sm-2'>
|
||||||
<select class="custom-select" @change="selectedFirstPeriod">
|
<select class='custom-select' @change='selectedFirstPeriod'>
|
||||||
<template v-if="page">
|
<template v-if='page'>
|
||||||
<option v-for="p in page.perioddesc"
|
<option v-for='p in page.perioddesc'
|
||||||
:key="p.id"
|
:key='p.id'
|
||||||
:value="p.period"
|
:value='p.period'
|
||||||
:selected="(structure.firstperiod == p.period)?true:false"
|
:selected='(structure.firstperiod == p.period) ? true : false'
|
||||||
>{{p.fullname}}</option>
|
>{{p.fullname}}</option>
|
||||||
</template>
|
</template>
|
||||||
<option v-else disabled>{{text.loading}}</option>
|
<option v-else disabled>{{text.loading}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2 ">
|
<div class='col-sm-2 '>
|
||||||
<select class="custom-select" @change="selectedLastPeriod">
|
<select class='custom-select' @change='selectedLastPeriod'>
|
||||||
<template v-if="page">
|
<template v-if='page'>
|
||||||
<template v-for="p in page.perioddesc">
|
<template v-for='p in page.perioddesc'>
|
||||||
<option v-if="p.period >= structure.firstperiod"
|
<option v-if='p.period >= structure.firstperiod'
|
||||||
:key="p.id"
|
:key='p.id'
|
||||||
:value="p.period"
|
:value='p.period'
|
||||||
:selected="(structure.lastperiod == p.period)?true:false"
|
:selected='(structure.lastperiod == p.period) ? true : false'
|
||||||
>{{p.fullname}}</option>
|
>{{p.fullname}}</option>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
@ -141,10 +143,10 @@ if ($studyplan->name() != $page->fullname()) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-studyplanreport :structure="structure" ></s-studyplanreport>
|
<q-studyplanreport :structure='structure' ></s-studyplanreport>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
END;
|
||||||
|
|
||||||
print $OUTPUT->footer();
|
print $OUTPUT->footer();
|
||||||
|
|
27
settings.php
27
settings.php
|
@ -55,7 +55,7 @@ if ($hassiteconfig) {
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Default image for study plans
|
// Default image for study plans.
|
||||||
$page->add(new admin_setting_configstoredfile('local_treestudyplan/defaulticon',
|
$page->add(new admin_setting_configstoredfile('local_treestudyplan/defaulticon',
|
||||||
get_string('setting_defaulticon', 'local_treestudyplan'),
|
get_string('setting_defaulticon', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_defaulticon', 'local_treestudyplan'),
|
get_string('settingdesc_defaulticon', 'local_treestudyplan'),
|
||||||
|
@ -89,8 +89,17 @@ if ($hassiteconfig) {
|
||||||
get_string('settingdesc_display_heading', 'local_treestudyplan')
|
get_string('settingdesc_display_heading', 'local_treestudyplan')
|
||||||
));
|
));
|
||||||
|
|
||||||
$displayfields = ["shortname" => get_string("shortname"), "idnumber" => get_string("idnumber"), "fullname" => get_string("fullname") ];
|
$displayfields = [
|
||||||
$infofields = ["" => get_string('none'), "description" => get_string("description"), "contacts" => get_string("teachers"), "idnumber" => get_string("idnumber")];
|
"shortname" => get_string("shortname"),
|
||||||
|
"idnumber" => get_string("idnumber"),
|
||||||
|
"fullname" => get_string("fullname"),
|
||||||
|
];
|
||||||
|
$infofields = [
|
||||||
|
"" => get_string('none'),
|
||||||
|
"description" => get_string("description"),
|
||||||
|
"contacts" => get_string("teachers"),
|
||||||
|
"idnumber" => get_string("idnumber"),
|
||||||
|
];
|
||||||
$handler = \core_customfield\handler::get_handler('core_course', 'course');
|
$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) {
|
||||||
|
@ -106,49 +115,43 @@ if ($hassiteconfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//get_config("local_treestudyplan", "enableplansharing")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/enableplansharing',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/enableplansharing',
|
||||||
get_string('setting_enableplansharing', 'local_treestudyplan'),
|
get_string('setting_enableplansharing', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_enableplansharing', 'local_treestudyplan'),
|
get_string('settingdesc_enableplansharing', 'local_treestudyplan'),
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
//get_config("local_treestudyplan", "timelessperiods")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/timelessperiods',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/timelessperiods',
|
||||||
get_string('setting_timelessperiods', 'local_treestudyplan'),
|
get_string('setting_timelessperiods', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_timelessperiods', 'local_treestudyplan'),
|
get_string('settingdesc_timelessperiods', 'local_treestudyplan'),
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
//get_config("local_treestudyplan", "limitcourselist")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/limitcourselist',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/limitcourselist',
|
||||||
get_string('setting_limitcourselist', 'local_treestudyplan'),
|
get_string('setting_limitcourselist', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_limitcourselist', 'local_treestudyplan'),
|
get_string('settingdesc_limitcourselist', 'local_treestudyplan'),
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
//get_config("local_treestudyplan", "hivizdropslots")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/hivizdropslots',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/hivizdropslots',
|
||||||
get_string('setting_hivizdropslots', 'local_treestudyplan'),
|
get_string('setting_hivizdropslots', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_hivizdropslots', 'local_treestudyplan'),
|
get_string('settingdesc_hivizdropslots', 'local_treestudyplan'),
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
//get_config("local_treestudyplan", "toolboxleft")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/toolboxleft',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/toolboxleft',
|
||||||
get_string('setting_toolboxleft', 'local_treestudyplan'),
|
get_string('setting_toolboxleft', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_toolboxleft', 'local_treestudyplan'),
|
get_string('settingdesc_toolboxleft', 'local_treestudyplan'),
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
//get_config("local_treestudyplan", "copystudylinesnewpage")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/copystudylinesnewpage',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/copystudylinesnewpage',
|
||||||
get_string('setting_copystudylinesnewpage', 'local_treestudyplan'),
|
get_string('setting_copystudylinesnewpage', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_copystudylinesnewpage', 'local_treestudyplan'),
|
get_string('settingdesc_copystudylinesnewpage', 'local_treestudyplan'),
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
//get_config("local_treestudyplan", "continueperiodnumberingnewpage")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/continueperiodnumberingnewpage',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/continueperiodnumberingnewpage',
|
||||||
get_string('setting_continueperiodnumberingnewpage', 'local_treestudyplan'),
|
get_string('setting_continueperiodnumberingnewpage', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_continueperiodnumberingnewpage', 'local_treestudyplan'),
|
get_string('settingdesc_continueperiodnumberingnewpage', 'local_treestudyplan'),
|
||||||
|
@ -156,7 +159,6 @@ if ($hassiteconfig) {
|
||||||
));
|
));
|
||||||
|
|
||||||
if (premium::enabled()) {
|
if (premium::enabled()) {
|
||||||
//get_config("local_treestudyplan", "enablecoach")
|
|
||||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/enablecoach',
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/enablecoach',
|
||||||
get_string('setting_enablecoach', 'local_treestudyplan'),
|
get_string('setting_enablecoach', 'local_treestudyplan'),
|
||||||
get_string('settingdesc_enablecoach', 'local_treestudyplan'),
|
get_string('settingdesc_enablecoach', 'local_treestudyplan'),
|
||||||
|
@ -410,7 +412,7 @@ if ($hassiteconfig) {
|
||||||
));
|
));
|
||||||
|
|
||||||
if ($CFG->debugdeveloper) {
|
if ($CFG->debugdeveloper) {
|
||||||
// show decrypted key data
|
// Show decrypted key data.
|
||||||
$pagepremium->add(new admin_setting_description('local_treestudyplan/premium_debug',
|
$pagepremium->add(new admin_setting_description('local_treestudyplan/premium_debug',
|
||||||
get_string('setting_premium_debug', 'local_treestudyplan'),
|
get_string('setting_premium_debug', 'local_treestudyplan'),
|
||||||
premium::debuginfo() . "<br> <br>" // Add empty row at end.
|
premium::debuginfo() . "<br> <br>" // Add empty row at end.
|
||||||
|
@ -420,5 +422,4 @@ if ($hassiteconfig) {
|
||||||
// Add settings page2 to the admin settings category.
|
// Add settings page2 to the admin settings category.
|
||||||
$ADMIN->add('local_treestudyplan', $pagepremium);
|
$ADMIN->add('local_treestudyplan', $pagepremium);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
83
test.php
83
test.php
|
@ -1,83 +0,0 @@
|
||||||
<?php
|
|
||||||
function sitematch($key, $site) {
|
|
||||||
// Add double slashes to key and site if no scheme is set.
|
|
||||||
// Basically: if no double slashes present before any dots, shashes or @s.
|
|
||||||
if (!\preg_match_all('#^[^./@]*?//#', $key )) {
|
|
||||||
$key = "//".$key;
|
|
||||||
}
|
|
||||||
if (!\preg_match_all('#^[^./@]*?//#', $site)) {
|
|
||||||
$site = "//".$site;
|
|
||||||
}
|
|
||||||
// Use parse_url() to split path and host.
|
|
||||||
$keyurl = (object)\parse_url($key);
|
|
||||||
$siteurl = (object)\parse_url($site);
|
|
||||||
|
|
||||||
// No match if host is empty on key or site
|
|
||||||
if (empty($keyurl->host) || empty($siteurl->host)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($keyurl->host == "*") {
|
|
||||||
// * matches all
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First match the host part.
|
|
||||||
$keyparts = \array_reverse(\explode(".", $keyurl->host));
|
|
||||||
$siteparts = \array_reverse(\explode(".", $siteurl->host));
|
|
||||||
|
|
||||||
// Trim starting www from both parts, since site.domain and www.site.domain should be treated as the same.
|
|
||||||
if (($x = \array_pop($keyparts)) != "www") {\array_push($keyparts, $x);}
|
|
||||||
if (($x = \array_pop($siteparts)) != "www") {\array_push($siteparts, $x);}
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($keyparts); $i++) {
|
|
||||||
// No match if the site does not have a part, but the key does. Unless the key part is *
|
|
||||||
if (!isset($siteparts[$i]) ) {
|
|
||||||
if ($keyparts[$i] != "*") {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
$i++; //increment $i by one before break, to make sure the comparison following this loop holds.
|
|
||||||
break; // Stop comparison. Host part matches.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now do a proper case insensitive check for matching.
|
|
||||||
// Uses fnmatch to easily handle shell type wildcards.
|
|
||||||
if ( ! \fnmatch($keyparts[$i], $siteparts[$i], \FNM_CASEFOLD)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Fail if the site has a deeper subdomain than the key, unless the deepest key subdomain is *
|
|
||||||
if ($keyparts[$i-1] != '*' && count($siteparts) > ($i)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we made it here then the host part matches. Now check the path.
|
|
||||||
// If path is /*, matches all subpaths including /
|
|
||||||
$keypath = empty($keyurl->path) ? "/" : $keyurl->path;
|
|
||||||
$sitepath = empty($siteurl->path) ? "/" : $siteurl->path;
|
|
||||||
|
|
||||||
// Trim trailing / from both paths before comparison
|
|
||||||
if (\strlen($sitepath) > 1) {
|
|
||||||
$sitepath = \rtrim($sitepath, "/");
|
|
||||||
}
|
|
||||||
if (\strlen($keypath) > 1) {
|
|
||||||
$keypath = \rtrim($keypath, "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a case insensitive fnmatch on the site so wildcards are matched too.
|
|
||||||
return \fnmatch($keypath, $sitepath, \FNM_CASEFOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tests = [
|
|
||||||
["*", "https://www.miqra.nl"],
|
|
||||||
["*/*", "https://www.miqra.nl"],
|
|
||||||
["*", "https://clients.openedu.nl/fith"],
|
|
||||||
["clients.openedu.nl/fith", "https://clients.openedu.nl/fith/"],
|
|
||||||
["clients.openedu.nl/fith/", "https://clients.openedu.nl/fith"],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach($tests as $test) {
|
|
||||||
[$key, $site] = $test;
|
|
||||||
print("Checking key '{$key}' on site '{$site}': " . (sitematch($key, $site)?"MATCH":"FAIL") . "\n");
|
|
||||||
}
|
|
|
@ -65,7 +65,6 @@ $ci = new contextinfo($studyplancontext);
|
||||||
$contextname = $ci->pathstr();
|
$contextname = $ci->pathstr();
|
||||||
|
|
||||||
$PAGE->set_pagelayout('base');
|
$PAGE->set_pagelayout('base');
|
||||||
//$PAGE->set_context($studyplancontext);
|
|
||||||
$PAGE->set_title(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);
|
$PAGE->set_heading(get_string('view_plan', 'local_treestudyplan')." - ".$contextname);
|
||||||
|
|
||||||
|
@ -73,19 +72,29 @@ if ($studyplancontext->id > 1) {
|
||||||
navigation_node::override_active_url(new moodle_url('/course/index.php', ['categoryid' => $categoryid ]));
|
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'));
|
||||||
|
|
||||||
// Coursecat context
|
// Coursecat context.
|
||||||
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later
|
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later.
|
||||||
} else {
|
} else {
|
||||||
// System context
|
// System context.
|
||||||
$cat = \core_course_category::top();
|
$cat = \core_course_category::top();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$cat->is_uservisible()) {
|
if (!$cat->is_uservisible()) {
|
||||||
throw new \moodle_exception("error:cannotviewcategory", "local_treestudyplan", "/local/treestudyplan/view_plan.php", $contextname);
|
throw new \moodle_exception(
|
||||||
|
"error:cannotviewcategory",
|
||||||
|
"local_treestudyplan",
|
||||||
|
"/local/treestudyplan/view_plan.php",
|
||||||
|
$contextname
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_capability('local/treestudyplan:viewuserreports', $studyplancontext)) {
|
if (!has_capability('local/treestudyplan:viewuserreports', $studyplancontext)) {
|
||||||
throw new \moodle_exception("error:nostudyplanviewaccess", "local_treestudyplan", "/local/treestudyplan/view_plan.php", $contextname);
|
throw new \moodle_exception(
|
||||||
|
"error:nostudyplanviewaccess",
|
||||||
|
"local_treestudyplan",
|
||||||
|
"/local/treestudyplan/view_plan.php",
|
||||||
|
$contextname
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load javascripts and specific css.
|
// Load javascripts and specific css.
|
||||||
|
@ -108,7 +117,15 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
||||||
}
|
}
|
||||||
|
|
||||||
print $OUTPUT->header();
|
print $OUTPUT->header();
|
||||||
?>
|
$text = (object)[
|
||||||
|
'loading' => t("loading", null, "core"),
|
||||||
|
'back' => t('back'),
|
||||||
|
'studyplan_select' => t("studyplan_select"),
|
||||||
|
'selectstudent_btn' => t('selectstudent_btn'),
|
||||||
|
'showoverview' => t("showoverview"),
|
||||||
|
'studyplan_noneselected' => t("studyplan_noneselected"),
|
||||||
|
];
|
||||||
|
print <<<END
|
||||||
<div id='root'>
|
<div id='root'>
|
||||||
<div class='vue-loader' v-show='false'>
|
<div class='vue-loader' v-show='false'>
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
@ -117,25 +134,26 @@ print $OUTPUT->header();
|
||||||
</div>
|
</div>
|
||||||
<div v-cloak>
|
<div v-cloak>
|
||||||
<div v-if='!activestudyplan && usedcontexts' class='ml-3 mb-3 s-context-selector'>
|
<div v-if='!activestudyplan && usedcontexts' class='ml-3 mb-3 s-context-selector'>
|
||||||
<b-form-select text='<?php print($contextname);?>' :value="contextid" @change='switchContext'
|
<b-form-select text='{$contextname}' :value="contextid" @change='switchContext'
|
||||||
:class="(!(usedcontexts.length)) ? 'text-primary' : ''">
|
:class="(!(usedcontexts.length)) ? 'text-primary' : ''">
|
||||||
<b-form-select-option v-if='!(usedcontexts.length)' :value="contextid"
|
<b-form-select-option v-if='!(usedcontexts.length)' :value="contextid"
|
||||||
:class="'text-primary'">
|
:class="'text-primary'">
|
||||||
<span><?php t("loading", null, "core"); ?>...</span></b-form-select-option>
|
<span>{$text->loading}...</span></b-form-select-option>
|
||||||
<b-form-select-option v-for='ctx in usedcontexts' :key='ctx.id' :value="ctx.context_id"
|
<b-form-select-option v-for='ctx in usedcontexts' :key='ctx.id' :value="ctx.context_id"
|
||||||
:class="(ctx.studyplancount > 0) ? 'font-weight-bold' : ''"
|
:class="(ctx.studyplancount > 0) ? 'font-weight-bold' : ''"
|
||||||
><span v-for="(p, i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span>
|
><span v-for="(p, i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span>
|
||||||
<span>({{ ctx.studyplancount }})</span></b-form-select-option>
|
<span>({{ ctx.studyplancount }})</span></b-form-select-option>
|
||||||
</b-form-select>
|
</b-form-select>
|
||||||
<div v-if="!(usedcontexts.length)" style="position: relative; top: 0.3rem; width: 1.2rem; height: 1.2rem; font-size: 0.7rem;"
|
<div v-if="!(usedcontexts.length)"
|
||||||
|
style="position: relative; top: 0.3rem; width: 1.2rem; height: 1.2rem; font-size: 0.7rem;"
|
||||||
class="spinner-border text-primary" role="status"></div>
|
class="spinner-border text-primary" role="status"></div>
|
||||||
</div>
|
</div>
|
||||||
<h3 v-else><?php print $contextname; ?></h3>
|
<h3 v-else>{$contextname}</h3>
|
||||||
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
||||||
<template v-if="displayedstudyplan">
|
<template v-if="displayedstudyplan">
|
||||||
<a href='#' @click.prevent='closeStudyplan'
|
<a href='#' @click.prevent='closeStudyplan'
|
||||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> {$text->back}</a>
|
||||||
<span><?php t("studyplan_select"); ?></span>
|
<span>{$text->studyplan_select}</span>
|
||||||
<b-form-select lazy :text='dropdown_title' :value='displayedstudyplan.id'>
|
<b-form-select lazy :text='dropdown_title' :value='displayedstudyplan.id'>
|
||||||
<b-form-select-option
|
<b-form-select-option
|
||||||
v-for='(studyplan, planindex) in studyplans'
|
v-for='(studyplan, planindex) in studyplans'
|
||||||
|
@ -164,7 +182,7 @@ print $OUTPUT->header();
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
|
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
|
||||||
<template #defaultlabel><span class='text-primary'><?php t("showoverview"); ?></span></template>
|
<template #defaultlabel><span class='text-primary'>{$text->showoverview}</span></template>
|
||||||
</s-prevnext-selector>
|
</s-prevnext-selector>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -172,7 +190,7 @@ print $OUTPUT->header();
|
||||||
<div class='t-studyplan-container'>
|
<div class='t-studyplan-container'>
|
||||||
<h2 v-if='displayedstudyplan&& selectedstudent'
|
<h2 v-if='displayedstudyplan&& selectedstudent'
|
||||||
>{{selectedstudent.firstname}} {{selectedstudent.lastname}} - {{displayedstudyplan.name}}</h2>
|
>{{selectedstudent.firstname}} {{selectedstudent.lastname}} - {{displayedstudyplan.name}}</h2>
|
||||||
<h2 v-else-if='displayedstudyplan'><?php t("showoverview"); ?> - {{displayedstudyplan.name}}</h2>
|
<h2 v-else-if='displayedstudyplan'>{$text->showoverview} - {{displayedstudyplan.name}}</h2>
|
||||||
<r-studyplan v-if='!loadingstudyplan && displayedstudyplan'
|
<r-studyplan v-if='!loadingstudyplan && displayedstudyplan'
|
||||||
v-model='displayedstudyplan' :teachermode='!selectedstudent'
|
v-model='displayedstudyplan' :teachermode='!selectedstudent'
|
||||||
></r-studyplan>
|
></r-studyplan>
|
||||||
|
@ -180,7 +198,7 @@ print $OUTPUT->header();
|
||||||
<span class="sr-only">Loading...</span>
|
<span class="sr-only">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class='t-studyplan-notselected'>
|
<div v-else class='t-studyplan-notselected'>
|
||||||
<p><?php t("studyplan_noneselected"); ?></p>
|
<p>{$text->studyplan_noneselected}</p>
|
||||||
<b-card-group deck>
|
<b-card-group deck>
|
||||||
<s-studyplan-card
|
<s-studyplan-card
|
||||||
v-for='(studyplan, planindex) in studyplans'
|
v-for='(studyplan, planindex) in studyplans'
|
||||||
|
@ -194,6 +212,6 @@ print $OUTPUT->header();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
END;
|
||||||
|
|
||||||
print $OUTPUT->footer();
|
print $OUTPUT->footer();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user