Implemented custom user-tops and did somestyling in the courses list

This commit is contained in:
PMKuipers 2024-02-03 23:50:58 +01:00
parent 29c8dd9aa4
commit 737da8c051
8 changed files with 193 additions and 13 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3821,14 +3821,15 @@ export default {
template: ` template: `
<li class="t-coursecat-list-item"> <li class="t-coursecat-list-item">
<span v-if="hasDetails" v-b-toggle="'coursecat-'+value.id"> <span v-if="hasDetails" v-b-toggle="'coursecat-'+value.id">
<i class="when-closed fa fa-caret-right"></i> <i class="when-closed fa fa-caret-right t-caret"></i>
<i class="when-open fa fa-caret-down"></i> <i class="when-open fa fa-caret-down t-caret"></i>
<span class="t-coursecat-heading"> <span class="t-coursecat-heading">
<i class="t-coursecat-list-item fa fa-tasks"></i> <i class="t-coursecat-list-item fa fa-tasks"></i>
{{ value.category.name }} {{ value.category.name }}
</span> </span>
</span> </span>
<span v-else> <span v-else>
<i class="when-closed fa t-caret" style="visibility: hidden"></i>
<span class="t-coursecat-heading"> <span class="t-coursecat-heading">
<i class="t-coursecat-list-item fa fa-tasks"></i> <i class="t-coursecat-list-item fa fa-tasks"></i>
{{ value.category.name }} {{ value.category.name }}

View File

@ -48,6 +48,87 @@ class courseservice extends \external_api {
const CAP_VIEW = "local/treestudyplan:viewuserreports"; const CAP_VIEW = "local/treestudyplan:viewuserreports";
/**
* 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.
* @param int $userid Id of the user
* @return array of core_course_category
*/
public static function user_tops($userid) {
global $DB;
$tops = [];
/*
SELECT UNIQUE ctx.* FROM mdl_context AS ctx
INNER JOIN mdl_role_assignments AS ra ON ra.contextid = ctx.id
INNER JOIN mdl_role_capabilities AS rc ON ra.roleid = rc.roleid
LEFT JOIN mdl_course_categories AS cat ON ctx.instanceid = cat.id
WHERE ( ctx.contextlevel = 40 OR ctx.contextlevel = 10 )
AND ra.userid = 58 AND rc.capability = 'moodle/category:viewcourselist'
ORDER BY ctx.depth ASC, cat.sortorder ASC;
*/
$capability = 'moodle/category:viewcourselist';
$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
WHERE ( ctx.contextlevel = :ctxl_coursecat OR ctx.contextlevel = :ctxl_system )
AND ra.userid = :userid AND rc.capability = :capability
ORDER BY ctx.depth ASC, cat.sortorder ASC";
// Use recordset to handle the eventuality of a really big and complex moodle setup.
$recordset = $DB->get_recordset_sql($sql, ["userid" => $userid, "capability" => $capability,
"ctxl_coursecat" => \CONTEXT_COURSECAT, "ctxl_system" => \CONTEXT_SYSTEM,]);
$contextids = [];
foreach ($recordset as $r) {
// Get the paths as an array.
$parents = explode("/",$r->path);
// Strip the first item, since it is an empty one.
array_shift($parents);
// Strip the last item, since it refers to self.
array_pop($parents);
// Figure out if any of the remaining parent contexts are already contexts with permission.
$intersect = array_intersect($contextids,$parents);
if (count($intersect) == 0) {
// Double check permissions according to the moodle capability system.
$ctx = \context::instance_by_id($r->id);
if (has_capability($capability,$ctx,$userid)) {
// Get the actual category object.
if ($r->contextlevel == \CONTEXT_SYSTEM) {
// The user can view all (non-hidden) categories, so add all categories of depth = 1;
$tops = []; // Reset the array, just in case.
$rs = $DB->get_recordset("course_categories",["depth" => 1],'sortorder');
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);
if ($cat !== null) {
// Register the category.
array_push($tops,$cat);
}
}
$rs->close();
break; // Stop the loop immediately so the list of visible depth 2 categories is returned.
} else { // Can only be \CONTEXT_COURSECAT according to the SQL query.
// 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($r->instanceid, \IGNORE_MISSING, false, $userid);
if ($cat !== null) {
// Register the context id in the list now, since we know the category is really visible.
array_push($contextids,$r->id);
// Register the category.
array_push($tops,$cat);
}
}
}
}
}
$recordset->close();
return $tops;
}
/** /**
* Return value description for map_categories function * Return value description for map_categories function
*/ */
@ -93,7 +174,7 @@ class courseservice extends \external_api {
* @return array * @return array
*/ */
public static function map_categories($rootid = 0) { public static function map_categories($rootid = 0) {
global $CFG, $DB; global $USER;
$root = \core_course_category::get($rootid,\MUST_EXIST,true); $root = \core_course_category::get($rootid,\MUST_EXIST,true);
@ -101,15 +182,12 @@ class courseservice extends \external_api {
if ($root->id == 0) { if ($root->id == 0) {
// On the system level, determine the user's topmost allowed catecories. // On the system level, determine the user's topmost allowed catecories.
$usertop = \core_course_category::user_top(); // This uses a custom function, since moodle's "core_course_category::user_top()" is somewhat deficient.
if ($usertop->id == 0) { $children = self::user_tops($USER->id);
// Top category..
$children = $root->get_children(); // Returns a list of çore_course_category, let it overwrite $children.
} else {
$children = [$usertop];
}
} else if ($root->is_uservisible()) { } else if ($root->is_uservisible()) {
$children = [$root]; $children = [$root];
} else {
return []; // Category not user visible.
} }
$list = []; $list = [];

View File

@ -121,7 +121,6 @@ class studyplanservice extends \external_api {
$studyplan = studyplan::find_by_id($id); $studyplan = studyplan::find_by_id($id);
webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $studyplan->context()); webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $studyplan->context());
$model = $studyplan->editor_model(); $model = $studyplan->editor_model();
debug::dump($model);
return $model; return $model;
} else { } else {
return null; return null;

View File

@ -264,6 +264,22 @@
stroke-opacity: 0.5; stroke-opacity: 0.5;
stroke-dasharray: 4 4; stroke-dasharray: 4 4;
} }
.path-local-treestudyplan ul.t-coursecat-list,
.path-local-treestudyplan ul.t-course-list,
.features-treestudyplan ul.t-coursecat-list,
.features-treestudyplan ul.t-course-list {
padding-left: 1em;
}
.path-local-treestudyplan ul.t-coursecat-list ul.t-coursecat-list,
.path-local-treestudyplan ul.t-coursecat-list ul.t-course-list,
.path-local-treestudyplan ul.t-course-list ul.t-coursecat-list,
.path-local-treestudyplan ul.t-course-list ul.t-course-list,
.features-treestudyplan ul.t-coursecat-list ul.t-coursecat-list,
.features-treestudyplan ul.t-coursecat-list ul.t-course-list,
.features-treestudyplan ul.t-course-list ul.t-coursecat-list,
.features-treestudyplan ul.t-course-list ul.t-course-list {
padding-left: 1.5em;
}
.path-local-treestudyplan ul.t-item-module-children, .path-local-treestudyplan ul.t-item-module-children,
.path-local-treestudyplan ul.t-coursecat-list li, .path-local-treestudyplan ul.t-coursecat-list li,
.path-local-treestudyplan ul.t-course-list li, .path-local-treestudyplan ul.t-course-list li,
@ -293,6 +309,26 @@
max-width: 24px; max-width: 24px;
max-height: 24px; max-height: 24px;
} }
.path-local-treestudyplan li.t-coursecat-list-item i.t-caret,
.features-treestudyplan li.t-coursecat-list-item i.t-caret {
width: 9px;
}
.path-local-treestudyplan li.t-coursecat-list-item,
.path-local-treestudyplan li.t-course-list-item .draggable-course,
.features-treestudyplan li.t-coursecat-list-item,
.features-treestudyplan li.t-course-list-item .draggable-course {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.path-local-treestudyplan li.t-coursecat-list-item:hover,
.path-local-treestudyplan li.t-course-list-item .draggable-course:hover,
.features-treestudyplan li.t-coursecat-list-item:hover,
.features-treestudyplan li.t-course-list-item .draggable-course:hover {
text-overflow: clip;
white-space: normal;
word-break: break-word;
}
.path-local-treestudyplan i.t-coursecat-list-item, .path-local-treestudyplan i.t-coursecat-list-item,
.features-treestudyplan i.t-coursecat-list-item { .features-treestudyplan i.t-coursecat-list-item {
color: var(--coursecat-list); color: var(--coursecat-list);

View File

@ -169,6 +169,16 @@
stroke-dasharray: 4 4; stroke-dasharray: 4 4;
} }
ul.t-coursecat-list,
ul.t-course-list {
padding-left: 1em;
ul.t-coursecat-list,
ul.t-course-list {
padding-left: 1.5em;
}
}
ul.t-item-module-children, ul.t-item-module-children,
ul.t-coursecat-list li, ul.t-coursecat-list li,
ul.t-course-list li { ul.t-course-list li {
@ -195,6 +205,26 @@
max-height: 24px; max-height: 24px;
} }
li.t-coursecat-list-item {
i.t-caret {
width: 9px;
}
}
li.t-coursecat-list-item, li.t-course-list-item .draggable-course {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
text-overflow: clip;
white-space: normal;
word-break: break-word;
}
}
i.t-coursecat-list-item { i.t-coursecat-list-item {
color: var(--coursecat-list); color: var(--coursecat-list);
} }

