Completed code guidelines rework
This commit is contained in:
parent
e72be595aa
commit
64c821bbe9
|
@ -23,7 +23,6 @@
|
|||
namespace local_treestudyplan;
|
||||
|
||||
use moodle_exception;
|
||||
use ValueError;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
@ -78,7 +77,7 @@ abstract class aggregator {
|
|||
* Create a new aggregatior object based on the specified method
|
||||
* @param mixed $method Aggregation method
|
||||
* @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 {
|
||||
if (self::supported($method)) {
|
||||
|
@ -98,7 +97,7 @@ abstract class aggregator {
|
|||
public static function create_or_default($method, $configstr): self {
|
||||
try {
|
||||
return self::create($method, $configstr);
|
||||
} catch (\ValueError $x) {
|
||||
} catch (\moodle_exception $x) {
|
||||
return self::create(self::FALLBACK, "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ class associationservice extends \external_api {
|
|||
"lastname" => new \external_value(PARAM_TEXT, 'last name'),
|
||||
"idnumber" => new \external_value(PARAM_TEXT, 'id number'),
|
||||
"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) {
|
||||
global $DB;
|
||||
if (!empty($studyplanid)) {
|
||||
|
@ -735,7 +741,7 @@ class associationservice extends \external_api {
|
|||
webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
|
||||
|
||||
$user = $DB->get_record("user", ["id" => $userid]);
|
||||
if ( has_capability(self::CAP_COACH, $studyplan->context(), $user)) {
|
||||
if (has_capability(self::CAP_COACH, $studyplan->context(), $user)) {
|
||||
if (!$DB->record_exists('local_treestudyplan_coach', ['studyplan_id' => $studyplanid, 'user_id' => $userid])) {
|
||||
$id = $DB->insert_record('local_treestudyplan_coach', [
|
||||
'studyplan_id' => $studyplanid,
|
||||
|
|
|
@ -60,7 +60,7 @@ class badgeinfo {
|
|||
BADGE_STATUS_ACTIVE => 0,
|
||||
BADGE_STATUS_INACTIVE_LOCKED => 1,
|
||||
BADGE_STATUS_ACTIVE_LOCKED => 1,
|
||||
BADGE_STATUS_ARCHIVED => 1, // We don't want to edit archived badges anyway.... .
|
||||
BADGE_STATUS_ARCHIVED => 1, // We don't want to edit archived badges anyway.....
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -270,13 +270,17 @@ class badgeinfo {
|
|||
return $badge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define structure for badge completion parts
|
||||
* @param object $value VALUE_OPTIONAL or VALUE_REQUIRED
|
||||
*/
|
||||
protected static function badge_completion_structure($value) {
|
||||
return new \external_single_structure([
|
||||
"types" => new \external_multiple_structure(new \external_single_structure([
|
||||
'criteria' => new \external_multiple_structure(new \external_single_structure([
|
||||
"title" => new \external_value(PARAM_RAW, 'criterion title'),
|
||||
"description" => new \external_value(PARAM_RAW, 'criterion description'),
|
||||
"link"=> new \external_value(PARAM_RAW, 'link to criterion resource', VALUE_OPTIONAL),
|
||||
"link" => new \external_value(PARAM_RAW, 'link to criterion resource', VALUE_OPTIONAL),
|
||||
"requirements" => new \external_multiple_structure(new \external_single_structure([
|
||||
"title" => new \external_value(PARAM_RAW, 'requirment title'),
|
||||
"completed" => new \external_value(PARAM_BOOL, 'criterion is completed or not', VALUE_OPTIONAL),
|
||||
|
@ -297,6 +301,10 @@ class badgeinfo {
|
|||
], '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 {
|
||||
|
||||
$count = 0;
|
||||
|
@ -323,14 +331,14 @@ class badgeinfo {
|
|||
// Determine how to process the progress data, depending on the TYPE's aggregation.
|
||||
if ($typeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
|
||||
$typecount = 1;
|
||||
$typeprogress = ($typeprogress > 0)?1:0;
|
||||
$typeprogress = ($typeprogress > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Determine how to patch this data into the overall progress numbers, depending on the OVERALL aggregation.
|
||||
if ($badgeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
|
||||
/* If ANY completion overall, count only the criteria type with the highest completion percentage -.
|
||||
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)) {
|
||||
$fraction = $typefraction;
|
||||
$count = $typecount;
|
||||
|
@ -342,7 +350,7 @@ class badgeinfo {
|
|||
$progress += $typeprogress;
|
||||
}
|
||||
|
||||
$aggrgationhandle = ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any";
|
||||
$aggrgationhandle = ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL) ? "all" : "any";
|
||||
$typeinfo = [
|
||||
'title' => ucfirst(get_string("criteria_descr_$type", "badges", get_string($aggrgationhandle, "core"))),
|
||||
'aggregation' => $aggrgationhandle,
|
||||
|
@ -355,7 +363,7 @@ class badgeinfo {
|
|||
}
|
||||
}
|
||||
|
||||
$aggrgationhandle = ($badgeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any";
|
||||
$aggrgationhandle = ($badgeagg == BADGE_CRITERIA_AGGREGATION_ALL) ? "all" : "any";
|
||||
return [
|
||||
"types" => $types,
|
||||
"title" => ucfirst(get_string("criteria_descr_0", "badges", mb_strtolower(get_string($aggrgationhandle, "core")))),
|
||||
|
@ -444,7 +452,8 @@ class badgeinfo {
|
|||
$title = \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'])) {
|
||||
$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').
|
||||
get_string('modulename', $mod->modname) . ' - ' . $mod->name,
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($p["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)) {
|
||||
$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;
|
||||
}
|
||||
$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) {
|
||||
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 {
|
||||
$params = $crit->params;
|
||||
}
|
||||
|
@ -534,7 +543,8 @@ class badgeinfo {
|
|||
} else {
|
||||
$description = \html_writer::tag('b', '"' . $course->fullname . '"');
|
||||
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'])) {
|
||||
$description .= get_string('criteria_descr_grade', 'badges', $p['grade']);
|
||||
|
@ -549,18 +559,18 @@ class badgeinfo {
|
|||
'completion' => [
|
||||
'title' => get_string('coursecompleted', 'completion'),
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($p["grade"])) {
|
||||
$subcrit["requirements"]["grade"] = [
|
||||
'title' => get_string('criteria_descr_grade', 'badges', $p["grade"]),
|
||||
|
||||
];
|
||||
}
|
||||
if (isset($p["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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -592,7 +603,7 @@ class badgeinfo {
|
|||
$fields = profile_get_custom_fields();
|
||||
// Get formatted field name if such field exists.
|
||||
$fieldname = isset($fields[$p['field']]->name) ?
|
||||
format_string($fields[$p['field']]->name): null;
|
||||
format_string($fields[$p['field']]->name) : null;
|
||||
} else {
|
||||
$fieldname = \core_user\fields::get_display_name($p['field']);
|
||||
}
|
||||
|
@ -614,7 +625,7 @@ class badgeinfo {
|
|||
if (isset($userid)) {
|
||||
$join = '';
|
||||
$where = '';
|
||||
$sqlparams = [ 'userid'=> $userid];
|
||||
$sqlparams = [ 'userid' => $userid];
|
||||
|
||||
if (is_numeric($p['field'])) {
|
||||
// This is a custom field.
|
||||
|
@ -773,17 +784,6 @@ class badgeinfo {
|
|||
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);
|
||||
$conditions = "";
|
||||
$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);
|
||||
|
||||
$sql = "SELECT DISTINCT b.id from {badge} b
|
||||
|
@ -860,7 +861,7 @@ class badgeinfo {
|
|||
$basesqlparams = array_merge($basesqlparams, $inparams);
|
||||
}
|
||||
|
||||
// Get all course badges for this course
|
||||
// Get all course badges for this course.
|
||||
$badgesids = [];
|
||||
$sql = "SELECT DISTINCT b.id from {badge} b
|
||||
WHERE b.courseid = :courseid AND $conditions";
|
||||
|
@ -886,14 +887,14 @@ class badgeinfo {
|
|||
$badgeids = [];
|
||||
foreach (studyline::find_page_children($page) as $line) {
|
||||
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();
|
||||
$relatedbadges = badgeinfo::find_badges_by_course_relation($courseid, $search, $active);
|
||||
$relatedbadges = self::find_badges_by_course_relation($courseid, $search, $active);
|
||||
foreach ($relatedbadges as $id) {
|
||||
$badgeids[] = $id;
|
||||
}
|
||||
if ($includecoursebadges) {
|
||||
$coursebadges = badgeinfo::find_course_badges($courseid, $search, $active);
|
||||
$coursebadges = self::find_course_badges($courseid, $search, $active);
|
||||
foreach ($coursebadges as $id) {
|
||||
$badgeids[] = $id;
|
||||
}
|
||||
|
@ -936,7 +937,7 @@ class badgeinfo {
|
|||
$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
|
||||
WHERE $conditions";
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ class cascadecohortsync {
|
|||
$records = $DB->get_records("enrol", $searchparams);
|
||||
foreach ($records as $instance) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ class cascadecohortsync {
|
|||
// Find the study lines associated to this studyplan.
|
||||
$lines = $this->studyplan->get_all_studylines();
|
||||
|
||||
foreach($lines as $line) {
|
||||
foreach ($lines as $line) {
|
||||
$this->syncline($line);
|
||||
|
||||
}
|
||||
|
@ -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...
|
||||
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
|
||||
|
@ -224,7 +225,7 @@ class cascadecohortsync {
|
|||
|
||||
if (get_config("local_treestudyplan", "csync_autoremove")) {
|
||||
// Only try the autoremove if the option is enabled.
|
||||
foreach($this->list_cohortsyncs($courseid) as $instance) {
|
||||
foreach ($this->list_cohortsyncs($courseid) as $instance) {
|
||||
$this->unlink_cohortsync($instance);
|
||||
}
|
||||
|
||||
|
@ -262,7 +263,6 @@ class cascadecohortsync {
|
|||
// It already exists.
|
||||
// Check if this studyplan is already referenced in customtext4 in json format.
|
||||
|
||||
// TODO: Check this code - Maybe add option to not remember manually added stuff .
|
||||
$plans = json_decode($instance->customtext4);
|
||||
if ($plans == false || !is_array(($plans))) {
|
||||
// If the data was not an array (null or garbled), count it as manually added.
|
||||
|
@ -297,7 +297,8 @@ class cascadecohortsync {
|
|||
// In the customtext4 field in json format.
|
||||
|
||||
$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.
|
||||
// First synchronise the enrolment.
|
||||
|
@ -322,7 +323,7 @@ class cascadecohortsync {
|
|||
if (get_config("local_treestudyplan", "csync_autoremove")) {
|
||||
// Only try the autoremove if the option is enabled.
|
||||
|
||||
foreach($this->list_cohortsyncs($courseid) as $instance) {
|
||||
foreach ($this->list_cohortsyncs($courseid) as $instance) {
|
||||
// Only check the records that have studyplan information in the customtext4 field.
|
||||
// First check if the cohort is not one of the cohort id's we have associated.
|
||||
if (!in_array($instance->customint1, $this->cohortids)) {
|
||||
|
|
|
@ -78,7 +78,7 @@ class cascadeusersync {
|
|||
// And find the users that are linked to this studyplan.
|
||||
$userids = $this->studyplan->get_linked_user_ids();
|
||||
|
||||
foreach($lines as $line) {
|
||||
foreach ($lines as $line) {
|
||||
$this->syncline($line);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ class corecompletioninfo {
|
|||
$info = [
|
||||
"conditions" => $conditions,
|
||||
"aggregation" => self::aggregation_handle($this->completion->get_aggregation_method()),
|
||||
"enabled" => $this->completion->is_enabled()
|
||||
"enabled" => $this->completion->is_enabled(),
|
||||
];
|
||||
|
||||
// Check if completion tracking is enabled for this course - otherwise, revert to defaults .
|
||||
|
@ -261,7 +261,7 @@ class corecompletioninfo {
|
|||
// Loop through all condition types to see if they are applicable.
|
||||
foreach (self::completiontypes() as $type => $handle) {
|
||||
$criterias = $this->completion->get_criteria($type); // Returns array of relevant criteria items.
|
||||
if (count($criterias) > 0 ) {
|
||||
if (count($criterias) > 0) {
|
||||
// Only take it into account if the criteria count is > 0.
|
||||
$cinfo = [
|
||||
"type" => $handle,
|
||||
|
@ -314,7 +314,7 @@ class corecompletioninfo {
|
|||
} else {
|
||||
$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();
|
||||
// Build requirements.
|
||||
$details['requirement'] = [];
|
||||
|
@ -354,7 +354,6 @@ class corecompletioninfo {
|
|||
$details = [
|
||||
"type" => get_string('coursegrade', 'completion'),
|
||||
"criteria" => get_string('graderequired', 'completion'),
|
||||
// TODO: convert to selected representation (letter, percentage, etc).
|
||||
"requirement" => get_string('graderequired', 'completion')
|
||||
.": ".$displaygrade,
|
||||
"status" => "",
|
||||
|
@ -496,14 +495,14 @@ class corecompletioninfo {
|
|||
and we want to show similar behaviour. This happens when completion data is reset
|
||||
in a module
|
||||
*/
|
||||
if ( !$completion->is_complete()
|
||||
if (!$completion->is_complete()
|
||||
&&
|
||||
in_array($gradecompletion, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS])) {
|
||||
/* If a passing grade was provided, but the activity was not completed,
|
||||
* most likely the completion data was erased.
|
||||
*/
|
||||
|
||||
if ( !is_null($cm->completiongradeitemnumber) || ($cm->completionpassgrade) ) {
|
||||
if (!is_null($cm->completiongradeitemnumber) || ($cm->completionpassgrade)) {
|
||||
// Show a warning if this activity has grade completions to help make sense of the completion.
|
||||
$iinfo["warning"] = get_string("warning_incomplete_pass", "local_treestudyplan");
|
||||
} else {
|
||||
|
@ -589,8 +588,6 @@ class corecompletioninfo {
|
|||
* @return stdClass|null object containing 'grade' and optional 'feedback' attribute
|
||||
*/
|
||||
private function get_grade($cm, $userid) {
|
||||
// TODO: Display grade in the way described in the course setup (with letters if needed).
|
||||
|
||||
$gi = grade_item::fetch(['itemtype' => 'mod',
|
||||
'itemmodule' => $cm->modname,
|
||||
'iteminstance' => $cm->instance,
|
||||
|
@ -624,7 +621,6 @@ class corecompletioninfo {
|
|||
* @return stdClass|null object containing 'grade' and optional 'feedback' attribute
|
||||
*/
|
||||
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',
|
||||
'courseid' => $this->course->id]);
|
||||
|
||||
|
@ -740,7 +736,7 @@ class corecompletioninfo {
|
|||
$result = new \stdClass;
|
||||
$result->count = $count;
|
||||
$result->completed = $completed;
|
||||
$result->percentage = ($count > 0) ? (($completed / $count) * 100): 0;
|
||||
$result->percentage = ($count > 0) ? (($completed / $count) * 100) : 0;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,10 @@ class coursecompetencyinfo {
|
|||
], "details about gradable submissions", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert completion stats to web service model.
|
||||
* @param object $stats Stats object
|
||||
*/
|
||||
protected static function completionstats($stats) {
|
||||
return [
|
||||
"students" => $stats->count,
|
||||
|
@ -113,7 +117,6 @@ class coursecompetencyinfo {
|
|||
"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),
|
||||
"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),
|
||||
"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),
|
||||
|
@ -121,7 +124,8 @@ class coursecompetencyinfo {
|
|||
"feedback" => new \external_value(PARAM_RAW, 'feedback provided with this competency', VALUE_OPTIONAL),
|
||||
];
|
||||
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');
|
||||
}
|
||||
|
@ -157,12 +161,12 @@ class coursecompetencyinfo {
|
|||
private function competencyinfo_model($competency, $userid=null): array {
|
||||
$displayfield = get_config("local_treestudyplan", "competency_displayname");
|
||||
$detailfield = get_config("local_treestudyplan", "competency_detailfield");
|
||||
$headingfield = ($displayfield != 'description')?$displayfield:"shortname";
|
||||
$headingfield = ($displayfield != 'description') ? $displayfield : "shortname";
|
||||
$framework = $competency->get_framework();
|
||||
|
||||
$heading = $framework->get($headingfield);
|
||||
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 = [[
|
||||
'id' => $framework->get('id'),
|
||||
|
@ -174,7 +178,7 @@ class coursecompetencyinfo {
|
|||
$competencypath[] = $c->get('shortname');
|
||||
$heading = $c->get($headingfield);
|
||||
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[] = [
|
||||
'id' => $c->get('id'),
|
||||
|
@ -186,7 +190,7 @@ class coursecompetencyinfo {
|
|||
|
||||
$heading = $competency->get($headingfield);
|
||||
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[] = [
|
||||
'id' => $competency->get('id'),
|
||||
|
@ -197,7 +201,7 @@ class coursecompetencyinfo {
|
|||
|
||||
$title = $competency->get($displayfield);
|
||||
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 = [
|
||||
'id' => $competency->get('id'),
|
||||
|
@ -226,7 +230,7 @@ class coursecompetencyinfo {
|
|||
$nproficient = 0;
|
||||
|
||||
$cis = [];
|
||||
foreach($coursecompetencies as $c) {
|
||||
foreach ($coursecompetencies as $c) {
|
||||
$ci = $this->competencyinfo_model($c);
|
||||
if (!empty($studentlist)) {
|
||||
$stats = $this->proficiency_stats($c, $studentlist);
|
||||
|
@ -262,9 +266,9 @@ class coursecompetencyinfo {
|
|||
$ruleconfig = json_decode($ruleconfig);
|
||||
$points = $ruleconfig->base->points;
|
||||
|
||||
// Make a nice map of the competency rule config
|
||||
// Make a nice map of the competency rule config.
|
||||
$crlist = [];
|
||||
foreach($ruleconfig->competencies as $cr) {
|
||||
foreach ($ruleconfig->competencies as $cr) {
|
||||
$crlist[$cr->id] = $cr;
|
||||
}
|
||||
$ci["rule"] = $ruletext . " ({$points} ".get_string("points", "core_grades").")";
|
||||
|
@ -272,11 +276,11 @@ class coursecompetencyinfo {
|
|||
$ci["rule"] = $ruletext;
|
||||
}
|
||||
|
||||
// get one level of children
|
||||
// Get one level of children.
|
||||
$dids = competency::get_descendants_ids($c);
|
||||
if (count($dids) > 0) {
|
||||
$children = [];
|
||||
foreach($dids as $did) {
|
||||
foreach ($dids as $did) {
|
||||
$cc = new competency($did);
|
||||
$cci = $this->competencyinfo_model($cc);
|
||||
if (!empty($studentlist)) {
|
||||
|
@ -355,21 +359,21 @@ class coursecompetencyinfo {
|
|||
$ruleconfig = json_decode($ruleconfig);
|
||||
$pointsreq = $ruleconfig->base->points;
|
||||
$points = 0;
|
||||
// Make a nice map of the competency rule config
|
||||
// Make a nice map of the competency rule config.
|
||||
$crlist = [];
|
||||
foreach($ruleconfig->competencies as $cr) {
|
||||
foreach ($ruleconfig->competencies as $cr) {
|
||||
$crlist[$cr->id] = $cr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// get one level of children
|
||||
// Get one level of children.
|
||||
$dids = competency::get_descendants_ids($c);
|
||||
if (count($dids) > 0) {
|
||||
$dcount = 0;
|
||||
$dprogress = 0;
|
||||
$children = [];
|
||||
foreach($dids as $did) {
|
||||
foreach ($dids as $did) {
|
||||
$cc = new competency($did);
|
||||
$cci = $this->competencyinfo_model($cc, $userid);
|
||||
$cp = $p = $this->proficiency($cc, $userid);
|
||||
|
@ -442,6 +446,11 @@ class coursecompetencyinfo {
|
|||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine proficiency stats
|
||||
* @param object $competency
|
||||
* @param array $studentlist
|
||||
*/
|
||||
protected function proficiency_stats($competency, $studentlist) {
|
||||
$r = new \stdClass();
|
||||
$r->count = 0;
|
||||
|
@ -453,8 +462,8 @@ class coursecompetencyinfo {
|
|||
foreach ($studentlist as $sid) {
|
||||
$p = $this->proficiency($competency, $sid);
|
||||
$r->count += 1;
|
||||
$r->nproficient += ($p->proficient === true)?1:0;
|
||||
$r->nfailed += ($p->proficient === false)?1:0;
|
||||
$r->nproficient += ($p->proficient === true) ? 1 : 0;
|
||||
$r->nfailed += ($p->proficient === false) ? 1 : 0;
|
||||
$r->ncourseproficient += ($p->courseproficient) ? 1 : 0;
|
||||
$r->nneedreview += ($p->needreview) ? 1 : 0;
|
||||
}
|
||||
|
@ -535,7 +544,9 @@ class coursecompetencyinfo {
|
|||
$ucc = self::get_user_competency_in_course($this->course->id, $userid, $competencyid);
|
||||
$r->courseproficient = $ucc->get('proficiency');
|
||||
$r->coursegrade = $scale->get_nearest_item($ucc->get('grade'));
|
||||
} catch (\Exception $x) {}
|
||||
} catch (\Exception $x) {
|
||||
$ucc = null;
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
@ -552,12 +563,12 @@ class coursecompetencyinfo {
|
|||
$competencyid = $competency->get('id');
|
||||
$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");
|
||||
|
||||
// Get the first valid note and return it;
|
||||
foreach($evidence as $e) {
|
||||
if ( in_array($e->get('action'), [evidence::ACTION_OVERRIDE, evidence::ACTION_COMPLETE] )) {
|
||||
// Get the first valid note and return it.
|
||||
foreach ($evidence as $e) {
|
||||
if (in_array($e->get('action'), [evidence::ACTION_OVERRIDE, evidence::ACTION_COMPLETE])) {
|
||||
return $e->get('note');
|
||||
break;
|
||||
}
|
||||
|
@ -575,13 +586,15 @@ class coursecompetencyinfo {
|
|||
public static function require_competency(int $competencyid, int $itemid, bool $required) {
|
||||
global $DB;
|
||||
$item = studyitem::find_by_id($itemid);
|
||||
// Make sure conditions are properly configured;
|
||||
// Make sure conditions are properly configured.
|
||||
$conditions = [];
|
||||
try {
|
||||
$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"])) {
|
||||
$conditions["competencies"] = [];
|
||||
}
|
||||
|
@ -595,7 +608,7 @@ class coursecompetencyinfo {
|
|||
$conditions["competencies"][$competencyid]["required"] = boolval($required);
|
||||
}
|
||||
|
||||
// Store conditions;
|
||||
// Store conditions.
|
||||
$item->edit(["conditions" => json_encode($conditions)]);
|
||||
return success::success();
|
||||
}
|
||||
|
@ -609,10 +622,12 @@ class coursecompetencyinfo {
|
|||
$conditions = [];
|
||||
try {
|
||||
$conditions = json_decode($this->studyitem->conditions(), true);
|
||||
} catch (\Exception $x) {}
|
||||
} catch (\Exception $x) {
|
||||
$conditions = [];
|
||||
}
|
||||
|
||||
// Make sure the competencied field exists
|
||||
if ( isset($conditions["competencies"])
|
||||
// Make sure the competencied field exists.
|
||||
if (isset($conditions["competencies"])
|
||||
&& is_array($conditions["competencies"])
|
||||
&& isset($conditions["competencies"][$competency->get("id")])
|
||||
&& isset($conditions["competencies"][$competency->get("id")]["required"])
|
||||
|
|
|
@ -267,7 +267,7 @@ class courseinfo {
|
|||
'fullname' => $this->course->fullname,
|
||||
'shortname' => $this->course->shortname,
|
||||
'displayname' => $this->displayname(),
|
||||
'context' => $contextinfo->model()
|
||||
'context' => $contextinfo->model(),
|
||||
];
|
||||
|
||||
return $info;
|
||||
|
@ -502,7 +502,7 @@ class courseinfo {
|
|||
*/
|
||||
public function extrafields_model($includeteachervisible=false) {
|
||||
$list = [];
|
||||
for ($i=1; $i <= 5; $i++) {
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$field = get_config('local_treestudyplan', 'courseinfo'.$i.'_field');
|
||||
if ($field) {
|
||||
$title = self::extrafields_localize_title(get_config('local_treestudyplan', 'courseinfo'.$i.'_title'));
|
||||
|
@ -516,7 +516,7 @@ class courseinfo {
|
|||
"type" => $type,
|
||||
"fieldname" => $field,
|
||||
"courseid" => $this->course->id,
|
||||
"checked" => ($type=="checkbox") ? ($raw ? true : false):null,
|
||||
"checked" => ($type == "checkbox") ? ($raw ? true : false) : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -524,11 +524,15 @@ class courseinfo {
|
|||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize title for extra field
|
||||
* @param string $field
|
||||
*/
|
||||
protected static function extrafields_localize_title($field) {
|
||||
$lang = trim(current_language());
|
||||
$lines = explode("\n", $field);
|
||||
$title = "";
|
||||
$fallback = ""; // Fallback to first title
|
||||
$fallback = ""; // Fallback to first title.
|
||||
foreach ($lines as $l) {
|
||||
$parts = explode("|", $l, 2);
|
||||
if (count($parts) > 0) {
|
||||
|
@ -537,7 +541,7 @@ class courseinfo {
|
|||
$fallback = $parts[0];
|
||||
}
|
||||
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]);
|
||||
} else if (trim($parts[1]) == $lang) {
|
||||
return trim($parts[0]);
|
||||
|
@ -546,7 +550,7 @@ class courseinfo {
|
|||
}
|
||||
|
||||
// Return default title or fall back to first localizef title.
|
||||
return (strlen($title) > 0)?$title:$fallback;
|
||||
return (strlen($title) > 0) ? $title : $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -558,13 +562,13 @@ class courseinfo {
|
|||
if ($fieldname == "description") {
|
||||
// Process embedded files.
|
||||
$value = \file_rewrite_pluginfile_urls(
|
||||
// The description content
|
||||
// The description content.
|
||||
$this->course()->summary,
|
||||
// The pluginfile URL which will serve the request.
|
||||
'pluginfile.php',
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that file are stored in.
|
||||
$this->coursecontext->id, // System instance is always used for this
|
||||
// The combination of contextid / component / filearea / itemid.
|
||||
// Form the virtual bucket that file are stored in.
|
||||
$this->coursecontext->id, // System instance is always used for this.
|
||||
'course',
|
||||
'summary',
|
||||
''
|
||||
|
@ -600,7 +604,7 @@ class courseinfo {
|
|||
$raw = $data->get_value();
|
||||
$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 ($type == "date") {
|
||||
// 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.
|
||||
* 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
|
||||
* @return array of core_course_category
|
||||
*/
|
||||
|
@ -65,9 +66,9 @@ class courseservice extends \external_api {
|
|||
if (has_capability($capability, \context_system::instance(), $userid)) {
|
||||
if ($capability == 'moodle/category:viewcourselist') {
|
||||
// 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');
|
||||
foreach( $rs as $rcat) {
|
||||
foreach ($rs as $rcat) {
|
||||
// Get the category, and double check if the category is visible to the current user.
|
||||
// Just in case it is a hidden category and the user does not have the viewhidden permission.
|
||||
$cat = \core_course_category::get($rcat->id, \IGNORE_MISSING, false, $userid);
|
||||
|
@ -79,7 +80,7 @@ class courseservice extends \external_api {
|
|||
$rs->close();
|
||||
|
||||
} 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.
|
||||
// Recurses only once.
|
||||
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.
|
||||
// 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
|
||||
INNER JOIN {role_assignments} AS ra ON ra.contextid = ctx.id
|
||||
INNER JOIN {role_capabilities} AS rc ON ra.roleid = rc.roleid
|
||||
LEFT JOIN {course_categories} AS cat ON ctx.instanceid = cat.id
|
||||
$sql = "SELECT UNIQUE ctx.* FROM {context} ctx
|
||||
INNER JOIN {role_assignments} ra ON ra.contextid = ctx.id
|
||||
INNER JOIN {role_capabilities} rc ON ra.roleid = rc.roleid
|
||||
LEFT JOIN {course_categories} cat ON ctx.instanceid = cat.id
|
||||
WHERE ( ctx.contextlevel = :ctxl_coursecat )
|
||||
AND ra.userid = :userid AND rc.capability = :capability
|
||||
ORDER BY ctx.depth ASC, cat.sortorder ASC";
|
||||
|
@ -133,7 +134,6 @@ class courseservice extends \external_api {
|
|||
}
|
||||
}
|
||||
}
|
||||
//$recordset->close();
|
||||
}
|
||||
|
||||
return $tops;
|
||||
|
@ -152,10 +152,10 @@ class courseservice extends \external_api {
|
|||
$tops = [];
|
||||
$pathlike = $DB->sql_like('ctx.path', ':pathsearch');
|
||||
|
||||
$sql = "SELECT UNIQUE ctx.* FROM {context} AS ctx
|
||||
INNER JOIN {role_assignments} AS ra ON ra.contextid = ctx.id
|
||||
INNER JOIN {role_capabilities} AS rc ON ra.roleid = rc.roleid
|
||||
LEFT JOIN {course_categories} AS cat ON ctx.instanceid = cat.id
|
||||
$sql = "SELECT UNIQUE ctx.* FROM {context} ctx
|
||||
INNER JOIN {role_assignments} ra ON ra.contextid = ctx.id
|
||||
INNER JOIN {role_capabilities} rc ON ra.roleid = rc.roleid
|
||||
LEFT JOIN {course_categories} cat ON ctx.instanceid = cat.id
|
||||
WHERE ( ctx.contextlevel = :ctxl_coursecat )
|
||||
AND ra.userid = :userid AND rc.capability = :capability
|
||||
AND {$pathlike}
|
||||
|
@ -255,7 +255,7 @@ class courseservice extends \external_api {
|
|||
throw new moodle_exception("error:nocategoriesvisible", "local_treestudyplan");
|
||||
}
|
||||
} else {
|
||||
if (\get_config("local_treestudyplan", "limitcourselist")) { // TODO: Insert config setting here
|
||||
if (\get_config("local_treestudyplan", "limitcourselist")) {
|
||||
$studyplan = studyplan::find_by_id($studyplanid);
|
||||
$context = $studyplan->context();
|
||||
if ($context->contextlevel == \CONTEXT_SYSTEM) {
|
||||
|
@ -446,7 +446,7 @@ class courseservice extends \external_api {
|
|||
|
||||
$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)) {
|
||||
try {
|
||||
// Get the context.
|
||||
|
@ -455,8 +455,8 @@ class courseservice extends \external_api {
|
|||
if (has_capability($capability, $refctx)) {
|
||||
$insertctxs[] = $refctx;
|
||||
}
|
||||
} catch(\dml_missing_record_exception $x) {
|
||||
// ignore context
|
||||
} catch (\dml_missing_record_exception $x) {
|
||||
$refctx = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,10 +469,10 @@ class courseservice extends \external_api {
|
|||
if ($idx !== false) {
|
||||
|
||||
$contextids = array_merge(
|
||||
array_slice($contextids, 0, $idx+1),
|
||||
array_slice($contextids, 0, $idx + 1),
|
||||
array_reverse(array_slice($ipath, 0, $i)),
|
||||
array_slice($contextids, $idx+1, count($contextids) - 1)
|
||||
) ;
|
||||
array_slice($contextids, $idx + 1, count($contextids) - 1)
|
||||
);
|
||||
|
||||
$found = true;
|
||||
break;
|
||||
|
@ -484,7 +484,7 @@ class courseservice extends \external_api {
|
|||
}
|
||||
|
||||
// Now translate this to the list of categories.
|
||||
foreach ($contextids as $ctxid ) {
|
||||
foreach ($contextids as $ctxid) {
|
||||
try {
|
||||
$ctx = \context::instance_by_id($ctxid);
|
||||
if ($ctx->contextlevel == CONTEXT_SYSTEM) {
|
||||
|
@ -504,7 +504,7 @@ class courseservice extends \external_api {
|
|||
}
|
||||
}
|
||||
} catch (\dml_missing_record_exception $x) {
|
||||
// ignore context
|
||||
$ctx = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -539,7 +539,6 @@ class courseservice extends \external_api {
|
|||
return new \external_function_parameters( [
|
||||
"gradeitemid" => new \external_value(PARAM_INT, 'Grade item ID to scan progress for', VALUE_DEFAULT),
|
||||
"studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in', VALUE_DEFAULT),
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,16 +19,16 @@
|
|||
* @copyright 2023 P.M. Kuipers
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace local_treestudyplan;
|
||||
|
||||
use Exception;
|
||||
use JsonException;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Crude tool to aid in debugging
|
||||
*/
|
||||
class debug {
|
||||
/**
|
||||
* Dump an object in json format to debug log
|
||||
* @param $object Object to dump
|
||||
* @param $filename File to write to
|
||||
* @return any The object
|
||||
|
@ -42,9 +42,9 @@ class debug {
|
|||
fwrite($f, $json."\n");
|
||||
} catch (JsonException $x) {
|
||||
fwrite($f, "Error processing json: ". $x->getMessage()."\n");
|
||||
fwrite($f, "Print_r dump: \n".print_r($object, true)."\n");
|
||||
}
|
||||
} catch (\Exception $x) {
|
||||
$f = null;
|
||||
|
||||
} finally {
|
||||
fclose($f);
|
||||
|
@ -52,22 +52,10 @@ class debug {
|
|||
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 $filename File to write to
|
||||
* @return any The object
|
||||
|
@ -77,6 +65,21 @@ class debug {
|
|||
$f = fopen($filename, "a+");
|
||||
fwrite($f, $text."\n");
|
||||
} 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 {
|
||||
fclose($f);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
<?php
|
||||
|
||||
// This file is part of the Studyplan plugin for Moodle
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* 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;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->dirroot."/lib/formslib.php");
|
||||
|
||||
/**
|
||||
* Base class for moodle forms used in this plugin.
|
||||
*/
|
||||
abstract class formbase extends \moodleform {
|
||||
|
||||
/**
|
||||
|
@ -13,9 +36,9 @@ abstract class formbase extends \moodleform {
|
|||
*/
|
||||
public function __construct($params, $ajaxformdata=null) {
|
||||
$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);
|
||||
// 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));
|
||||
} else {
|
||||
throw new \moodle_exception('accessexception', 'core');
|
||||
|
@ -48,7 +71,9 @@ abstract class formbase extends \moodleform {
|
|||
* @return bool True if security validation passes.
|
||||
* @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
|
||||
|
@ -72,5 +97,4 @@ abstract class formbase extends \moodleform {
|
|||
throw new \moodle_exception('no_form_data', 'local_treestudyplan', '', null, $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,24 @@
|
|||
<?php
|
||||
// This file is part of the Studyplan plugin for Moodle
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* 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;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
@ -33,7 +53,7 @@ class studyplan_editform extends formbase {
|
|||
*/
|
||||
public static function init_customdata(object $params) {
|
||||
$customdata = new stdClass;
|
||||
$customdata->create = $params->mode=='create' ? true : false;
|
||||
$customdata->create = $params->mode == 'create' ? true : false;
|
||||
if ($customdata->create) {
|
||||
$customdata->context = \context::instance_by_id($params->contextid);
|
||||
} else {
|
||||
|
@ -47,32 +67,20 @@ class studyplan_editform extends formbase {
|
|||
'trusttext' => true,
|
||||
'subdirs' => true,
|
||||
'maxfiles' => 20,
|
||||
'maxbytes' => 20*1024*1024,
|
||||
'context' => \context_system::instance(), // Keep the files in system context
|
||||
'maxbytes' => 20 * 1024 * 1024,
|
||||
'context' => \context_system::instance(), // Keep the files in system context.
|
||||
];
|
||||
$customdata->fileoptions = [
|
||||
'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,
|
||||
'maxfiles' => 1, // Just one file
|
||||
'maxfiles' => 1, // Just one file.
|
||||
'accepted_types' => ['.jpg', '.png', '.jpeg', '.svg', '.svgz'],
|
||||
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
|
||||
];
|
||||
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
|
||||
* Also validate parameters and access permissions here
|
||||
|
@ -98,7 +106,7 @@ class studyplan_editform extends formbase {
|
|||
$august = strtotime("first day of august next year");
|
||||
}
|
||||
$entry->startdate = $august;
|
||||
$entry->enddate = $august + (364*24*60*60); // Not bothering about leap years here.
|
||||
$entry->enddate = $august + (364 * 24 * 60 * 60); // Not bothering about leap years here.
|
||||
$entry->periods = 4;
|
||||
} else {
|
||||
$entry = $DB->get_record(studyplan::TABLE, ['id' => $customdata->plan->id()]);
|
||||
|
@ -109,7 +117,7 @@ class studyplan_editform extends formbase {
|
|||
$agcfg = json_decode($customdata->plan->aggregator()->config_string(), true);
|
||||
}
|
||||
|
||||
// Prepare the editor
|
||||
// Prepare the editor.
|
||||
$entry = file_prepare_standard_editor( $entry,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
|
@ -119,15 +127,15 @@ class studyplan_editform extends formbase {
|
|||
($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.
|
||||
$draftitemid = file_get_submitted_draft_itemid('icon');
|
||||
file_prepare_draft_area(
|
||||
// The $draftitemid is the target location.
|
||||
$draftitemid,
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that files are currently stored in
|
||||
// and will be copied from.
|
||||
// The combination of contextid / component / filearea / itemid.
|
||||
// Form the virtual bucket that files are currently stored in.
|
||||
// And will be copied from.
|
||||
\context_system::instance()->id,
|
||||
'local_treestudyplan',
|
||||
'icon',
|
||||
|
@ -154,10 +162,10 @@ class studyplan_editform extends formbase {
|
|||
$mform = $this->_form;
|
||||
$customdata = (object)$this->_customdata;
|
||||
|
||||
// Register integer type
|
||||
text_integer::Register();
|
||||
// Register integer type.
|
||||
text_integer::register();
|
||||
|
||||
// Define the form
|
||||
// Define the form.
|
||||
$field = 'name';
|
||||
$mform->addElement('text', $field,
|
||||
get_string('studyplan_name', 'local_treestudyplan'),
|
||||
|
@ -176,7 +184,7 @@ class studyplan_editform extends formbase {
|
|||
[]);
|
||||
|
||||
$contextlist = [];
|
||||
foreach(courseservice::list_available_categories('edit') as $c) {
|
||||
foreach (courseservice::list_available_categories('edit') as $c) {
|
||||
$contextlist[$c['context_id']] = implode(" / ", $c['category']['path']);
|
||||
}
|
||||
|
||||
|
@ -196,8 +204,8 @@ class studyplan_editform extends formbase {
|
|||
|
||||
if ($customdata->create) {
|
||||
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
||||
if ( !$timeless) {
|
||||
// Only add these fields if a new studyplan is created, to easily initialize the first page
|
||||
if (!$timeless) {
|
||||
// Only add these fields if a new studyplan is created, to easily initialize the first page.
|
||||
$field = 'startdate';
|
||||
$mform->addElement('date_selector', $field,
|
||||
get_string('studyplan_startdate', 'local_treestudyplan'),
|
||||
|
@ -219,7 +227,7 @@ class studyplan_editform extends formbase {
|
|||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
} else {
|
||||
// These fields are only relevant if the studyplan is edited
|
||||
// These fields are only relevant if the studyplan is edited.
|
||||
$field = 'suspended';
|
||||
$mform->addElement('advcheckbox', $field,
|
||||
get_string('studyplan_suspend', 'local_treestudyplan'),
|
||||
|
@ -238,9 +246,9 @@ class studyplan_editform extends formbase {
|
|||
}
|
||||
|
||||
$aggregators = [];
|
||||
foreach(aggregator::list_model() as $a) {
|
||||
// Add method only if not deprecated or currently used
|
||||
if ( $customdata->simplemodel['aggregation'] == $a['id'] || !($a['deprecated']) ) {
|
||||
foreach (aggregator::list_model() as $a) {
|
||||
// Add method only if not deprecated or currently used.
|
||||
if ($customdata->simplemodel['aggregation'] == $a['id'] || !($a['deprecated'])) {
|
||||
$aggregators[$a['id']] = $a['name'];
|
||||
}
|
||||
}
|
||||
|
@ -307,7 +315,8 @@ class studyplan_editform extends formbase {
|
|||
$customdata = (object)$this->_customdata;
|
||||
|
||||
// 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) {
|
||||
$entrykey = $entry->aggregation."_".$key;
|
||||
$agcfg[$key] = $entry->$entrykey;
|
||||
|
@ -316,7 +325,7 @@ class studyplan_editform extends formbase {
|
|||
|
||||
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,
|
||||
'shortname' => $entry->shortname,
|
||||
'idnumber' => $entry->idnumber,
|
||||
|
@ -327,7 +336,7 @@ class studyplan_editform extends formbase {
|
|||
'enddate' => date("Y-m-d", $entry->enddate),
|
||||
'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,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
|
@ -335,7 +344,7 @@ class studyplan_editform extends formbase {
|
|||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$plan->id());
|
||||
// Update the description
|
||||
// Update the description.
|
||||
$plan->edit([
|
||||
'description' => $entry->description,
|
||||
'descriptionformat' => $entry->descriptionformat,
|
||||
|
@ -343,7 +352,7 @@ class studyplan_editform extends formbase {
|
|||
} else {
|
||||
$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,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
|
@ -352,7 +361,7 @@ class studyplan_editform extends formbase {
|
|||
'studyplan',
|
||||
$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,
|
||||
'shortname' => $entry->shortname,
|
||||
'idnumber' => $entry->idnumber,
|
||||
|
@ -370,8 +379,8 @@ class studyplan_editform extends formbase {
|
|||
file_save_draft_area_files(
|
||||
// The $entry->icon property contains the itemid of the draft file area.
|
||||
$entry->icon,
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that file are stored in.
|
||||
// The combination of contextid / component / filearea / itemid.
|
||||
// Form the virtual bucket that file are stored in.
|
||||
\context_system::instance()->id,
|
||||
'local_treestudyplan',
|
||||
'icon',
|
||||
|
@ -387,5 +396,4 @@ class studyplan_editform extends formbase {
|
|||
debug::dump($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +1,30 @@
|
|||
<?php
|
||||
// This file is part of the Studyplan plugin for Moodle
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* 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;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/repository/lib.php');
|
||||
use local_treestudyplan\aggregator;
|
||||
use local_treestudyplan\studyplan;
|
||||
use local_treestudyplan\premium;
|
||||
use local_treestudyplan\contextinfo;
|
||||
use local_treestudyplan\studyplanservice;
|
||||
use local_treestudyplan\courseservice;
|
||||
|
@ -38,18 +56,6 @@ class studyplan_fromtemplateform extends formbase {
|
|||
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
|
||||
* Also validate parameters and access permissions here
|
||||
|
@ -72,7 +78,7 @@ class studyplan_fromtemplateform extends formbase {
|
|||
$august = strtotime("first day of august next year");
|
||||
}
|
||||
$entry->startdate = $august;
|
||||
$entry->enddate = $august + (364*24*60*60); // Not bothering about leap years here.
|
||||
$entry->enddate = $august + (364 * 24 * 60 * 60); // Not bothering about leap years here.
|
||||
$entry->periods = 4;
|
||||
$entry->template_id = 0;
|
||||
|
||||
|
@ -87,13 +93,13 @@ class studyplan_fromtemplateform extends formbase {
|
|||
$mform = $this->_form;
|
||||
$customdata = (object)$this->_customdata;
|
||||
|
||||
// Register integer type
|
||||
text_integer::Register();
|
||||
// Register integer type.
|
||||
text_integer::register();
|
||||
|
||||
// Define the form
|
||||
// Define the form.
|
||||
|
||||
$templatelist = [];
|
||||
foreach(studyplan::find_template() as $s) {
|
||||
foreach (studyplan::find_template() as $s) {
|
||||
$c = (new contextinfo($s->context()))->model();
|
||||
$templatelist[$s->id()] = implode(" / ", $c['path']) . " / " . $s->name();
|
||||
}
|
||||
|
@ -125,7 +131,7 @@ class studyplan_fromtemplateform extends formbase {
|
|||
[]);
|
||||
|
||||
$contextlist = [];
|
||||
foreach(courseservice::list_available_categories('edit') as $c) {
|
||||
foreach (courseservice::list_available_categories('edit') as $c) {
|
||||
$contextlist[$c['context_id']] = implode(" / ", $c['category']['path']);
|
||||
}
|
||||
|
||||
|
@ -136,8 +142,8 @@ class studyplan_fromtemplateform extends formbase {
|
|||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
||||
if ( !$timeless) {
|
||||
// Only add these fields if the studyplans are timed
|
||||
if (!$timeless) {
|
||||
// Only add these fields if the studyplans are timed.
|
||||
$field = 'startdate';
|
||||
$mform->addElement('date_selector', $field,
|
||||
get_string('studyplan_startdate', 'local_treestudyplan'),
|
||||
|
@ -180,5 +186,4 @@ class studyplan_fromtemplateform extends formbase {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +1,32 @@
|
|||
<?php
|
||||
// This file is part of the Studyplan plugin for Moodle
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* 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;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/repository/lib.php');
|
||||
use local_treestudyplan\aggregator;
|
||||
use local_treestudyplan\studyplan;
|
||||
use local_treestudyplan\studyplanpage;
|
||||
use local_treestudyplan\courseservice;
|
||||
use local_treestudyplan\form\text_integer;
|
||||
use local_treestudyplan\local\helpers\webservicehelper;
|
||||
use local_treestudyplan\studyplanservice;
|
||||
use moodle_exception;
|
||||
use stdClass;
|
||||
|
@ -33,7 +50,7 @@ class studyplanpage_editform extends formbase {
|
|||
*/
|
||||
public static function init_customdata(object $params) {
|
||||
$customdata = new stdClass;
|
||||
$customdata->create = $params->mode=='create' ? true : false;
|
||||
$customdata->create = $params->mode == 'create' ? true : false;
|
||||
if ($customdata->create) {
|
||||
$customdata->plan = studyplan::find_by_id($params->studyplan_id);
|
||||
} else {
|
||||
|
@ -47,32 +64,20 @@ class studyplanpage_editform extends formbase {
|
|||
'trusttext' => true,
|
||||
'subdirs' => true,
|
||||
'maxfiles' => 20,
|
||||
'maxbytes' => 20*1024*1024,
|
||||
'context' => \context_system::instance(), // Keep the files in system context
|
||||
'maxbytes' => 20 * 1024 * 1024,
|
||||
'context' => \context_system::instance(), // Keep the files in system context.
|
||||
];
|
||||
$customdata->fileoptions = [
|
||||
'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,
|
||||
'maxfiles' => 1, // Just one file
|
||||
'maxfiles' => 1, // Just one file.
|
||||
'accepted_types' => ['.jpg', '.png'],
|
||||
'return_types' => \FILE_INTERNAL | \FILE_EXTERNAL,
|
||||
];
|
||||
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
|
||||
* Also validate parameters and access permissions here
|
||||
|
@ -91,21 +96,21 @@ class studyplanpage_editform extends formbase {
|
|||
$entry = new stdClass;
|
||||
$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();
|
||||
if (count($otherpages) > 0) {
|
||||
$lastpage = $otherpages[count($otherpages) -1];
|
||||
$lastpage = $otherpages[count($otherpages) - 1];
|
||||
|
||||
// Propose 1 year after the last page's start date, if no end date is set.
|
||||
if ($lastpage->enddate(false) == null) {
|
||||
$entry->startdate = $lastpage->startdate()->add(new \DateInterval("P1Y"))->format("U");
|
||||
} 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");
|
||||
}
|
||||
|
||||
} 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");
|
||||
if ($august < time()) {
|
||||
$august = strtotime("first day of august next year");
|
||||
|
@ -114,7 +119,7 @@ class studyplanpage_editform extends formbase {
|
|||
$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;
|
||||
} else {
|
||||
$entry = $DB->get_record(studyplanpage::TABLE, ['id' => $customdata->page->id()]);
|
||||
|
@ -122,7 +127,7 @@ class studyplanpage_editform extends formbase {
|
|||
$entry->enddate = strtotime($entry->enddate);
|
||||
}
|
||||
|
||||
// Prepare the editor
|
||||
// Prepare the editor.
|
||||
$entry = file_prepare_standard_editor( $entry,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
|
@ -143,10 +148,10 @@ class studyplanpage_editform extends formbase {
|
|||
$mform = $this->_form;
|
||||
$customdata = (object)$this->_customdata;
|
||||
|
||||
// Register integer type
|
||||
text_integer::Register();
|
||||
// Register integer type.
|
||||
text_integer::register();
|
||||
|
||||
// Define the form
|
||||
// Define the form.
|
||||
$field = 'fullname';
|
||||
$mform->addElement('text', $field,
|
||||
get_string('studyplan_name', 'local_treestudyplan'),
|
||||
|
@ -160,7 +165,7 @@ class studyplanpage_editform extends formbase {
|
|||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$timeless = \get_config("local_treestudyplan", "timelessperiods");
|
||||
if ( !$timeless) {
|
||||
if (!$timeless) {
|
||||
$field = 'startdate';
|
||||
$mform->addElement('date_selector', $field,
|
||||
get_string('studyplan_startdate', 'local_treestudyplan'),
|
||||
|
@ -201,7 +206,7 @@ class studyplanpage_editform extends formbase {
|
|||
|
||||
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,
|
||||
'shortname' => $entry->shortname,
|
||||
'startdate' => date("Y-m-d", $entry->startdate),
|
||||
|
@ -209,7 +214,7 @@ class studyplanpage_editform extends formbase {
|
|||
'periods' => $entry->periods,
|
||||
'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,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
|
@ -217,7 +222,7 @@ class studyplanpage_editform extends formbase {
|
|||
'local_treestudyplan',
|
||||
'studyplanpage',
|
||||
$page->id());
|
||||
// Update the description
|
||||
// Update the description.
|
||||
$page->edit([
|
||||
'description' => $entry->description,
|
||||
'descriptionformat' => $entry->descriptionformat,
|
||||
|
@ -225,7 +230,7 @@ class studyplanpage_editform extends formbase {
|
|||
} else {
|
||||
$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,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
|
@ -234,7 +239,7 @@ class studyplanpage_editform extends formbase {
|
|||
'studyplanpage',
|
||||
$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,
|
||||
'shortname' => $entry->shortname,
|
||||
'description' => $entry->description,
|
||||
|
@ -251,5 +256,4 @@ class studyplanpage_editform extends formbase {
|
|||
*/
|
||||
return studyplanservice::clean_returnvalue(studyplanpage::editor_structure(), $page->editor_model());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,25 @@
|
|||
<?php
|
||||
// This file is part of the Studyplan plugin for Moodle
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* 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;
|
||||
use MoodleQuickForm_text;
|
||||
use MoodleQuickForm;
|
||||
|
@ -7,8 +28,11 @@ defined('MOODLE_INTERNAL') || die();
|
|||
global $CFG;
|
||||
require_once($CFG->libdir . "/form/text.php");
|
||||
|
||||
class text_integer extends MoodleQuickForm_text {
|
||||
/**
|
||||
* Class to handle integer only form field.
|
||||
*/
|
||||
class text_integer extends MoodleQuickForm_text {
|
||||
/**
|
||||
* Accepts a renderer
|
||||
*
|
||||
* @param object $renderer An HTML_QuickForm_Renderer object
|
||||
|
@ -32,7 +56,7 @@ class text_integer extends MoodleQuickForm_text {
|
|||
|
||||
$unsigned = (isset($this->_attributes['unsigned']) && $this->_attributes['unsigned']);
|
||||
$nonzero = (isset($this->_attributes['nonzero']) && $this->_attributes['nonzero']);
|
||||
$context = array(
|
||||
$context = [
|
||||
'element' => $elementcontext,
|
||||
'label' => $label,
|
||||
'unsigned' => ($unsigned) ? true : false ,
|
||||
|
@ -41,7 +65,7 @@ class text_integer extends MoodleQuickForm_text {
|
|||
'advanced' => $advanced,
|
||||
'helpbutton' => $helpbutton,
|
||||
'error' => $error,
|
||||
);
|
||||
];
|
||||
$html = $OUTPUT->render_from_template('local_treestudyplan/form/element_text_integer', $context);
|
||||
if ($renderer->_inGroup) {
|
||||
$this->_groupElementTemplate = $html;
|
||||
|
@ -59,7 +83,10 @@ class text_integer extends MoodleQuickForm_text {
|
|||
$renderer->_html .= $html;
|
||||
}
|
||||
|
||||
public static function Register() {
|
||||
/**
|
||||
* Register the element
|
||||
*/
|
||||
public static function register() {
|
||||
global $CFG;
|
||||
MoodleQuickForm::registerElementType(
|
||||
// The custom element is named `course_competency_rule`.
|
||||
|
|
|
@ -337,7 +337,7 @@ class gradeinfo {
|
|||
public function user_model($userid) {
|
||||
global $DB;
|
||||
$grade = $this->gradeitem->get_final($userid);
|
||||
// Format grade for proper display
|
||||
// Format grade for proper display.
|
||||
if (is_object($grade)) {
|
||||
$finalgrade = \grade_format_gradevalue($grade->finalgrade, $this->gradeitem, true, null, 1);
|
||||
} else {
|
||||
|
|
|
@ -148,7 +148,7 @@ class gradingscanner {
|
|||
$ungraded++;
|
||||
} else {
|
||||
$grade = $this->gi->get_final($userid);
|
||||
if ( (!empty($grade->finalgrade)) && is_numeric($grade->finalgrade)) {
|
||||
if ((!empty($grade->finalgrade)) && is_numeric($grade->finalgrade)) {
|
||||
// Compare grade to minimum grade.
|
||||
if ($this->grade_passed($grade)) {
|
||||
$completedpass++;
|
||||
|
@ -180,7 +180,7 @@ class gradingscanner {
|
|||
// First determine if we have a grade_config for this scale or this maximum grade.
|
||||
$finalgrade = $grade->finalgrade;
|
||||
$scale = $this->gi->load_scale();
|
||||
if ( isset($scale)) {
|
||||
if (isset($scale)) {
|
||||
$gradecfg = $DB->get_record($table, ["scale_id" => $scale->id]);
|
||||
} else if ($this->gi->grademin == 0) {
|
||||
$gradecfg = $DB->get_record($table, ["grade_points" => $this->gi->grademax]);
|
||||
|
|
|
@ -173,7 +173,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
|||
$totalrequired = 0;
|
||||
$requiredmet = 0;
|
||||
|
||||
$minprogress = ($this->cfg()->accept_pending_as_submitted) ? completion : :PENDING : completion::PROGRESS;
|
||||
$minprogress = ($this->cfg()->accept_pending_as_submitted) ? completion::PENDING : completion::PROGRESS;
|
||||
|
||||
foreach ($completions as $index => $c) {
|
||||
|
||||
|
@ -193,10 +193,10 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
|||
$started = $progress + $failed;
|
||||
$allrequiredmet = ($requiredmet >= $totalrequired);
|
||||
|
||||
$fractioncompleted = ($total > 0) ? (floatval($completed) / floatval($total)): 0.0;
|
||||
$fractionprogress = ($total > 0) ? (floatval($progress) / floatval($total)): 0.0;
|
||||
$fractionfailed = ($total > 0) ? (floatval($failed) / floatval($total)): 0.0;
|
||||
$fractionstarted = ($total > 0) ? (floatval($started) / floatval($total)): 0.0;
|
||||
$fractioncompleted = ($total > 0) ? (floatval($completed) / floatval($total)) : 0.0;
|
||||
$fractionprogress = ($total > 0) ? (floatval($progress) / floatval($total)) : 0.0;
|
||||
$fractionfailed = ($total > 0) ? (floatval($failed) / floatval($total)) : 0.0;
|
||||
$fractionstarted = ($total > 0) ? (floatval($started) / floatval($total)) : 0.0;
|
||||
|
||||
if ($total == 0) {
|
||||
return completion::INCOMPLETE;
|
||||
|
@ -230,7 +230,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
|||
*/
|
||||
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
|
||||
$course = $courseinfo->course();
|
||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()): false;
|
||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
|
||||
// Note: studyitem condition config is not used in this aggregator.
|
||||
// Loop through all associated gradables and count the totals, completed, etc..
|
||||
$completions = [];
|
||||
|
@ -278,32 +278,32 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
|||
$total = count($completion);
|
||||
|
||||
if ($method == "ANY") {
|
||||
if ( $statecount[completion::EXCELLENT] >= 1 ) {
|
||||
if ($statecount[completion::EXCELLENT] >= 1) {
|
||||
return completion::EXCELLENT;
|
||||
} else if ( $statecount[completion::GOOD] >= 1 ) {
|
||||
} else if ($statecount[completion::GOOD] >= 1) {
|
||||
return completion::GOOD;
|
||||
} else if ( $statecount[completion::COMPLETED] >= 1 ) {
|
||||
} else if ($statecount[completion::COMPLETED] >= 1) {
|
||||
return completion::COMPLETED;
|
||||
} else if ( $statecount[completion::PROGRESS] >= 1 ) {
|
||||
} else if ($statecount[completion::PROGRESS] >= 1) {
|
||||
return completion::PROGRESS;
|
||||
} else if ( $statecount[completion::FAILED] >= 1) {
|
||||
} else if ($statecount[completion::FAILED] >= 1) {
|
||||
return completion::FAILED;
|
||||
} else {
|
||||
return completion::INCOMPLETE;
|
||||
}
|
||||
} else { /* default value of ALL */
|
||||
if ( $total == $statecount[completion::EXCELLENT]) {
|
||||
if ($total == $statecount[completion::EXCELLENT]) {
|
||||
return completion::EXCELLENT;
|
||||
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||
} else if ($total == ( $statecount[completion::EXCELLENT]
|
||||
+ $statecount[completion::GOOD]) ) {
|
||||
return completion::GOOD;
|
||||
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||
} else if ($total == ( $statecount[completion::EXCELLENT]
|
||||
+ $statecount[completion::GOOD]
|
||||
+ $statecount[completion::COMPLETED]) ) {
|
||||
return completion::COMPLETED;
|
||||
} else if ( $statecount[completion::FAILED]) {
|
||||
} else if ($statecount[completion::FAILED]) {
|
||||
return completion::FAILED;
|
||||
} else if ( $total == $statecount[completion::INCOMPLETE]) {
|
||||
} else if ($total == $statecount[completion::INCOMPLETE]) {
|
||||
return completion::INCOMPLETE;
|
||||
} else {
|
||||
return completion::PROGRESS;
|
||||
|
@ -323,7 +323,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
|||
$gradeitem = $gradeinfo->get_gradeitem();
|
||||
$grade = $gradeitem->get_final($userid);
|
||||
$course = \get_course($gradeitem->courseid); // Fetch course from cache.
|
||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()): false;
|
||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
|
||||
|
||||
if (empty($grade)) {
|
||||
return completion::INCOMPLETE;
|
||||
|
@ -344,7 +344,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
|||
// First determine if we have a grade_config for this scale or this maximum grade.
|
||||
$finalgrade = $grade->finalgrade;
|
||||
$scale = $gradeinfo->get_scale();
|
||||
if ( isset($scale)) {
|
||||
if (isset($scale)) {
|
||||
$gradecfg = $DB->get_record($table, ["scale_id" => $scale->id]);
|
||||
} else if ($gradeitem->grademin == 0) {
|
||||
$gradecfg = $DB->get_record($table, ["grade_points" => $gradeitem->grademax]);
|
||||
|
|
|
@ -165,7 +165,7 @@ class competency_aggregator extends \local_treestudyplan\aggregator {
|
|||
* @return int Aggregated completion as completion class constant
|
||||
*/
|
||||
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
|
||||
// Retrieve the course competencies
|
||||
// Retrieve the course competencies.
|
||||
$course = $courseinfo->course();
|
||||
$cci = new coursecompetencyinfo($course, $studyitem);
|
||||
|
||||
|
@ -196,9 +196,9 @@ class competency_aggregator extends \local_treestudyplan\aggregator {
|
|||
|
||||
}
|
||||
|
||||
// Determine minimum for
|
||||
// Determine minimum for.
|
||||
$limit = $this->cfg()->thresh_completed * $count;
|
||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()): false;
|
||||
$coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
|
||||
|
||||
if ($proficient >= $count && $requiredmet >= $requiredcount) {
|
||||
if ($limit < $count) {
|
||||
|
@ -209,13 +209,13 @@ class competency_aggregator extends \local_treestudyplan\aggregator {
|
|||
} else if ($proficient > $limit && $requiredmet >= $requiredcount) {
|
||||
return completion::COMPLETED;
|
||||
} else if ($proficient > 0) {
|
||||
if ( $this->cfg()->use_failed && $coursefinished) {
|
||||
if ($this->cfg()->use_failed && $coursefinished) {
|
||||
return completion::FAILED;
|
||||
} else {
|
||||
return completion::PROGRESS;
|
||||
}
|
||||
} else {
|
||||
if ( $this->cfg()->use_failed && $coursefinished) {
|
||||
if ($this->cfg()->use_failed && $coursefinished) {
|
||||
return completion::FAILED;
|
||||
} else {
|
||||
return completion::INCOMPLETE;
|
||||
|
@ -247,32 +247,32 @@ class competency_aggregator extends \local_treestudyplan\aggregator {
|
|||
$total = count($completion);
|
||||
|
||||
if ($method == "ANY") {
|
||||
if ( $statecount[completion::EXCELLENT] >= 1 ) {
|
||||
if ($statecount[completion::EXCELLENT] >= 1) {
|
||||
return completion::EXCELLENT;
|
||||
} else if ( $statecount[completion::GOOD] >= 1 ) {
|
||||
} else if ($statecount[completion::GOOD] >= 1) {
|
||||
return completion::GOOD;
|
||||
} else if ( $statecount[completion::COMPLETED] >= 1 ) {
|
||||
} else if ($statecount[completion::COMPLETED] >= 1) {
|
||||
return completion::COMPLETED;
|
||||
} else if ( $statecount[completion::PROGRESS] >= 1 ) {
|
||||
} else if ($statecount[completion::PROGRESS] >= 1) {
|
||||
return completion::PROGRESS;
|
||||
} else if ( $statecount[completion::FAILED] >= 1) {
|
||||
} else if ($statecount[completion::FAILED] >= 1) {
|
||||
return completion::FAILED;
|
||||
} else {
|
||||
return completion::INCOMPLETE;
|
||||
}
|
||||
} else { /* default value of ALL */
|
||||
if ( $total == $statecount[completion::EXCELLENT]) {
|
||||
if ($total == $statecount[completion::EXCELLENT]) {
|
||||
return completion::EXCELLENT;
|
||||
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||
} else if ($total == ( $statecount[completion::EXCELLENT]
|
||||
+ $statecount[completion::GOOD]) ) {
|
||||
return completion::GOOD;
|
||||
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||
} else if ($total == ( $statecount[completion::EXCELLENT]
|
||||
+ $statecount[completion::GOOD]
|
||||
+ $statecount[completion::COMPLETED]) ) {
|
||||
return completion::COMPLETED;
|
||||
} else if ( $statecount[completion::FAILED]) {
|
||||
} else if ($statecount[completion::FAILED]) {
|
||||
return completion::FAILED;
|
||||
} else if ( $total == $statecount[completion::INCOMPLETE]) {
|
||||
} else if ($total == $statecount[completion::INCOMPLETE]) {
|
||||
return completion::INCOMPLETE;
|
||||
} else {
|
||||
return completion::PROGRESS;
|
||||
|
|
|
@ -121,7 +121,7 @@ class core_aggregator extends \local_treestudyplan\aggregator {
|
|||
|
||||
if ($completion->is_enabled() && $completion->is_tracked_user($userid)) {
|
||||
if ($completion->is_course_complete($userid)) {
|
||||
// Completed is completed
|
||||
// Completed is completed.
|
||||
return completion::COMPLETED;
|
||||
} else {
|
||||
// Check if the course is over or not, if it is over, display failed.
|
||||
|
@ -174,32 +174,32 @@ class core_aggregator extends \local_treestudyplan\aggregator {
|
|||
$total = count($completion);
|
||||
|
||||
if ($method == "ANY") {
|
||||
if ( $statecount[completion::EXCELLENT] >= 1 ) {
|
||||
if ($statecount[completion::EXCELLENT] >= 1) {
|
||||
return completion::EXCELLENT;
|
||||
} else if ( $statecount[completion::GOOD] >= 1 ) {
|
||||
} else if ($statecount[completion::GOOD] >= 1) {
|
||||
return completion::GOOD;
|
||||
} else if ( $statecount[completion::COMPLETED] >= 1 ) {
|
||||
} else if ($statecount[completion::COMPLETED] >= 1) {
|
||||
return completion::COMPLETED;
|
||||
} else if ( $statecount[completion::PROGRESS] >= 1 ) {
|
||||
} else if ($statecount[completion::PROGRESS] >= 1) {
|
||||
return completion::PROGRESS;
|
||||
} else if ( $statecount[completion::FAILED] >= 1) {
|
||||
} else if ($statecount[completion::FAILED] >= 1) {
|
||||
return completion::FAILED;
|
||||
} else {
|
||||
return completion::INCOMPLETE;
|
||||
}
|
||||
} else { /* default value of ALL */
|
||||
if ( $total == $statecount[completion::EXCELLENT]) {
|
||||
if ($total == $statecount[completion::EXCELLENT]) {
|
||||
return completion::EXCELLENT;
|
||||
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||
} else if ($total == ( $statecount[completion::EXCELLENT]
|
||||
+ $statecount[completion::GOOD]) ) {
|
||||
return completion::GOOD;
|
||||
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||
} else if ($total == ( $statecount[completion::EXCELLENT]
|
||||
+ $statecount[completion::GOOD]
|
||||
+ $statecount[completion::COMPLETED]) ) {
|
||||
return completion::COMPLETED;
|
||||
} else if ( $statecount[completion::FAILED]) {
|
||||
} else if ($statecount[completion::FAILED]) {
|
||||
return completion::FAILED;
|
||||
} else if ( $total == $statecount[completion::INCOMPLETE]) {
|
||||
} else if ($total == $statecount[completion::INCOMPLETE]) {
|
||||
return completion::INCOMPLETE;
|
||||
} else {
|
||||
return completion::PROGRESS;
|
||||
|
|
|
@ -93,7 +93,6 @@ class gradegenerator {
|
|||
"Praesent nec risus vestibulum quam venenatis tempor.",
|
||||
"Nullam rhoncus ex a quam egestas, eu auctor enim lobortis.",
|
||||
"Nam luctus ante id lacus scelerisque, quis blandit ante elementum.",
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -230,7 +229,7 @@ class gradegenerator {
|
|||
|
||||
// First get the configured interpretation for this scale or grade.
|
||||
$scale = $gi->load_scale();
|
||||
if ( isset($scale)) {
|
||||
if (isset($scale)) {
|
||||
$gradecfg = $DB->get_record($table, ["scale_id" => $scale->id]);
|
||||
} else if ($gi->grademin == 0) {
|
||||
$gradecfg = $DB->get_record($table, ["grade_points" => $gi->grademax]);
|
||||
|
@ -254,7 +253,7 @@ class gradegenerator {
|
|||
}
|
||||
|
||||
$r->gradetext = $r->grade;
|
||||
if ( isset($scale)) {
|
||||
if (isset($scale)) {
|
||||
$scaleitems = $scale->load_items();
|
||||
if ($r->grade > 0) {
|
||||
$r->gradetext = trim($scale->get_nearest_item($r->grade));
|
||||
|
@ -289,21 +288,21 @@ class gradegenerator {
|
|||
$r = (object)[
|
||||
"gi" => $g,
|
||||
"grade" => $gi->grademin + $grade,
|
||||
"fb" => ($grade > 0) ? $gr->fb : ""
|
||||
"fb" => ($grade > 0) ? $gr->fb : "",
|
||||
];
|
||||
} else if (!$gr->result) {
|
||||
// PROGRESS.
|
||||
$r = (object)[
|
||||
"gi" => $g,
|
||||
"grade" => $gi->grademin + rand(round($range * 0.35), round($range * 0.55) - 1 ),
|
||||
"fb" => $gr->fb
|
||||
"fb" => $gr->fb,
|
||||
];
|
||||
} else {
|
||||
// COMPLETED.
|
||||
$r = (object)[
|
||||
"gi" => $g,
|
||||
"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)) {
|
||||
// TODO: replace this by accesslib function \has_any_capability().
|
||||
foreach ($capability as $cap) {
|
||||
if (has_capability($cap, $context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (has_capability($capability, $context)) {
|
||||
return true;
|
||||
return \has_any_capability($capability, $context);
|
||||
} else {
|
||||
return has_capability($capability, $context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,19 +30,19 @@ class randomimage {
|
|||
|
||||
/**
|
||||
* Image width
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* Image height
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
private $height ;
|
||||
private $height;
|
||||
|
||||
/**
|
||||
* Number of shapes to draw
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
private $shapes;
|
||||
|
||||
|
@ -66,7 +66,7 @@ class randomimage {
|
|||
IMG_ARC_PIE,
|
||||
IMG_ARC_CHORD,
|
||||
IMG_ARC_EDGED,
|
||||
IMG_ARC_NOFILL
|
||||
IMG_ARC_NOFILL,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -75,11 +75,12 @@ class randomimage {
|
|||
* @param integer $maxpts Max number of point to use
|
||||
* @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());
|
||||
$pts = $this->random_pts(\random_int(3, $maxpts));
|
||||
imagefilledpolygon($im, $pts, $color);
|
||||
$numpoints = \random_int(3, $maxpts);
|
||||
$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
|
||||
* @return void
|
||||
*/
|
||||
private function random_arc($im)
|
||||
{
|
||||
private function random_arc($im) {
|
||||
$cx = \random_int(0, $this->width);
|
||||
$cy = \random_int(0, $this->height);
|
||||
$w = \random_int(0, $this->width);
|
||||
|
@ -104,13 +104,12 @@ class randomimage {
|
|||
* Generates an array of random alpha color values.
|
||||
* @return Array [r, g, b, a]
|
||||
*/
|
||||
private function random_color_alpha(): Array
|
||||
{
|
||||
private function random_color_alpha(): Array {
|
||||
return [
|
||||
\random_int(0, 255),
|
||||
\random_int(0, 255),
|
||||
\random_int(0, 255),
|
||||
\random_int(0, 127)
|
||||
\random_int(0, 127),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -119,16 +118,21 @@ class randomimage {
|
|||
* @param integer $length Number of sets of points to generate
|
||||
* @return Array The resulting array of points
|
||||
*/
|
||||
private function random_pts($length): Array
|
||||
{
|
||||
private function random_pts($length): Array {
|
||||
$pts = [];
|
||||
for($n = 0; $n < $length; $n++) {
|
||||
for ($n = 0; $n < $length; $n++) {
|
||||
$pts[] = \random_int(0, $this->width);
|
||||
$pts[] = \random_int(0, $this->height);
|
||||
}
|
||||
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) {
|
||||
$this->shapes = $shapes;
|
||||
$this->width = $width;
|
||||
|
@ -159,15 +163,20 @@ class randomimage {
|
|||
imagedestroy($im);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image content as string
|
||||
* @return string
|
||||
*/
|
||||
public function content() {
|
||||
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)) {
|
||||
unlink($this->tempfile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -117,7 +117,7 @@ class period {
|
|||
$pend = $pstart + $ptime;
|
||||
}
|
||||
|
||||
// Continue period numbers if so configured
|
||||
// Continue period numbers if so configured.
|
||||
if (get_config("local_treestudyplan", "continueperiodnumberingnewpage")) {
|
||||
$offset = 0;
|
||||
foreach (studyplanpage::find_studyplan_children($page->studyplan()) as $p) {
|
||||
|
|
|
@ -25,19 +25,22 @@ defined('MOODLE_INTERNAL') || die();
|
|||
|
||||
require_once($CFG->libdir.'/externallib.php');
|
||||
|
||||
use DateTime;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Handle badge information in the same style as the other classes
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Certficate code
|
||||
* @var string
|
||||
*/
|
||||
private static $premiumcrt = "-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjMCFFlyhmKf1fN7U5lQL/dtlsyP24AQMA0GCSqGSIb3DQEBCwUAMGEx
|
||||
CzAJBgNVBAYTAk5MMRYwFAYDVQQIDA1Ob29yZC1Ib2xsYW5kMRowGAYDVQQKDBFN
|
||||
|
@ -59,78 +62,94 @@ n8FqBInMBhGu1uz0Qeo09ke0RHRnghP9EXfig/veMegASZeEhFqmS2Bdiy6gqeZ5
|
|||
Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
||||
-----END CERTIFICATE-----";
|
||||
|
||||
/**
|
||||
* Cached status
|
||||
* @var object
|
||||
*/
|
||||
private static $cachedpremiumstatus = null;
|
||||
|
||||
/**
|
||||
* Check if premium is supported
|
||||
* @return bool
|
||||
*/
|
||||
public static function supported() {
|
||||
return self::$premiumsupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt encrypted data
|
||||
* @param string $encrypted Encrypted data
|
||||
*/
|
||||
private static function decrypt($encrypted) {
|
||||
// Get the public key.
|
||||
$key = \openssl_get_publickey(self::$premiumcrt);
|
||||
if ($key === false ) {
|
||||
throw new \ValueError("Error parsing public key data)");
|
||||
if ($key === false) {
|
||||
throw new \ParseError("Error parsing public key data)");
|
||||
}
|
||||
// Determine the key size.
|
||||
$keysize = \openssl_pkey_get_details($key)["bits"];
|
||||
//
|
||||
$blocksize = ($keysize / 8); // Bits / 8. Whether padded or not.
|
||||
|
||||
// Decode data in
|
||||
// Decode data in.
|
||||
$b64 = \base64_decode($encrypted);
|
||||
if ($b64 === false) {
|
||||
throw new \ValueError("Error in base64 decoding");
|
||||
throw new \ParseError("Error in base64 decoding");
|
||||
}
|
||||
|
||||
$data = \str_split($b64, $blocksize);
|
||||
$decrypted = "";
|
||||
$i = 0;
|
||||
foreach($data as $chunk) {
|
||||
foreach ($data as $chunk) {
|
||||
if (\openssl_public_decrypt($chunk, $dchunk, $key, \OPENSSL_PKCS1_PADDING)) {
|
||||
$decrypted .= $dchunk;
|
||||
} else {
|
||||
throw new \ValueError("Error decrypting chunk $i ({$blocksize} bytes)");
|
||||
throw new \ParseError("Error decrypting chunk $i ({$blocksize} bytes)");
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
// 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.
|
||||
try {
|
||||
if (\PHP_MAJOR_VERSION < 8) {
|
||||
\openssl_pkey_free($key);
|
||||
} catch (\Exception $x) {}
|
||||
}
|
||||
|
||||
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) {
|
||||
// Headers are repeated in this function for easier testing and copy-pasting into other projects.
|
||||
$STARTHEADER = "----- BEGIN ACTIVATION KEY -----";
|
||||
$ENDHEADER = "----- END ACTIVATION KEY -----";
|
||||
$startheader = "----- BEGIN ACTIVATION KEY -----";
|
||||
$endheader = "----- END ACTIVATION KEY -----";
|
||||
|
||||
$parts = preg_split("/\r?\n/", \trim($data));
|
||||
if (count($parts) > 2) {
|
||||
$start = -1;
|
||||
$end = -1;
|
||||
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...
|
||||
for ($i = 0; $i < count($parts); $i++) {
|
||||
// Make sure all unicode spaces are converted to normal spaces before comparing.
|
||||
$p = trim(preg_replace('/\s+/u', ' ', $parts[$i]));
|
||||
|
||||
if ( $p == $STARTHEADER ) {
|
||||
$start = $i+1;
|
||||
if ($p == $startheader) {
|
||||
$start = $i + 1;
|
||||
}
|
||||
if ($start > 0 && $p == $ENDHEADER) {
|
||||
if ($start > 0 && $p == $endheader) {
|
||||
$end = $i;
|
||||
}
|
||||
}
|
||||
|
||||
if ($start < 0 || $end < 0 || $end - $start <= 0) {
|
||||
throw new \ValueError("Invalid activation key wrappers");
|
||||
throw new \ParseError("Invalid activation key wrappers");
|
||||
} else {
|
||||
$keyslice = array_slice($parts, $start, $end - $start);
|
||||
return implode("\n", $keyslice);
|
||||
}
|
||||
} else {
|
||||
throw new \ValueError("Invalid activation key");
|
||||
throw new \ParseError("Invalid activation key");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +159,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
*/
|
||||
public static function enabled() {
|
||||
if (self::$premiumsupported) {
|
||||
$status = self::premiumStatus();
|
||||
$status = self::premiumstatus();
|
||||
return $status->enabled;
|
||||
} else {
|
||||
return true;
|
||||
|
@ -153,7 +172,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
* @return bool
|
||||
*/
|
||||
public static function has_intent($intent) {
|
||||
$status = self::premiumStatus();
|
||||
$status = self::premiumstatus();
|
||||
if ($status->enabled) {
|
||||
return \in_array(\strtolower($intent), $status->intents);
|
||||
} else {
|
||||
|
@ -161,6 +180,9 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate debug info.
|
||||
*/
|
||||
public static function debuginfo() {
|
||||
$s = "<pre>";
|
||||
try {
|
||||
|
@ -172,10 +194,10 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
$s .= $json . "\n----\n";
|
||||
$decoded = \json_decode($json, false);
|
||||
$s .= "Read as object:\n----\n";
|
||||
$s .= print_r($decoded, true) ."\n----\n";
|
||||
$status = self::premiumStatus();
|
||||
$s .= \json_encode($decoded, \JSON_PRETTY_PRINT) ."\n----\n";
|
||||
$status = self::premiumstatus();
|
||||
$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";
|
||||
} else {
|
||||
$s .= "Premium key empty";
|
||||
|
@ -188,14 +210,15 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
}
|
||||
}
|
||||
|
||||
$s.="</pre>";
|
||||
$s .= "</pre>";
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine, cache and retrieve premium status
|
||||
* @return object
|
||||
*/
|
||||
protected static function premiumStatus() {
|
||||
protected static function premiumstatus() {
|
||||
if (!isset(self::$cachedpremiumstatus)) {
|
||||
|
||||
// Initialize default object.
|
||||
|
@ -217,9 +240,9 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
|
||||
if (is_object($decoded)) {
|
||||
|
||||
// Copy basic data/
|
||||
// Copy basic data/.
|
||||
$keys = ["name", "website", "expires", "issued"];
|
||||
foreach ( $keys as $k) {
|
||||
foreach ($keys as $k) {
|
||||
if (isset($decoded->$k)) {
|
||||
$o->$k = $decoded->$k;
|
||||
}
|
||||
|
@ -229,24 +252,26 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
$o->intents = explode(", ", $decoded->intent);
|
||||
}
|
||||
|
||||
// Convert dates to DateTime for
|
||||
// Convert dates to DateTime for.
|
||||
$now = new \DateTime();
|
||||
$issuedate = new \DateTime($o->issued);
|
||||
$expirydate = new \DateTime(); // Default to now
|
||||
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"));
|
||||
} else {
|
||||
try {
|
||||
$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)
|
||||
&& !empty($o->issued)
|
||||
&& self::website_match($o->website)
|
||||
) {
|
||||
if ($expirydate > $now ) {
|
||||
if ($expirydate > $now) {
|
||||
$o->enabled = true;
|
||||
$o->expired = false;
|
||||
} else {
|
||||
|
@ -263,7 +288,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (\ValueError $x) {
|
||||
} catch (\ParseError $x) {
|
||||
$o->status = \get_string("premium:invalidactivationcontent", "local_treestudyplan");
|
||||
}
|
||||
self::$cachedpremiumstatus = $o;
|
||||
|
@ -281,7 +306,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
|
||||
// Add double slashes to key and site if no scheme is set.
|
||||
// Basically: if no double slashes present before any dots, slashes or @s.
|
||||
if (!\preg_match_all('#^[^./@]*?//#', $key )) {
|
||||
if (!\preg_match_all('#^[^./@]*?//#', $key)) {
|
||||
$key = "//".$key;
|
||||
}
|
||||
if (!\preg_match_all('#^[^./@]*?//#', $site)) {
|
||||
|
@ -291,13 +316,13 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
$keyurl = (object)\parse_url($key);
|
||||
$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)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($keyurl->host == "*") {
|
||||
// * matches all
|
||||
// Value * matches all.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -306,37 +331,41 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
$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);}
|
||||
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]) ) {
|
||||
// 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.
|
||||
$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)) {
|
||||
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)) {
|
||||
// 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 /
|
||||
// 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
|
||||
// Trim trailing / from both paths before comparison.
|
||||
if (\strlen($sitepath) > 1) {
|
||||
$sitepath = \rtrim($sitepath, "/");
|
||||
}
|
||||
|
@ -365,7 +394,9 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
"name" => new \external_value(PARAM_TEXT, 'premium registration name'),
|
||||
"expires" => new \external_value(PARAM_TEXT, 'premium registration expiry date'),
|
||||
"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() {
|
||||
if (self::$premiumsupported) {
|
||||
$status = self::premiumStatus();
|
||||
$status = self::premiumstatus();
|
||||
$keys = [
|
||||
"enabled",
|
||||
"website",
|
||||
|
@ -386,7 +417,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
];
|
||||
|
||||
$result = [];
|
||||
foreach ( $keys as $param) {
|
||||
foreach ($keys as $param) {
|
||||
$result[$param] = $status->$param;
|
||||
}
|
||||
return $result;
|
||||
|
@ -407,7 +438,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
|
|||
* @return string HTML description of premium status.
|
||||
*/
|
||||
public static function statusdescription() {
|
||||
$status = self::premiumStatus();
|
||||
$status = self::premiumstatus();
|
||||
|
||||
$msg = new \stdClass;
|
||||
$msg->name = $status->name;
|
||||
|
|
|
@ -40,6 +40,9 @@ class reportservice extends \external_api {
|
|||
*/
|
||||
const CAP_VIEW = "local/treestudyplan:viewuserreports";
|
||||
|
||||
/**
|
||||
* Parameter description for webservice function
|
||||
*/
|
||||
public static function get_report_structure_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters([
|
||||
"pageid" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
||||
|
@ -83,7 +86,7 @@ class reportservice extends \external_api {
|
|||
$firstperiod = 1;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -95,9 +98,9 @@ class reportservice extends \external_api {
|
|||
"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);
|
||||
$pmodel = [
|
||||
'period' => $period->model(),
|
||||
|
@ -127,13 +130,15 @@ class reportservice extends \external_api {
|
|||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for webservice function
|
||||
*/
|
||||
public static function get_report_data_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters([
|
||||
"pageid" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
||||
"userid" => new \external_value(PARAM_INT, 'id of user'),
|
||||
"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),
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -144,6 +149,13 @@ class reportservice extends \external_api {
|
|||
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) {
|
||||
$page = studyplanpage::find_by_id($pageid);
|
||||
webservicehelper::require_capabilities(self::CAP_VIEW, $page->studyplan()->context());
|
||||
|
@ -156,15 +168,12 @@ class reportservice extends \external_api {
|
|||
$firstperiod = 1;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
$model = [];
|
||||
|
||||
//TODO: Build report structure
|
||||
|
||||
for($i = $firstperiod; $i <= $lastperiod; $i++) {
|
||||
for ($i = $firstperiod; $i <= $lastperiod; $i++) {
|
||||
$lines = studyline::find_page_children($page);
|
||||
foreach ($lines as $line) {
|
||||
$items = studyitem::search_studyline_children($line, $i, studyitem::COURSE);
|
||||
|
@ -175,11 +184,12 @@ class reportservice extends \external_api {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $model;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for webservice function
|
||||
*/
|
||||
public static function get_report_details_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters([
|
||||
"itemid" => new \external_value(PARAM_INT, 'id of studyitem'),
|
||||
|
|
|
@ -222,7 +222,7 @@ class studentstudyplanservice extends \external_api {
|
|||
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;
|
||||
|
||||
|
@ -279,7 +279,7 @@ class studentstudyplanservice extends \external_api {
|
|||
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);
|
||||
$userid = $invite->user_id;
|
||||
|
@ -341,7 +341,7 @@ class studentstudyplanservice extends \external_api {
|
|||
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);
|
||||
$studyplan = $page->studyplan();
|
||||
|
@ -556,8 +556,8 @@ class studentstudyplanservice extends \external_api {
|
|||
|
||||
\external_api::validate_context(\context_system::instance());
|
||||
|
||||
// Return empty list if coach role not enabled
|
||||
if ( ! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach")) ) {
|
||||
// Return empty list if coach role not enabled.
|
||||
if (! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -570,7 +570,9 @@ class studentstudyplanservice extends \external_api {
|
|||
if (has_capability(self::CAP_COACH, $studyplan->context(), $USER)) {
|
||||
$list[] = $studyplan->simple_model_coach();
|
||||
}
|
||||
} catch(\Exception $x) {}
|
||||
} catch (\Exception $x) {
|
||||
$studyplan = null;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
@ -694,7 +696,7 @@ class studentstudyplanservice extends \external_api {
|
|||
$o->enrol($userid);
|
||||
}
|
||||
|
||||
// Trigger immediate cohort synchronization for this line only
|
||||
// Trigger immediate cohort synchronization for this line only.
|
||||
autocohortsync::syncline($o);
|
||||
|
||||
return $o->enrol_info_model($userid);
|
||||
|
|
|
@ -422,7 +422,7 @@ class studyitem {
|
|||
|
||||
foreach ($resequence as $sq) {
|
||||
// Only change line_id if new line is within the same studyplan page.
|
||||
if ( self::find_by_id($sq['id'])->studyline()->page()->id() ==
|
||||
if (self::find_by_id($sq['id'])->studyline()->page()->id() ==
|
||||
studyline::find_by_id($sq['line_id'])->page()->id() ) {
|
||||
$DB->update_record(self::TABLE, [
|
||||
'id' => $sq['id'],
|
||||
|
@ -646,7 +646,7 @@ class studyitem {
|
|||
break;
|
||||
}
|
||||
}
|
||||
return ($notexpired) ? completion : :COMPLETED : completion::INCOMPLETE;
|
||||
return ($notexpired) ? completion::COMPLETED : completion::INCOMPLETE;
|
||||
} else {
|
||||
return completion::COMPLETED;
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class studyitemconnection {
|
|||
/**
|
||||
* Get target studyitem id for this connection
|
||||
*/
|
||||
public function to_id() :int {
|
||||
public function to_id(): int {
|
||||
return $this->r->to_id;
|
||||
}
|
||||
|
||||
|
@ -167,9 +167,9 @@ class studyitemconnection {
|
|||
'to_id' => $toid,
|
||||
]);
|
||||
|
||||
return success::success(['msg'=>'Items Disconnected']);
|
||||
return success::success(['msg' => 'Items Disconnected']);
|
||||
} else {
|
||||
return success::success(['msg'=>'Connection does not exist']);
|
||||
return success::success(['msg' => 'Connection does not exist']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -198,13 +198,13 @@ class studyline {
|
|||
|
||||
$plan = $this->studyplan();
|
||||
if (!empty($userid)) {
|
||||
if ( $plan->has_linked_user(intval($userid))) {
|
||||
if ( $this->self_enrollable() && $userid == $USER->id ) {
|
||||
if ($plan->has_linked_user(intval($userid))) {
|
||||
if ($this->self_enrollable() && $userid == $USER->id) {
|
||||
return true;
|
||||
}
|
||||
if ( $this->role_enrollable()) {
|
||||
if ($this->role_enrollable()) {
|
||||
$context = $plan->context();
|
||||
foreach ( $this->enrol_roleids() as $rid) {
|
||||
foreach ($this->enrol_roleids() as $rid) {
|
||||
if (\user_has_role_assignment($USER->id, $rid, $context->id)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -212,9 +212,9 @@ class studyline {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if ( $this->role_enrollable()) {
|
||||
if ($this->role_enrollable()) {
|
||||
$context = $plan->context();
|
||||
foreach ( $this->enrol_roleids() as $rid) {
|
||||
foreach ($this->enrol_roleids() as $rid) {
|
||||
if (\user_has_role_assignment($USER->id, $rid, $context->id)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -239,13 +239,15 @@ class studyline {
|
|||
global $DB;
|
||||
$list = [];
|
||||
$roles = explode(", ", $this->r->enrolrole);
|
||||
foreach($roles as $r) {
|
||||
foreach ($roles as $r) {
|
||||
$roleid = intval($r);
|
||||
if ($roleid > 0) {
|
||||
try {
|
||||
$role = $DB->get_record('role', ["id" => $roleid], "*", MUST_EXIST);
|
||||
$list[] = $role;
|
||||
} catch(\Exception $x) {}
|
||||
} catch (\Exception $x) {
|
||||
$role = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +261,7 @@ class studyline {
|
|||
global $DB;
|
||||
$list = [];
|
||||
$roles = explode(", ", $this->r->enrolrole);
|
||||
foreach($roles as $r) {
|
||||
foreach ($roles as $r) {
|
||||
$roleid = intval($r);
|
||||
if ($roleid > 0) {
|
||||
$list[] = $roleid;
|
||||
|
@ -274,7 +276,7 @@ class studyline {
|
|||
*/
|
||||
public function enrol_roles_model(): array {
|
||||
$list = [];
|
||||
foreach($this->enrol_roles() as $role) {
|
||||
foreach ($this->enrol_roles() as $role) {
|
||||
$name = role_get_name($role, $this->context()); // Get localized role name.
|
||||
$list[] = [
|
||||
"id" => $role->id,
|
||||
|
@ -304,7 +306,7 @@ class studyline {
|
|||
self::SLOTSET_FILTER => new \external_multiple_structure(
|
||||
studyitem::editor_structure(), 'filter items'),
|
||||
])
|
||||
)
|
||||
),
|
||||
], "Study line editor structure", $value);
|
||||
}
|
||||
|
||||
|
@ -552,7 +554,7 @@ class studyline {
|
|||
self::SLOTSET_FILTER => new \external_multiple_structure(
|
||||
studyitem::user_structure(), 'filter items'),
|
||||
])
|
||||
)
|
||||
),
|
||||
], 'Studyline with user info', $value);
|
||||
}
|
||||
|
||||
|
@ -606,15 +608,16 @@ class studyline {
|
|||
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
|
||||
*/
|
||||
public function get_enrolled_userids() {
|
||||
$userids = $this->studyplan()->find_linked_userids();
|
||||
if ($this->enrollable()) {
|
||||
$list = [];
|
||||
foreach($userids as $uid) {
|
||||
if ( $this->isenrolled($uid)) {
|
||||
foreach ($userids as $uid) {
|
||||
if ($this->isenrolled($uid)) {
|
||||
$list[] = $uid;
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +635,7 @@ class studyline {
|
|||
public function isenrolled($userid) {
|
||||
global $DB;
|
||||
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 {
|
||||
$r = $DB->get_record('local_treestudyplan_lineuser', [
|
||||
'line_id' => $this->id(),
|
||||
|
@ -721,7 +724,7 @@ class studyline {
|
|||
// Registration already exists, check if enrolled is true and update accordingly.
|
||||
if (boolval($r->enrolled)) {
|
||||
$r->enrolled = 0;
|
||||
$r->timeenrolled = time(); // Regi
|
||||
$r->timeenrolled = time(); // Regi.
|
||||
$r->enrolledby = $USER->id;
|
||||
$DB->update_record("local_treestudyplan_lineuser", $r);
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ class studyplan {
|
|||
*/
|
||||
public function startdate() {
|
||||
$date = null;
|
||||
foreach($this->pages() as $p) {
|
||||
foreach ($this->pages() as $p) {
|
||||
if (!isset($date) || $p->startdate() < $date) {
|
||||
$date = $p->startdate();
|
||||
}
|
||||
|
@ -154,13 +154,17 @@ class studyplan {
|
|||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine studyplan icon
|
||||
* @return string Url of icon
|
||||
*/
|
||||
private function icon() {
|
||||
global $CFG;
|
||||
$fs = \get_file_storage();
|
||||
// Returns an array of `stored_file` instances.
|
||||
$files = $fs->get_area_files(\context_system::instance()->id, 'local_treestudyplan', 'icon', $this->id);
|
||||
|
||||
if (count($files) > 0 ) {
|
||||
if (count($files) > 0) {
|
||||
$file = array_shift($files);
|
||||
if ($file->get_filename() == ".") {
|
||||
// Get next file if the first is the directory itself.
|
||||
|
@ -183,8 +187,14 @@ class studyplan {
|
|||
// Fall back to the standard (ugly) default image.
|
||||
$url = new \moodle_url($CFG->wwwroot . "/local/treestudyplan/pix/default_icon.png");
|
||||
} else {
|
||||
$url = \moodle_url::make_pluginfile_url(\context_system::instance()->id, 'local_treestudyplan', 'defaulticon', 0, "/",
|
||||
$defaulticon);
|
||||
$url = \moodle_url::make_pluginfile_url(
|
||||
\context_system::instance()->id,
|
||||
'local_treestudyplan',
|
||||
'defaulticon',
|
||||
0,
|
||||
"/",
|
||||
$defaulticon
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -201,9 +211,9 @@ class studyplan {
|
|||
// The pluginfile URL which will serve the request.
|
||||
'pluginfile.php',
|
||||
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that file are stored in.
|
||||
\context_system::instance()->id, // System instance is always used for this
|
||||
// The combination of contextid / component / filearea / itemid.
|
||||
// Form the virtual bucket that file are stored in.
|
||||
\context_system::instance()->id, // System instance is always used for this.
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$this->id
|
||||
|
@ -216,7 +226,7 @@ class studyplan {
|
|||
* @return studyplanpage[]
|
||||
*/
|
||||
public function pages($refresh=false): array {
|
||||
if ( ((bool)$refresh) || empty($this->pagecache)) {
|
||||
if (((bool)$refresh) || empty($this->pagecache)) {
|
||||
$this->pagecache = studyplanpage::find_studyplan_children($this);
|
||||
}
|
||||
return $this->pagecache;
|
||||
|
@ -266,7 +276,11 @@ class studyplan {
|
|||
"aggregation_info" => aggregator::basic_structure(),
|
||||
"pages" => new \external_multiple_structure(studyplanpage::simple_structure(), 'pages'),
|
||||
"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),
|
||||
], 'Basic studyplan info', $value);
|
||||
}
|
||||
|
@ -313,7 +327,7 @@ class studyplan {
|
|||
$model = $this->simple_model();
|
||||
$users = $this->find_linked_userids();
|
||||
$sum = 0;
|
||||
foreach ( $users as $uid ) {
|
||||
foreach ($users as $uid) {
|
||||
$sum += $this->scanuserprogress($uid);
|
||||
}
|
||||
|
||||
|
@ -408,7 +422,8 @@ class studyplan {
|
|||
public static function add($fields, $bare = false): self {
|
||||
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];
|
||||
foreach ($addable as $f) {
|
||||
if (array_key_exists($f, $fields)) {
|
||||
|
@ -418,8 +433,8 @@ class studyplan {
|
|||
$id = $DB->insert_record(self::TABLE, $info);
|
||||
$plan = self::find_by_id($id); // Make sure the new studyplan is immediately cached.
|
||||
|
||||
// Add a single page and initialize it with placeholder data
|
||||
// This makes it easier to create a new study plan
|
||||
// Add a single page and initialize it with placeholder data.
|
||||
// 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....
|
||||
if (!$bare) {
|
||||
|
||||
|
@ -459,7 +474,7 @@ class studyplan {
|
|||
'aggregation',
|
||||
'aggregation_config',
|
||||
'suspended',
|
||||
'template'
|
||||
'template',
|
||||
];
|
||||
$info = ['id' => $this->id ];
|
||||
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_cohort", ["studyplan_id" => $this->id]);
|
||||
$DB->delete_records("local_treestudyplan_user", ["studyplan_id" => $this->id]);
|
||||
//
|
||||
|
||||
$DB->delete_records('local_treestudyplan', ['id' => $this->id]);
|
||||
return success::success();
|
||||
}
|
||||
|
@ -768,7 +783,8 @@ class studyplan {
|
|||
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
|
||||
*/
|
||||
public function has_linked_user($user) {
|
||||
|
@ -785,13 +801,14 @@ 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.
|
||||
*/
|
||||
public function is_coach($user=null) {
|
||||
global $DB, $USER
|
||||
;
|
||||
if ( ! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach")) ) {
|
||||
global $DB, $USER;
|
||||
|
||||
if (! (premium::enabled() && \get_config("local_treestudyplan", "enablecoach"))) {
|
||||
// If coach role is not available, return false immediately.
|
||||
return false;
|
||||
}
|
||||
|
@ -803,7 +820,7 @@ class studyplan {
|
|||
} else {
|
||||
$userid = $user->id;
|
||||
}
|
||||
$r = $DB->get_record(self::TABLE_COACH, ["studyplan_id" => $this->id, "user_id"=> $userid]);
|
||||
$r = $DB->get_record(self::TABLE_COACH, ["studyplan_id" => $this->id, "user_id" => $userid]);
|
||||
|
||||
if ($r && has_capability(associationservice::CAP_COACH, $this->context(), $userid)) {
|
||||
return true;
|
||||
|
@ -845,8 +862,8 @@ class studyplan {
|
|||
$prg = $p->scanuserprogress($userid);
|
||||
$progress += $prg;
|
||||
}
|
||||
// Now average it out over the amount of pages
|
||||
if (count($pages) > 0 ) {
|
||||
// Now average it out over the amount of pages.
|
||||
if (count($pages) > 0) {
|
||||
return $progress / count($pages);
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -912,7 +929,7 @@ class studyplan {
|
|||
'description' => $this->r->description,
|
||||
'descriptionformat' => $this->r->descriptionformat,
|
||||
'aggregation' => $this->r->aggregation,
|
||||
'aggregation_config' => $this->r->aggregation_config
|
||||
'aggregation_config' => $this->r->aggregation_config,
|
||||
], true);
|
||||
|
||||
// Copy any files related to this studyplan.
|
||||
|
@ -981,7 +998,7 @@ class studyplan {
|
|||
$json = json_encode([
|
||||
"type" => "studyplan",
|
||||
"version" => 2.0,
|
||||
"studyplan" => $model
|
||||
"studyplan" => $model,
|
||||
], \JSON_PRETTY_PRINT);
|
||||
return [ "format" => "application/json", "content" => $json];
|
||||
}
|
||||
|
@ -1042,10 +1059,10 @@ class studyplan {
|
|||
*/
|
||||
public function import_files($importfiles, $area) {
|
||||
$fs = get_file_storage();
|
||||
foreach($importfiles as $file) {
|
||||
foreach ($importfiles as $file) {
|
||||
if ($file['name'] != ".") {
|
||||
$fileinfo = [
|
||||
'contextid' =>\context_system::instance()->id, // ID of the system context.
|
||||
'contextid' => \context_system::instance()->id, // ID of the system context.
|
||||
'component' => 'local_treestudyplan', // Your component name.
|
||||
'filearea' => $area, // Usually = table name.
|
||||
'itemid' => $this->id, // ID of the studyplanpage.
|
||||
|
@ -1094,11 +1111,11 @@ class studyplan {
|
|||
// Create a new plan, based on the given parameters - this is the import studyplan part.
|
||||
$plan = self::add( $planmodel, true);
|
||||
|
||||
// Import the files
|
||||
// Import the files.
|
||||
if (isset( $planmodel['files']) && is_array( $planmodel['files'])) {
|
||||
$plan->import_files( $planmodel['files'], "studyplan");
|
||||
}
|
||||
// Import the icon
|
||||
// Import the icon.
|
||||
if (isset( $planmodel['iconfiles']) && is_array( $planmodel['iconfiles'])) {
|
||||
$plan->import_files( $planmodel['iconfiles'], "icon");
|
||||
}
|
||||
|
@ -1215,7 +1232,7 @@ class studyplan {
|
|||
$fields = $DB->get_fieldset_sql($sql, ["studyplan_id" => $this->id]);
|
||||
|
||||
$list = [];
|
||||
foreach($fields as $id) {
|
||||
foreach ($fields as $id) {
|
||||
$list[] = studyline::find_by_id($id);
|
||||
}
|
||||
return $list;
|
||||
|
|
|
@ -154,9 +154,9 @@ class studyplanpage {
|
|||
// The pluginfile URL which will serve the request.
|
||||
'pluginfile.php',
|
||||
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that file are stored in.
|
||||
\context_system::instance()->id, // System instance is always used for this
|
||||
// The combination of contextid / component / filearea / itemid.
|
||||
// Form the virtual bucket that file are stored in.
|
||||
\context_system::instance()->id, // System instance is always used for this.
|
||||
'local_treestudyplan',
|
||||
'studyplanpage',
|
||||
$this->id
|
||||
|
@ -164,6 +164,10 @@ class studyplanpage {
|
|||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine studyplan timing
|
||||
* @return string
|
||||
*/
|
||||
public function timing() {
|
||||
$now = new \DateTime();
|
||||
if ($now > $this->startdate()) {
|
||||
|
@ -292,7 +296,7 @@ class studyplanpage {
|
|||
}
|
||||
|
||||
$id = $DB->insert_record(self::TABLE, $info);
|
||||
// Refresh the page cache for the studyplan
|
||||
// Refresh the page cache for the studyplan.
|
||||
$plan->pages(true);
|
||||
$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")) {
|
||||
$templatepage = null;
|
||||
$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);
|
||||
foreach ($pages as $p) {
|
||||
if ($p->id() != $id) {
|
||||
|
@ -322,7 +326,7 @@ class studyplanpage {
|
|||
}
|
||||
|
||||
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,
|
||||
'name' => get_string("default_line_name", "local_treestudyplan"),
|
||||
'shortname' => get_string("default_line_shortname", "local_treestudyplan"),
|
||||
|
@ -450,8 +454,8 @@ class studyplanpage {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ( $courses > 0 ) {
|
||||
return ($completed/$courses);
|
||||
if ($courses > 0) {
|
||||
return ($completed / $courses);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -585,7 +589,7 @@ class studyplanpage {
|
|||
$json = json_encode([
|
||||
"type" => "studyplanpage",
|
||||
"version" => 2.0,
|
||||
"page" => $model
|
||||
"page" => $model,
|
||||
], \JSON_PRETTY_PRINT);
|
||||
return [ "format" => "application/json", "content" => $json];
|
||||
}
|
||||
|
@ -752,10 +756,10 @@ class studyplanpage {
|
|||
*/
|
||||
public function import_files($importfiles, $area) {
|
||||
$fs = get_file_storage();
|
||||
foreach($importfiles as $file) {
|
||||
foreach ($importfiles as $file) {
|
||||
if ($file['name'] != ".") {
|
||||
$fileinfo = [
|
||||
'contextid' =>\context_system::instance()->id, // ID of the system context.
|
||||
'contextid' => \context_system::instance()->id, // ID of the system context.
|
||||
'component' => 'local_treestudyplan', // Your component name.
|
||||
'filearea' => $area, // Usually = table name.
|
||||
'itemid' => $this->id, // ID of the studyplanpage.
|
||||
|
|
|
@ -133,7 +133,7 @@ class studyplanservice extends \external_api {
|
|||
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()) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -902,7 +902,6 @@ class studyplanservice extends \external_api {
|
|||
*/
|
||||
public static function list_badges_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters( [
|
||||
|
||||
] );
|
||||
}
|
||||
|
||||
|
@ -922,12 +921,9 @@ class studyplanservice extends \external_api {
|
|||
|
||||
$result = [];
|
||||
$badges = badges_get_badges(BADGE_TYPE_SITE, "timemodified");
|
||||
|
||||
foreach ($badges as $badge) {
|
||||
// TODO: Add config option to list only active badges.
|
||||
$result[] = (new badgeinfo($badge))->editor_model();
|
||||
|
||||
// TODO: Include course badges somehow... Just site badges is not enough.
|
||||
|
||||
}
|
||||
return $result;
|
||||
|
||||
|
@ -962,7 +958,7 @@ class studyplanservice extends \external_api {
|
|||
*/
|
||||
public static function search_badges($search, $active=false) {
|
||||
$systemcontext = webservicehelper::system_context();
|
||||
// Check system permission to
|
||||
// Check system permission to.
|
||||
webservicehelper::require_capabilities("moodle/badges:viewbadges", $systemcontext);
|
||||
|
||||
$results = [];
|
||||
|
@ -1115,7 +1111,8 @@ class studyplanservice extends \external_api {
|
|||
$studyplan = $item->studyline()->studyplan();
|
||||
if (($studyplan->is_coach() && !$studyplan->suspended())
|
||||
|| 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();
|
||||
} else {
|
||||
return success::fail("Access denied")->model();
|
||||
|
@ -1518,7 +1515,7 @@ class studyplanservice extends \external_api {
|
|||
// Validate import context.
|
||||
webservicehelper::require_capabilities(self::CAP_EDIT, $plan->context());
|
||||
$result = $plan->import_pages($content, $format);
|
||||
return ($result ? success : :success(): success::fail())->model();
|
||||
return ($result ? success::success() : success::fail())->model();
|
||||
} catch (\webservice_access_exception $x) {
|
||||
return success::fail("Access denied")->model();
|
||||
}
|
||||
|
@ -1556,7 +1553,7 @@ class studyplanservice extends \external_api {
|
|||
webservicehelper::require_capabilities(self::CAP_EDIT, $page->studyplan()->context());
|
||||
|
||||
$result = $page->import_studylines($content, $format);
|
||||
return ($result ? success : :success(): success::fail())->model();
|
||||
return ($result ? success::success() : success::fail())->model();
|
||||
} catch (\webservice_access_exception $x) {
|
||||
return success::fail("Access denied")->model();
|
||||
}
|
||||
|
@ -1622,7 +1619,7 @@ class studyplanservice extends \external_api {
|
|||
foreach (explode("&", $formdata) as $pair) {
|
||||
$p = explode("=", $pair, 2);
|
||||
$k = urldecode($p[0]);
|
||||
$v = (count($p) > 1) ? urldecode($p[1]): "";
|
||||
$v = (count($p) > 1) ? urldecode($p[1]) : "";
|
||||
|
||||
if (strpos($k, "[") > 0 && strpos($k, "]") == strlen($k) - 1) {
|
||||
// Its a bracketet field, like filename[text] which should be separated and put into a named array.
|
||||
|
@ -1892,7 +1889,7 @@ class studyplanservice extends \external_api {
|
|||
$studyitems = studyitem::find_studyline_children($line);
|
||||
foreach ($studyitems as $item) {
|
||||
if ($item->type() == studyitem::COURSE) {
|
||||
if ( $item->slot() <= $page->periods() ) {
|
||||
if ($item->slot() <= $page->periods()) {
|
||||
$period = $periods[$item->slot()];
|
||||
try {
|
||||
self::course_period_timing($period->id(), $item->courseid(), $item->span());
|
||||
|
@ -1991,11 +1988,11 @@ class studyplanservice extends \external_api {
|
|||
$contextlevels = [CONTEXT_SYSTEM, CONTEXT_COURSECAT];
|
||||
$list = [];
|
||||
$roles = \get_all_roles();
|
||||
foreach($roles as $role) {
|
||||
foreach ($roles as $role) {
|
||||
$name = \role_get_name($role, $p->context()); // Get localized role name.
|
||||
$rctxtlevels = \get_role_contextlevels($role->id);
|
||||
$intersect = array_intersect($rctxtlevels, $contextlevels);
|
||||
if ( count($intersect) > 0 ) {
|
||||
if (count($intersect) > 0) {
|
||||
$list[] = [
|
||||
'id' => $role->id,
|
||||
'name' => $name,
|
||||
|
@ -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);
|
||||
return $list;
|
||||
}
|
||||
|
@ -2132,7 +2129,7 @@ class studyplanservice extends \external_api {
|
|||
$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);
|
||||
return $list;
|
||||
}
|
||||
|
@ -2174,7 +2171,7 @@ class studyplanservice extends \external_api {
|
|||
|
||||
$list = [];
|
||||
$p = $o->studyplan();
|
||||
foreach( $p->find_linked_userids() as $userid) {
|
||||
foreach ($p->find_linked_userids() as $userid) {
|
||||
$list[] = self::student_enrol_status_model($userid, $o);
|
||||
}
|
||||
|
||||
|
@ -2195,7 +2192,6 @@ class studyplanservice extends \external_api {
|
|||
*/
|
||||
public static function count_templates_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters( [
|
||||
|
||||
] );
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class autocohortsync extends \core\task\scheduled_task {
|
|||
$userenroller = new cascadeusersync($plan);
|
||||
$userenroller->sync($line);
|
||||
}
|
||||
// Leave the csync required flag for the next auto update
|
||||
// Leave the csync required flag for the next auto update.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ class teachingfinder {
|
|||
public static function is_teaching_studyplan(studyplan $plan, $userid) {
|
||||
global $DB;
|
||||
$count = $DB->count_records(self::TABLE, ['teacher_id' => $userid, "studyplan_id" => $plan->id()]);
|
||||
return ($count > 0)?true:false;
|
||||
return ($count > 0) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +76,7 @@ class teachingfinder {
|
|||
public static function is_teaching($userid) {
|
||||
global $DB;
|
||||
$count = $DB->count_records(self::TABLE, ['teacher_id' => $userid]);
|
||||
return ($count > 0)?true:false;
|
||||
return ($count > 0) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,7 +167,6 @@ class teachingfinder {
|
|||
$now = time();
|
||||
foreach ($list as $pageid) {
|
||||
// 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]);
|
||||
$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";
|
||||
|
||||
/**
|
||||
* Load moodle form
|
||||
* @param string $formname
|
||||
* @param string $params
|
||||
* @param string $ajaxformdata
|
||||
*/
|
||||
protected static function load_mform($formname, $params, $ajaxformdata = null) {
|
||||
global $CFG;
|
||||
/* We don't need to load the form php file (class autoloading will handle that)
|
||||
|
@ -58,7 +64,7 @@ class utilityservice extends \external_api {
|
|||
|
||||
$mformclassname = "\\local_treestudyplan\\form\\{$formname}";
|
||||
// Check if the form is a subclass of formbase.
|
||||
if ( is_a($mformclassname, "\\local_treestudyplan\\form\\formbase", true)) {
|
||||
if (is_a($mformclassname, "\\local_treestudyplan\\form\\formbase", true)) {
|
||||
$jsonparams = json_decode($params);
|
||||
$mform = new $mformclassname( $jsonparams, $ajaxformdata);
|
||||
return $mform;
|
||||
|
@ -121,7 +127,7 @@ class utilityservice extends \external_api {
|
|||
return new \external_single_structure(
|
||||
[
|
||||
'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 = [];
|
||||
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);
|
||||
|
||||
$return = $mform->process_submission();
|
||||
|
@ -193,7 +199,7 @@ class utilityservice extends \external_api {
|
|||
|
||||
return [
|
||||
"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([
|
||||
'help' => false,
|
||||
'amount' => null
|
||||
'amount' => null,
|
||||
], [
|
||||
'h' => 'help',
|
||||
'a' => 'amount',
|
||||
|
@ -56,7 +56,7 @@ if ($options['help']) {
|
|||
cli_writeln($usage);
|
||||
exit(2);
|
||||
}
|
||||
print_r($options);
|
||||
|
||||
if (empty($options['amount']) || !is_numeric($options['amount'])) {
|
||||
cli_error('Missing mandatory integer argument "amount"', 2);
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ $authplugin->sync_roles($user);
|
|||
login_attempt_valid($user);
|
||||
complete_user_login($user);
|
||||
|
||||
for ($i=0; $i<$amount; $i++) {
|
||||
for ($i = 0; $i < $amount; $i++) {
|
||||
$fortune = shell_exec("{$fortunepath} -n 160 ");
|
||||
$pieces = explode(" ", $fortune);
|
||||
$name = implode(" ", array_splice($pieces, 0, 4));
|
||||
|
|
|
@ -43,7 +43,7 @@ list($options, $unrecognised) = cli_get_params([
|
|||
'help' => false,
|
||||
'studyplan' => null,
|
||||
'all' => false,
|
||||
'file' => "/tmp/generategrades.json"
|
||||
'file' => "/tmp/generategrades.json",
|
||||
], [
|
||||
'h' => 'help',
|
||||
's' => 'studyplan',
|
||||
|
|
|
@ -47,13 +47,13 @@ list($options, $unrecognised) = cli_get_params([
|
|||
'dryrun' => false,
|
||||
'studyplan' => null,
|
||||
'all' => false,
|
||||
'file' => "/tmp/generategrades.json"
|
||||
'file' => "/tmp/generategrades.json",
|
||||
], [
|
||||
'h' => 'help',
|
||||
's' => 'studyplan',
|
||||
'a' => 'all',
|
||||
'f' => 'file',
|
||||
'd' => 'dryrun'
|
||||
'd' => 'dryrun',
|
||||
]);
|
||||
|
||||
if ($unrecognised) {
|
||||
|
@ -150,7 +150,10 @@ foreach ($plans as $plan) {
|
|||
$ug = $a->get_user_grade($u->id, true);
|
||||
$ug->grade = grade_floatval($gg->grade);
|
||||
$ug->grader = $USER->id;
|
||||
$ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
|
||||
$ug->feedbacktext = nl2br(htmlspecialchars(
|
||||
$gg->fb,
|
||||
ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401
|
||||
));
|
||||
$ug->feedbackformat = FORMAT_HTML;
|
||||
|
||||
if (!$options["dryrun"]) {
|
||||
|
|
24
coach.php
24
coach.php
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// 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
|
||||
* @copyright 2023 P.M. Kuipers
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
@ -37,7 +37,7 @@ $PAGE->set_title(get_string('coaching_plans', 'local_treestudyplan'));
|
|||
$PAGE->set_heading(get_string('coaching_plans', 'local_treestudyplan'));
|
||||
|
||||
premium::require_premium();
|
||||
if ( ! (\get_config("local_treestudyplan", "enablecoach")) ) {
|
||||
if (! (\get_config("local_treestudyplan", "enablecoach"))) {
|
||||
throw new \moodle_exception("error:coachdisabled", "local_treestudyplan");
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,6 @@ $PAGE->requires->js_call_amd('local_treestudyplan/page-coach', 'init', []);
|
|||
|
||||
/**
|
||||
* Shortcut function to provide translations
|
||||
*
|
||||
* @param mixed $str Translation key
|
||||
* @param null|string[] $param Parameters to pass to translation
|
||||
* @param string $plugin Location to search for translation strings
|
||||
|
@ -61,7 +60,14 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
|||
}
|
||||
|
||||
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 class='vue-loader' v-show='false'>
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
|
@ -73,7 +79,7 @@ print $OUTPUT->header();
|
|||
<template v-if="displayedstudyplan">
|
||||
<a href='#' @click.prevent='closeStudyplan'
|
||||
><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
|
||||
v-model="displayedstudyplan"
|
||||
v-if="displayedstudyplan.description"
|
||||
|
@ -93,14 +99,14 @@ print $OUTPUT->header();
|
|||
variant="primary"
|
||||
>
|
||||
<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>
|
||||
</template>
|
||||
</div>
|
||||
<div class='t-studyplan-container'>
|
||||
<h2 v-if='displayedstudyplan&& selectedstudent'
|
||||
>{{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">
|
||||
<r-studyplan v-if="selectedstudent"
|
||||
v-model='displayedstudyplan'
|
||||
|
@ -116,7 +122,7 @@ print $OUTPUT->header();
|
|||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<div v-else class='t-studyplan-notselected'>
|
||||
<p><?php t("studyplan_noneselected"); ?></p>
|
||||
<p>{$text->studyplan_noneselected}</p>
|
||||
<b-card-group deck>
|
||||
<s-studyplan-card
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
|
@ -130,6 +136,6 @@ print $OUTPUT->header();
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
END;
|
||||
|
||||
print $OUTPUT->footer();
|
||||
|
|
|
@ -27,63 +27,62 @@ $capabilities = [
|
|||
'riskbitmask' => RISK_DATALOSS ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'manager' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
'local/treestudyplan:forcescales' => [
|
||||
'riskbitmask' => RISK_DATALOSS ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'manager' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
'local/treestudyplan:selectowngradables' => [
|
||||
'riskbitmask' => RISK_DATALOSS ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'teacher' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'teacher' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
'local/treestudyplan:configure' => [
|
||||
'riskbitmask' => RISK_DATALOSS ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'manager' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
'local/treestudyplan:viewuserreports' => [
|
||||
'riskbitmask' => RISK_PERSONAL ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'manager' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
'local/treestudyplan:lineunenrol' => [
|
||||
'riskbitmask' => RISK_PERSONAL ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'manager' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
'local/treestudyplan:coach' => [
|
||||
'riskbitmask' => RISK_PERSONAL ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
'archetypes' => [
|
||||
'manager' => CAP_ALLOW,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$services = [
|
||||
|
||||
];
|
||||
|
||||
$functions = [
|
||||
|
@ -832,5 +831,4 @@ $functions = [
|
|||
'capabilities' => '',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
@ -49,5 +49,4 @@ $tasks = [
|
|||
'month' => '*',
|
||||
'dayofweek' => '*',
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
@ -394,7 +394,7 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
"fullname" => "Period {$pnum}",
|
||||
"shortname" => "P{$pnum}",
|
||||
"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);
|
||||
}
|
||||
|
@ -532,7 +532,6 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
|
||||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2024022502, 'local', 'treestudyplan');
|
||||
}
|
||||
|
|
2
doc.php
2
doc.php
|
@ -45,7 +45,7 @@ if (!file_exists($file)) {
|
|||
$mime = mime_content_type($file);
|
||||
$texttypes = ["text/html", "text/plain"];
|
||||
|
||||
if ( in_array($mime, $texttypes)) {
|
||||
if (in_array($mime, $texttypes)) {
|
||||
print $OUTPUT->header();
|
||||
print file_get_contents($file);
|
||||
print $OUTPUT->footer();
|
||||
|
|
|
@ -51,9 +51,9 @@ require_login();
|
|||
print $OUTPUT->header();
|
||||
|
||||
if (!empty($add)) {
|
||||
$data = array(
|
||||
$data = [
|
||||
'add' => 1,
|
||||
);
|
||||
];
|
||||
|
||||
} else if (!empty($update)) {
|
||||
$data = $DB->get_record("local_treestudyplan_invit", ['id' => $update]);
|
||||
|
|
|
@ -64,7 +64,6 @@ $ci = new contextinfo($studyplancontext);
|
|||
$contextname = $ci->pathstr();
|
||||
|
||||
$PAGE->set_pagelayout('base');
|
||||
//$PAGE->set_context($studyplancontext);
|
||||
$PAGE->set_title(get_string('cfg_plans', 'local_treestudyplan')." - ".$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 ]));
|
||||
$PAGE->navbar->add(get_string('cfg_plans', 'local_treestudyplan'));
|
||||
|
||||
// Coursecat context
|
||||
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later
|
||||
// Coursecat context.
|
||||
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later.
|
||||
} else {
|
||||
// System context
|
||||
// System context.
|
||||
$cat = \core_course_category::top();
|
||||
}
|
||||
|
||||
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)) {
|
||||
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.
|
||||
|
@ -110,8 +119,13 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
|||
}
|
||||
|
||||
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 class='vue-loader' v-show='false'>
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
|
@ -120,26 +134,27 @@ print $OUTPUT->header();
|
|||
</div>
|
||||
<div v-cloak>
|
||||
<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' : ''">
|
||||
<b-form-select-option v-if='!(usedcontexts.length)' :value="contextid"
|
||||
: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"
|
||||
:class="(ctx.studyplancount > 0) ? 'font-weight-bold' : ''"
|
||||
><span v-for="(p, i) in ctx.category.path"
|
||||
><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</span>
|
||||
</b-form-select-option>
|
||||
</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>
|
||||
|
||||
</div>
|
||||
<h3 v-else><?php print $contextname; ?></h3>
|
||||
<h3 v-else>{$contextname}</h3>
|
||||
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
||||
<a href='#' v-if='activestudyplan' @click.prevent='closeStudyplan'
|
||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
||||
<span v-if='activestudyplan'><?php t("studyplan_select"); ?></span>
|
||||
<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-option
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
|
@ -181,7 +196,7 @@ print $OUTPUT->header();
|
|||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<div v-else class='t-studyplan-notselected'>
|
||||
<p><?php t("studyplan_noneselected"); ?></p>
|
||||
<p>{$text->studyplan_noneselected}</p>
|
||||
<b-card-group deck>
|
||||
<s-studyplan-card
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
|
@ -193,7 +208,11 @@ print $OUTPUT->header();
|
|||
>
|
||||
<template #title>
|
||||
<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>
|
||||
</span>
|
||||
</template>
|
||||
|
@ -203,6 +222,5 @@ print $OUTPUT->header();
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
END;
|
||||
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}"));
|
||||
|
||||
print $OUTPUT->header();
|
||||
?>
|
||||
print "
|
||||
<div id='root'>
|
||||
<div class='vue-loader' v-show='false'>
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
<div class='spinner-border text-primary' role='status'>
|
||||
<span class='sr-only'>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-cloak>
|
||||
<r-report type="invited" :invitekey="invitekey"></r-report>
|
||||
<r-report type='invited' :invitekey='invitekey'></r-report>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
";
|
||||
|
||||
print $OUTPUT->footer();
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ $string["infofield_position_below"] = 'Below course results';
|
|||
$string["infofield_position_header"] = 'Header ';
|
||||
$string["infofield_position_footer"] = 'Footer';
|
||||
|
||||
for ($i=1;$i<=5;$i++) {
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$string["setting_infofield".$i."_field"] = 'Add course details from field ('.$i.')';
|
||||
$string["settingdesc_infofield".$i."_field"] = 'Information field to show';
|
||||
$string["setting_infofield".$i."_title"] = 'Title for course details from field ('.$i.').';
|
||||
|
|
|
@ -137,7 +137,7 @@ $string["infofield_position_below"] = 'Onder cursusresultaten';
|
|||
$string["infofield_position_header"] = 'Header ';
|
||||
$string["infofield_position_footer"] = 'Footer';
|
||||
|
||||
for ($i=1;$i<=5;$i++) {
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$string["setting_infofield".$i."_field"] = 'Laat gegevens zien uit cursusveld ('.$i.')';
|
||||
$string["settingdesc_infofield".$i."_field"] = 'Kies cursusveld om informatie uit te tonen';
|
||||
$string["setting_infofield".$i."_title"] = 'Titel voor gegevens uit cursusveld ('.$i.')';
|
||||
|
|
34
lib.php
34
lib.php
|
@ -56,7 +56,7 @@ function local_treestudyplan_autofill_customusermenuitems() {
|
|||
"/local/treestudyplan/edit-plan.php" => ["included" => false, "strkey" => "link_editplan"],
|
||||
];
|
||||
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"];
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ function local_treestudyplan_autofill_customusermenuitems() {
|
|||
$link, // Link.
|
||||
'', // Tooltip.
|
||||
$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;
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
|
|||
} else {
|
||||
$hideprimaryhrefs[] = "/local/treestudyplan/myreport.php";
|
||||
}
|
||||
if ( has_capability('local/treestudyplan:viewuserreports', context_system::instance())
|
||||
if (has_capability('local/treestudyplan:viewuserreports', context_system::instance())
|
||||
|| webservicehelper::has_capability_in_any_category('local/treestudyplan:viewuserreports')) {
|
||||
$node = navigation_node::create(
|
||||
get_string("link_viewplan", "local_treestudyplan"),
|
||||
|
@ -180,7 +180,7 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
|
|||
} else {
|
||||
$hideprimaryhrefs[] = "/local/treestudyplan/view-plan.php";
|
||||
}
|
||||
if ( has_capability('local/treestudyplan:editstudyplan', context_system::instance())
|
||||
if (has_capability('local/treestudyplan:editstudyplan', context_system::instance())
|
||||
|| webservicehelper::has_capability_in_any_category('local/treestudyplan:editstudyplan')
|
||||
) {
|
||||
$node = navigation_node::create(
|
||||
|
@ -198,11 +198,11 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
|
|||
$hideprimaryhrefs[] = "/local/treestudyplan/edit-plan.php";
|
||||
}
|
||||
|
||||
$coachsql = "SELECT COUNT('id') FROM {local_treestudyplan_coach} as c
|
||||
INNER JOIN {local_treestudyplan} AS t ON c.studyplan_id = t.id
|
||||
$coachsql = "SELECT COUNT('id') FROM {local_treestudyplan_coach} c
|
||||
INNER JOIN {local_treestudyplan} t ON c.studyplan_id = t.id
|
||||
WHERE c.user_id = :user_id";
|
||||
|
||||
if ( (premium::enabled() && \get_config("local_treestudyplan", "enablecoach")) &&
|
||||
if ((premium::enabled() && \get_config("local_treestudyplan", "enablecoach")) &&
|
||||
(has_capability('local/treestudyplan:coach', context_system::instance())
|
||||
|| webservicehelper::has_capability_in_any_category('local/treestudyplan:coach')
|
||||
) && $DB->count_records_sql($coachsql, ["user_id" => $USER->id]) > 0
|
||||
|
@ -332,7 +332,7 @@ function local_treestudyplan_send_invite($inviteid) {
|
|||
if ($invite->allow_details || $invite->allow_calendar || $invite->allow_badges) {
|
||||
$data['permissions'] = get_string('invite_mail_permissions', 'local_treestudyplan');
|
||||
$data['permissions'] .= "<ul>\n";
|
||||
if ($invite->allow_details ) {
|
||||
if ($invite->allow_details) {
|
||||
$data['permissions'] .= "<li>".get_string('invite_allow_details', 'local_treestudyplan')."</li>\n";
|
||||
}
|
||||
if ($invite->allow_calendar) {
|
||||
|
@ -449,14 +449,14 @@ function local_treestudyplan_pluginfile(
|
|||
$itemid = array_shift($args);
|
||||
|
||||
// Studyplan icons and description images are not secret, so don't overdo it on access control...
|
||||
if ( true ) {
|
||||
// Extract the filename / filepath from the $args array
|
||||
if (true) {
|
||||
// Extract the filename / filepath from the $args array.
|
||||
$filename = array_pop($args); // The last item in the $args array.
|
||||
if (empty($args)) {
|
||||
// $args is empty => the path is '/'.
|
||||
// Var $args is empty => the path is '/'.
|
||||
$filepath = '/';
|
||||
} else {
|
||||
// $args contains the remaining elements of the filepath.
|
||||
// Var $args contains the remaining elements of the filepath.
|
||||
$filepath = '/' . implode('/', $args) . '/';
|
||||
}
|
||||
|
||||
|
@ -468,7 +468,7 @@ function local_treestudyplan_pluginfile(
|
|||
return false;
|
||||
}
|
||||
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
|
||||
send_stored_file($file, 24*60*60, 0, $forcedownload, $options);
|
||||
send_stored_file($file, 24 * 60 * 60, 0, $forcedownload, $options);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -478,13 +478,13 @@ function local_treestudyplan_pluginfile(
|
|||
// Fetch the itemid from the path.
|
||||
$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.
|
||||
if (empty($args)) {
|
||||
// $args is empty => the path is '/'.
|
||||
// Var $args is empty => the path is '/'.
|
||||
$filepath = '/';
|
||||
} else {
|
||||
// $args contains the remaining elements of the filepath.
|
||||
// Var $args contains the remaining elements of the filepath.
|
||||
$filepath = '/' . implode('/', $args) . '/';
|
||||
}
|
||||
|
||||
|
@ -496,7 +496,7 @@ function local_treestudyplan_pluginfile(
|
|||
return false;
|
||||
}
|
||||
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
|
||||
send_stored_file($file, 24*60*60, 0, $forcedownload, $options);
|
||||
send_stored_file($file, 24 * 60 * 60, 0, $forcedownload, $options);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ require_login();
|
|||
$PAGE->set_pagelayout('base');
|
||||
$PAGE->set_context($systemcontext);
|
||||
|
||||
//$teachermode = has_capability("local/treestudyplan:viewuserreports", $systemcontext);
|
||||
$amteaching = teachingfinder::is_teaching($USER->id);
|
||||
$haveplans = studyplan::exist_for_user($USER->id);
|
||||
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
require_once("../../config.php");
|
||||
|
||||
use local_treestudyplan\contextinfo;
|
||||
use local_treestudyplan\courseservice;
|
||||
use local_treestudyplan\studyplan;
|
||||
use local_treestudyplan\studyplanpage;
|
||||
use local_treestudyplan\premium;
|
||||
|
||||
|
@ -42,8 +40,8 @@ $context = $studyplan->context();
|
|||
$ci = new contextinfo($context);
|
||||
$contextname = $ci->pathstr();
|
||||
|
||||
$firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show
|
||||
$lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show
|
||||
$firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show.
|
||||
$lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show.
|
||||
|
||||
$PAGE->set_pagelayout('report');
|
||||
$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) {
|
||||
$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
|
||||
|
@ -81,30 +83,30 @@ print "<h3><b>{$contextname}</b> / {$studyplan->name()}</h3>";
|
|||
if ($studyplan->name() != $page->fullname()) {
|
||||
print "<h4>{$page->fullname()}</h4>";
|
||||
}
|
||||
?>
|
||||
print <<<END
|
||||
<div id='root'>
|
||||
<div class='vue-loader' v-show='false'>
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
<div class='spinner-border text-primary' role='status'>
|
||||
<span class='sr-only'>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-cloak>
|
||||
<div class='vue-loader' v-if="!structure">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
<div class='vue-loader' v-if='!structure'>
|
||||
<div class='spinner-border text-primary' role='status'>
|
||||
<span class='sr-only'>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class='container q-pageperiodselection'>
|
||||
<div class='row'>
|
||||
<div class="col-sm-2 q-label">{{text.page}}</div>
|
||||
<div class="col-sm-6">
|
||||
<select class="custom-select" :value="page.id" @change="selectedPage">
|
||||
<template v-if="studyplan">
|
||||
<option v-for="p in studyplan.pages"
|
||||
:key="p.id"
|
||||
:value="p.id"
|
||||
:selected="(page.id == p.id)?true:false"
|
||||
<div class='col-sm-2 q-label'>{{text.page}}</div>
|
||||
<div class='col-sm-6'>
|
||||
<select class='custom-select' :value='page.id' @change='selectedPage'>
|
||||
<template v-if='studyplan'>
|
||||
<option v-for='p in studyplan.pages'
|
||||
:key='p.id'
|
||||
:value='p.id'
|
||||
:selected='(page.id == p.id) ? true : false'
|
||||
>{{p.fullname}}</option>
|
||||
</template>
|
||||
<option v-else disabled>{{text.loading}}</option>
|
||||
|
@ -112,27 +114,27 @@ if ($studyplan->name() != $page->fullname()) {
|
|||
</div>
|
||||
</div>
|
||||
<div class='row'>
|
||||
<div class="col-sm-2 q-label" >{{text.period}} ({{text.from}} / {{text.to}})</div>
|
||||
<div class="col-sm-2">
|
||||
<select class="custom-select" @change="selectedFirstPeriod">
|
||||
<template v-if="page">
|
||||
<option v-for="p in page.perioddesc"
|
||||
:key="p.id"
|
||||
:value="p.period"
|
||||
:selected="(structure.firstperiod == p.period)?true:false"
|
||||
<div class='col-sm-2 q-label' >{{text.period}} ({{text.from}} / {{text.to}})</div>
|
||||
<div class='col-sm-2'>
|
||||
<select class='custom-select' @change='selectedFirstPeriod'>
|
||||
<template v-if='page'>
|
||||
<option v-for='p in page.perioddesc'
|
||||
:key='p.id'
|
||||
:value='p.period'
|
||||
:selected='(structure.firstperiod == p.period) ? true : false'
|
||||
>{{p.fullname}}</option>
|
||||
</template>
|
||||
<option v-else disabled>{{text.loading}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-2 ">
|
||||
<select class="custom-select" @change="selectedLastPeriod">
|
||||
<template v-if="page">
|
||||
<template v-for="p in page.perioddesc">
|
||||
<option v-if="p.period >= structure.firstperiod"
|
||||
:key="p.id"
|
||||
:value="p.period"
|
||||
:selected="(structure.lastperiod == p.period)?true:false"
|
||||
<div class='col-sm-2 '>
|
||||
<select class='custom-select' @change='selectedLastPeriod'>
|
||||
<template v-if='page'>
|
||||
<template v-for='p in page.perioddesc'>
|
||||
<option v-if='p.period >= structure.firstperiod'
|
||||
:key='p.id'
|
||||
:value='p.period'
|
||||
:selected='(structure.lastperiod == p.period) ? true : false'
|
||||
>{{p.fullname}}</option>
|
||||
</template>
|
||||
</template>
|
||||
|
@ -141,10 +143,10 @@ if ($studyplan->name() != $page->fullname()) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-studyplanreport :structure="structure" ></s-studyplanreport>
|
||||
<q-studyplanreport :structure='structure' ></s-studyplanreport>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
END;
|
||||
|
||||
print $OUTPUT->footer();
|
||||
|
|
29
settings.php
29
settings.php
|
@ -55,7 +55,7 @@ if ($hassiteconfig) {
|
|||
true,
|
||||
));
|
||||
|
||||
// Default image for study plans
|
||||
// Default image for study plans.
|
||||
$page->add(new admin_setting_configstoredfile('local_treestudyplan/defaulticon',
|
||||
get_string('setting_defaulticon', 'local_treestudyplan'),
|
||||
get_string('settingdesc_defaulticon', 'local_treestudyplan'),
|
||||
|
@ -89,8 +89,17 @@ if ($hassiteconfig) {
|
|||
get_string('settingdesc_display_heading', 'local_treestudyplan')
|
||||
));
|
||||
|
||||
$displayfields = ["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")];
|
||||
$displayfields = [
|
||||
"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');
|
||||
|
||||
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',
|
||||
get_string('setting_enableplansharing', 'local_treestudyplan'),
|
||||
get_string('settingdesc_enableplansharing', 'local_treestudyplan'),
|
||||
true,
|
||||
));
|
||||
|
||||
//get_config("local_treestudyplan", "timelessperiods")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/timelessperiods',
|
||||
get_string('setting_timelessperiods', 'local_treestudyplan'),
|
||||
get_string('settingdesc_timelessperiods', 'local_treestudyplan'),
|
||||
false,
|
||||
));
|
||||
|
||||
//get_config("local_treestudyplan", "limitcourselist")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/limitcourselist',
|
||||
get_string('setting_limitcourselist', 'local_treestudyplan'),
|
||||
get_string('settingdesc_limitcourselist', 'local_treestudyplan'),
|
||||
false,
|
||||
));
|
||||
|
||||
//get_config("local_treestudyplan", "hivizdropslots")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/hivizdropslots',
|
||||
get_string('setting_hivizdropslots', 'local_treestudyplan'),
|
||||
get_string('settingdesc_hivizdropslots', 'local_treestudyplan'),
|
||||
false,
|
||||
));
|
||||
|
||||
//get_config("local_treestudyplan", "toolboxleft")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/toolboxleft',
|
||||
get_string('setting_toolboxleft', 'local_treestudyplan'),
|
||||
get_string('settingdesc_toolboxleft', 'local_treestudyplan'),
|
||||
true,
|
||||
));
|
||||
|
||||
//get_config("local_treestudyplan", "copystudylinesnewpage")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/copystudylinesnewpage',
|
||||
get_string('setting_copystudylinesnewpage', 'local_treestudyplan'),
|
||||
get_string('settingdesc_copystudylinesnewpage', 'local_treestudyplan'),
|
||||
false,
|
||||
));
|
||||
|
||||
//get_config("local_treestudyplan", "continueperiodnumberingnewpage")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/continueperiodnumberingnewpage',
|
||||
get_string('setting_continueperiodnumberingnewpage', 'local_treestudyplan'),
|
||||
get_string('settingdesc_continueperiodnumberingnewpage', 'local_treestudyplan'),
|
||||
|
@ -156,7 +159,6 @@ if ($hassiteconfig) {
|
|||
));
|
||||
|
||||
if (premium::enabled()) {
|
||||
//get_config("local_treestudyplan", "enablecoach")
|
||||
$page->add(new admin_setting_configcheckbox('local_treestudyplan/enablecoach',
|
||||
get_string('setting_enablecoach', 'local_treestudyplan'),
|
||||
get_string('settingdesc_enablecoach', 'local_treestudyplan'),
|
||||
|
@ -186,7 +188,7 @@ if ($hassiteconfig) {
|
|||
"below" => get_string("infofield_position_below", 'local_treestudyplan'),
|
||||
];
|
||||
|
||||
for ($i=1;$i<=5;$i++) {
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$page->add(new admin_setting_configselect('local_treestudyplan/courseinfo'.$i.'_field',
|
||||
get_string('setting_infofield'.$i.'_field', 'local_treestudyplan'),
|
||||
get_string('settingdesc_infofield'.$i.'_field', 'local_treestudyplan'),
|
||||
|
@ -410,7 +412,7 @@ if ($hassiteconfig) {
|
|||
));
|
||||
|
||||
if ($CFG->debugdeveloper) {
|
||||
// show decrypted key data
|
||||
// Show decrypted key data.
|
||||
$pagepremium->add(new admin_setting_description('local_treestudyplan/premium_debug',
|
||||
get_string('setting_premium_debug', 'local_treestudyplan'),
|
||||
premium::debuginfo() . "<br> <br>" // Add empty row at end.
|
||||
|
@ -420,5 +422,4 @@ if ($hassiteconfig) {
|
|||
// Add settings page2 to the admin settings category.
|
||||
$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();
|
||||
|
||||
$PAGE->set_pagelayout('base');
|
||||
//$PAGE->set_context($studyplancontext);
|
||||
$PAGE->set_title(get_string('view_plan', 'local_treestudyplan')." - ".$contextname);
|
||||
$PAGE->set_heading(get_string('view_plan', 'local_treestudyplan')." - ".$contextname);
|
||||
|
||||
|
@ -73,19 +72,29 @@ if ($studyplancontext->id > 1) {
|
|||
navigation_node::override_active_url(new moodle_url('/course/index.php', ['categoryid' => $categoryid ]));
|
||||
$PAGE->navbar->add(get_string('view_plan', 'local_treestudyplan'));
|
||||
|
||||
// Coursecat context
|
||||
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later
|
||||
// Coursecat context.
|
||||
$cat = \core_course_category::get($studyplancontext->instanceid, IGNORE_MISSING, true); // We checck visibility later.
|
||||
} else {
|
||||
// System context
|
||||
// System context.
|
||||
$cat = \core_course_category::top();
|
||||
}
|
||||
|
||||
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)) {
|
||||
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.
|
||||
|
@ -108,7 +117,15 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
|||
}
|
||||
|
||||
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 class='vue-loader' v-show='false'>
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
|
@ -117,25 +134,26 @@ print $OUTPUT->header();
|
|||
</div>
|
||||
<div v-cloak>
|
||||
<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' : ''">
|
||||
<b-form-select-option v-if='!(usedcontexts.length)' :value="contextid"
|
||||
: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"
|
||||
:class="(ctx.studyplancount > 0) ? 'font-weight-bold' : ''"
|
||||
><span v-for="(p, i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span>
|
||||
<span>({{ ctx.studyplancount }})</span></b-form-select-option>
|
||||
</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>
|
||||
</div>
|
||||
<h3 v-else><?php print $contextname; ?></h3>
|
||||
<h3 v-else>{$contextname}</h3>
|
||||
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
||||
<template v-if="displayedstudyplan">
|
||||
<a href='#' @click.prevent='closeStudyplan'
|
||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
||||
<span><?php t("studyplan_select"); ?></span>
|
||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> {$text->back}</a>
|
||||
<span>{$text->studyplan_select}</span>
|
||||
<b-form-select lazy :text='dropdown_title' :value='displayedstudyplan.id'>
|
||||
<b-form-select-option
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
|
@ -164,7 +182,7 @@ print $OUTPUT->header();
|
|||
variant="primary"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -172,7 +190,7 @@ print $OUTPUT->header();
|
|||
<div class='t-studyplan-container'>
|
||||
<h2 v-if='displayedstudyplan&& selectedstudent'
|
||||
>{{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'
|
||||
v-model='displayedstudyplan' :teachermode='!selectedstudent'
|
||||
></r-studyplan>
|
||||
|
@ -180,7 +198,7 @@ print $OUTPUT->header();
|
|||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<div v-else class='t-studyplan-notselected'>
|
||||
<p><?php t("studyplan_noneselected"); ?></p>
|
||||
<p>{$text->studyplan_noneselected}</p>
|
||||
<b-card-group deck>
|
||||
<s-studyplan-card
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
|
@ -194,6 +212,6 @@ print $OUTPUT->header();
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
END;
|
||||
|
||||
print $OUTPUT->footer();
|
||||
|
|
Loading…
Reference in New Issue
Block a user