View File

@ -264,6 +264,22 @@
stroke-opacity: 0.5; stroke-opacity: 0.5;
stroke-dasharray: 4 4; stroke-dasharray: 4 4;
} }
.path-local-treestudyplan ul.t-coursecat-list,
.path-local-treestudyplan ul.t-course-list,
.features-treestudyplan ul.t-coursecat-list,
.features-treestudyplan ul.t-course-list {
padding-left: 1em;
}
.path-local-treestudyplan ul.t-coursecat-list ul.t-coursecat-list,
.path-local-treestudyplan ul.t-coursecat-list ul.t-course-list,
.path-local-treestudyplan ul.t-course-list ul.t-coursecat-list,
.path-local-treestudyplan ul.t-course-list ul.t-course-list,
.features-treestudyplan ul.t-coursecat-list ul.t-coursecat-list,
.features-treestudyplan ul.t-coursecat-list ul.t-course-list,
.features-treestudyplan ul.t-course-list ul.t-coursecat-list,
.features-treestudyplan ul.t-course-list ul.t-course-list {
padding-left: 1.5em;
}
.path-local-treestudyplan ul.t-item-module-children, .path-local-treestudyplan ul.t-item-module-children,
.path-local-treestudyplan ul.t-coursecat-list li, .path-local-treestudyplan ul.t-coursecat-list li,
.path-local-treestudyplan ul.t-course-list li, .path-local-treestudyplan ul.t-course-list li,
@ -293,6 +309,26 @@
max-width: 24px; max-width: 24px;
max-height: 24px; max-height: 24px;
} }
.path-local-treestudyplan li.t-coursecat-list-item i.t-caret,
.features-treestudyplan li.t-coursecat-list-item i.t-caret {
width: 9px;
}
.path-local-treestudyplan li.t-coursecat-list-item,
.path-local-treestudyplan li.t-course-list-item .draggable-course,
.features-treestudyplan li.t-coursecat-list-item,
.features-treestudyplan li.t-course-list-item .draggable-course {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.path-local-treestudyplan li.t-coursecat-list-item:hover,
.path-local-treestudyplan li.t-course-list-item .draggable-course:hover,
.features-treestudyplan li.t-coursecat-list-item:hover,
.features-treestudyplan li.t-course-list-item .draggable-course:hover {
text-overflow: clip;
white-space: normal;
word-break: break-word;
}
.path-local-treestudyplan i.t-coursecat-list-item, .path-local-treestudyplan i.t-coursecat-list-item,
.features-treestudyplan i.t-coursecat-list-item { .features-treestudyplan i.t-coursecat-list-item {
color: var(--coursecat-list); color: var(--coursecat-list);