added coach association to studyplan
This commit is contained in:
parent
a57ee3d884
commit
c480c20098
2
amd/build/report-viewer-components.min.js
vendored
2
amd/build/report-viewer-components.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
amd/build/studyplan-editor-components.min.js
vendored
2
amd/build/studyplan-editor-components.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -19,6 +19,8 @@ import {ProcessStudyplan, ProcessStudyplanPage, objCopy} from './studyplan-proce
|
|||
import TSComponents from './treestudyplan-components';
|
||||
import {eventTypes as editSwEventTypes} from 'core/edit_switch';
|
||||
import { premiumenabled, premiumstatus } from "./util/premium";
|
||||
import FitTextVue from './util/fittext-vue';
|
||||
|
||||
|
||||
// Make π available as a constant
|
||||
const π = Math.PI;
|
||||
|
@ -53,7 +55,9 @@ const ENROLLABLE_SELF_ROLE = 3;
|
|||
export default {
|
||||
install(Vue/*,options*/){
|
||||
Vue.use(TSComponents);
|
||||
Vue.use(FitTextVue);
|
||||
let debug = new Debugger("treestudyplan-viewer");
|
||||
|
||||
let lastCaller = null;
|
||||
/**
|
||||
* Scroll current period into view
|
||||
|
@ -972,6 +976,8 @@ export default {
|
|||
},
|
||||
load_students() {
|
||||
const self=this;
|
||||
self.students=null;
|
||||
self.can_unenrol=false;
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_list_line_enrolled_students',
|
||||
args: { id: self.value.id },
|
||||
|
@ -995,9 +1001,10 @@ export default {
|
|||
:data-studyline="value.id" ref="mainEl"
|
||||
><div class="r-studyline-handle" :style="'background-color: ' + value.color"></div>
|
||||
<div class="r-studyline-title"><div>
|
||||
<abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr><br>
|
||||
<abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr>
|
||||
<template v-if="premiumenabled() && enrollable">
|
||||
<template v-if="teachermode">
|
||||
<br>
|
||||
<a v-if="!can_enrol"
|
||||
href='#' @click.prevent=""
|
||||
v-b-modal="'r-enrollments-'+value.id"
|
||||
|
@ -1060,9 +1067,6 @@ export default {
|
|||
>{{format_datetime(student.enrol.enrolled_time)}}</span></td>
|
||||
<td><span v-if="student.enrol.enrolled"
|
||||
>{{student.enrol.enrolled_by}}</span></td>
|
||||
<td v-if="can_enrol"><b-button variant="success"
|
||||
@click="enrol_student(student.user)"
|
||||
>{{text.enrol}}</b-button></td>
|
||||
<td ><b-button v-if="!student.enrol.enrolled && (can_enrol || can_unenrol)"
|
||||
variant="success"
|
||||
size="sm"
|
||||
|
@ -1080,22 +1084,26 @@ export default {
|
|||
</table>
|
||||
</b-modal>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-else-if="value.enrol.selfview">
|
||||
<br>
|
||||
<a v-if="!enrolled && !can_enrol"
|
||||
href='#' @click.prevent=""
|
||||
@click.prevent=""
|
||||
href='#'
|
||||
v-b-tooltip.focus
|
||||
:title="text.cannot_enrol"
|
||||
><i class='fa fa-lock text-danger'></i> {{text.cannot_enrol}}</a>
|
||||
><fittext maxsize="12pt"><i class='fa fa-lock text-danger'></i> {{text.cannot_enrol}}</fittext></a>
|
||||
<a v-else-if="!enrolled && can_enrol"
|
||||
href='#' @click.prevent=""
|
||||
@click.prevent=""
|
||||
href='#'
|
||||
v-b-modal="'r-enrol-'+value.id"
|
||||
:title="text.can_enrol"
|
||||
><i class='fa fa-unlock-alt text-info'></i> {{text.enrol}}</a>
|
||||
><fittext maxsize="12pt"><i class='fa fa-unlock-alt text-info'></i> {{text.enrol}}</fittext></a>
|
||||
<a v-else-if="enrolled"
|
||||
href='#' @click.prevent=""
|
||||
@click.prevent=""
|
||||
href='#'
|
||||
v-b-modal="'r-enrollment-'+value.id"
|
||||
:title="text.enrolled"
|
||||
><i class='fa fa-unlock text-success'></i> {{text.enrolled}}</a>
|
||||
><fittext maxsize="12pt"><i class='fa fa-unlock text-success'></i> {{text.enrolled}}</fittext></a>
|
||||
<b-modal
|
||||
:id="'r-enrol-'+value.id"
|
||||
:title="text.confirm"
|
||||
|
@ -1117,6 +1125,11 @@ export default {
|
|||
<b>{{by}}</b> {{this.value.enrol.enrolled_by}}</p>
|
||||
</b-modal>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
||||
<i v-if="!enrolled" class='fa fa-lock text-danger'></i>
|
||||
<i v-else class='fa fa-unlock text-success'></i>
|
||||
</template>
|
||||
</template>
|
||||
</div></div>
|
||||
</div>
|
||||
|
|
|
@ -200,14 +200,17 @@ export default {
|
|||
associations: 'associations',
|
||||
associated_cohorts: 'associated_cohorts',
|
||||
associated_users: 'associated_users',
|
||||
associated_coaches: 'associated_coaches',
|
||||
associate_cohorts: 'associate_cohorts',
|
||||
associate_users: 'associate_users',
|
||||
associate_coached: 'associate_coaches',
|
||||
add_association: 'add_association',
|
||||
delete_association: 'delete_association',
|
||||
associations_empty: 'associations_empty',
|
||||
associations_search: 'associations_search',
|
||||
cohorts: 'cohorts',
|
||||
users: 'users',
|
||||
coaches: 'coaches',
|
||||
selected: 'selected',
|
||||
name: 'name',
|
||||
context: 'context',
|
||||
|
@ -762,15 +765,17 @@ export default {
|
|||
association: {
|
||||
cohorts: [],
|
||||
users: [],
|
||||
coaches: []
|
||||
},
|
||||
loading: {
|
||||
cohorts: false,
|
||||
users: false,
|
||||
coaches: false,
|
||||
},
|
||||
search: {users: [], cohorts:[]},
|
||||
search: {users: [], cohorts:[], coaches:[]},
|
||||
selected: {
|
||||
search: {users: [] , cohorts:[]},
|
||||
associated: {users: [] , cohorts:[]}
|
||||
search: {users: [] , cohorts:[], coaches: []},
|
||||
associated: {users: [] , cohorts:[], coaches: []}
|
||||
},
|
||||
text: strings.studyplan_associate,
|
||||
};
|
||||
|
@ -784,6 +789,7 @@ export default {
|
|||
|
||||
},
|
||||
methods: {
|
||||
premiumenabled,
|
||||
showModal(){
|
||||
this.show = true;
|
||||
this.loadAssociations();
|
||||
|
@ -819,6 +825,17 @@ export default {
|
|||
self.association.cohorts = response.map(self.cohortOptionModel);
|
||||
self.loading.cohorts = false;
|
||||
}).catch(notification.exception);
|
||||
|
||||
if(premiumenabled()) {
|
||||
self.loading.coaches = true;
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_associated_coaches',
|
||||
args: { studyplan_id: self.value.id,}
|
||||
}])[0].then(function(response){
|
||||
self.association.coaches = response.map(self.userOptionModel);
|
||||
self.loading.coaches = false;
|
||||
}).catch(notification.exception);
|
||||
}
|
||||
},
|
||||
searchCohorts(searchtext){
|
||||
const self = this;
|
||||
|
@ -937,6 +954,71 @@ export default {
|
|||
}
|
||||
call(requests);
|
||||
},
|
||||
searchCoaches(searchtext){
|
||||
if(premiumenabled()){
|
||||
const self = this;
|
||||
if(searchtext.length > 0)
|
||||
{
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_find_coach',
|
||||
args: { like: searchtext, exclude_id: self.value.id}
|
||||
}])[0].then(function(response){
|
||||
self.search.coaches = response.map(self.userOptionModel);
|
||||
}).catch(notification.exception);
|
||||
}
|
||||
else {
|
||||
self.search.coaches = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
coachAssociate(){
|
||||
if(premiumenabled()){
|
||||
const self = this;
|
||||
let requests = [];
|
||||
const associated = self.association.coaches;
|
||||
const search = self.search.coaches;
|
||||
const searchselected = self.selected.search.coaches;
|
||||
for(const i in searchselected){
|
||||
const r = searchselected[i];
|
||||
|
||||
requests.push({
|
||||
methodname: 'local_treestudyplan_connect_coach',
|
||||
args: {studyplan_id: self.value.id, user_id: r},
|
||||
fail: notification.exception,
|
||||
done: function(response){
|
||||
if(response.success){
|
||||
transportItem(associated,search,r);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
call(requests);
|
||||
}
|
||||
},
|
||||
coachDisassociate(){
|
||||
if(premiumenabled()){
|
||||
const self = this;
|
||||
let requests = [];
|
||||
const associated = self.association.coaches;
|
||||
const associatedselected = self.selected.associated.coaches;
|
||||
const search = self.search.coaches;
|
||||
for(const i in associatedselected){
|
||||
const r = associatedselected[i];
|
||||
|
||||
requests.push({
|
||||
methodname: 'local_treestudyplan_disconnect_coach',
|
||||
args: {studyplan_id: self.value.id, user_id: r},
|
||||
fail: notification.exception,
|
||||
done: function(response){
|
||||
if(response.success){
|
||||
transportItem(search,associated,r);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
call(requests);
|
||||
}
|
||||
},
|
||||
}
|
||||
,
|
||||
template:
|
||||
|
@ -1041,6 +1123,52 @@ export default {
|
|||
</b-row>
|
||||
</b-container>
|
||||
</b-tab>
|
||||
<b-tab :title="text.coaches">
|
||||
<b-container>
|
||||
<b-row class='mb-2 mt-2'>
|
||||
<b-col>{{text.associated_coaches}}</b-col>
|
||||
<b-col>{{text.associate_coaches}}</b-col>
|
||||
</b-row>
|
||||
<b-row class='mb-2'>
|
||||
<b-col>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-form-input
|
||||
type="text"
|
||||
@input="searchCoaches($event)"
|
||||
placeholder="Search coaches"></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-form-select
|
||||
multiple
|
||||
v-model="selected.associated.coaches"
|
||||
:options="association.coaches"
|
||||
:select-size="10"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-form-select
|
||||
multiple
|
||||
v-model="selected.search.coaches"
|
||||
:options="search.coaches"
|
||||
:select-size="10"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class='mt-2'>
|
||||
<b-col>
|
||||
<b-button variant='danger' @click.prevent="coachDisassociate()"
|
||||
><i class='fa fa-chain-broken'></i> {{text.delete_association}}</b-button>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-button variant='success' @click.prevent="coachAssociate()"
|
||||
><i class='fa fa-link'></i> {{text.add_association}}</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</b-modal>
|
||||
</span>
|
||||
|
|
|
@ -42,7 +42,11 @@ class associationservice extends \external_api {
|
|||
* @var string
|
||||
*/
|
||||
const CAP_VIEW = "local/treestudyplan:viewuserreports";
|
||||
|
||||
/**
|
||||
* Capability required to be linked as coach to a studyplan
|
||||
* @var string
|
||||
*/
|
||||
const CAP_COACH = "local/treestudyplan:coach";
|
||||
/**
|
||||
* Webservice structure to use in describing a user
|
||||
*/
|
||||
|
@ -705,4 +709,198 @@ class associationservice extends \external_api {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parameter description for webservice function connect_user
|
||||
*/
|
||||
public static function connect_coach_parameters() : \external_function_parameters {
|
||||
return new \external_function_parameters( [
|
||||
"studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
|
||||
"user_id" => new \external_value(PARAM_INT, 'id of user to link', VALUE_OPTIONAL),
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return value description for webservice function connect_user
|
||||
*/
|
||||
public static function connect_coach_returns() : \external_description {
|
||||
return new \external_single_structure([
|
||||
"success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
|
||||
"msg" => new \external_value(PARAM_TEXT, 'message'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a user to a studyplan
|
||||
* @param mixed $studyplanid Id of studyplan
|
||||
* @param mixed $userid Id of user
|
||||
* @return array Success/fail model
|
||||
*/
|
||||
public static function connect_coach($studyplanid, $userid) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$studyplan = studyplan::find_by_id($studyplanid);
|
||||
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 (!$DB->record_exists('local_treestudyplan_coach', ['studyplan_id' => $studyplanid, 'user_id' => $userid])) {
|
||||
$id = $DB->insert_record('local_treestudyplan_coach', [
|
||||
'studyplan_id' => $studyplanid,
|
||||
'user_id' => $userid,
|
||||
]);
|
||||
|
||||
return ['success' => true, 'msg' => 'User connected as coach'];
|
||||
|
||||
} else {
|
||||
return ['success' => true, 'msg' => 'User already connected as coach'];
|
||||
}
|
||||
} else {
|
||||
return ['success' => false, 'msg' => 'User does not have coach capability in this studyplan\'s context'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for webservice function disconnect_user
|
||||
*/
|
||||
public static function disconnect_coach_parameters() : \external_function_parameters {
|
||||
return new \external_function_parameters( [
|
||||
"studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
|
||||
"user_id" => new \external_value(PARAM_INT, 'id of user to link', VALUE_OPTIONAL),
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return value description for webservice function disconnect_user
|
||||
*/
|
||||
public static function disconnect_coach_returns() : \external_description {
|
||||
return new \external_single_structure([
|
||||
"success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
|
||||
"msg" => new \external_value(PARAM_TEXT, 'message'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect a user from a studyplan
|
||||
* @param mixed $studyplanid Id of studyplan
|
||||
* @param mixed $userid Id of user
|
||||
* @return array Success/fail model
|
||||
*/
|
||||
public static function disconnect_coach($studyplanid, $userid) {
|
||||
global $CFG, $DB;
|
||||
$studyplan = studyplan::find_by_id($studyplanid);
|
||||
webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
|
||||
|
||||
if ($DB->record_exists('local_treestudyplan_coach', ['studyplan_id' => $studyplanid, 'user_id' => $userid])) {
|
||||
$DB->delete_records('local_treestudyplan_coach', [
|
||||
'studyplan_id' => $studyplanid,
|
||||
'user_id' => $userid,
|
||||
]);
|
||||
|
||||
|
||||
return ['success' => true, 'msg' => 'User Disconnected as coach'];
|
||||
} else {
|
||||
return ['success' => true, 'msg' => 'Connection does not exist'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parameter description for webservice function find_user
|
||||
*/
|
||||
public static function find_coach_parameters() : \external_function_parameters {
|
||||
return new \external_function_parameters( [
|
||||
'like' => new \external_value(PARAM_TEXT, 'search text'),
|
||||
'exclude_id' => new \external_value(PARAM_INT, 'exclude members of this studyplan', VALUE_OPTIONAL),
|
||||
'context_id' => new \external_value(PARAM_INT, 'context for this request', VALUE_OPTIONAL),
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return value description for webservice function find_user
|
||||
*/
|
||||
public static function find_coach_returns() : \external_description {
|
||||
return new \external_multiple_structure(self::user_structure());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search users for match
|
||||
* @param string $like String to match user firstname/lastname with
|
||||
* @param null $excludeid Do not include coaches connected to this studyplan id
|
||||
* @param int $contextid Context to search (default system)
|
||||
* @return array
|
||||
*/
|
||||
public static function find_coach($like, $excludeid = null, $contextid = 1) {
|
||||
global $CFG, $DB;
|
||||
|
||||
|
||||
// Only allow this if the user has the right to edit in this context.
|
||||
$context = webservicehelper::find_context($contextid);
|
||||
webservicehelper::require_capabilities(self::CAP_EDIT, $context);
|
||||
|
||||
$pattern = "%{$like}%";
|
||||
$params = ["pattern_fn" => $pattern,
|
||||
"pattern_ln" => $pattern,
|
||||
"pattern_un" => $pattern,
|
||||
];
|
||||
$sql = "SELECT DISTINCT u.* FROM {user} u LEFT JOIN {local_treestudyplan_coach} j ON u.id = j.user_id
|
||||
WHERE u.deleted != 1 AND (firstname LIKE :pattern_fn OR lastname LIKE :pattern_ln OR username LIKE :pattern_un)";
|
||||
if (isset($excludeid) && is_numeric($excludeid)) {
|
||||
$sql .= " AND (j.studyplan_id IS NULL OR j.studyplan_id != :exclude_id)";
|
||||
$params['exclude_id'] = $excludeid;
|
||||
}
|
||||
|
||||
$users = [];
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($rs as $r) {
|
||||
if (has_capability(self::CAP_COACH,$context,$r)) {
|
||||
$users[] = static::make_user_model($r);
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
self::sortusermodels($users);
|
||||
return $users;
|
||||
}
|
||||
/**
|
||||
* Parameter description for webservice function associated_users
|
||||
*/
|
||||
public static function associated_coaches_parameters() : \external_function_parameters {
|
||||
return new \external_function_parameters( [
|
||||
"studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return value description for webservice function associated_users
|
||||
*/
|
||||
public static function associated_coaches_returns() : \external_description {
|
||||
return new \external_multiple_structure(self::user_structure());
|
||||
}
|
||||
|
||||
/**
|
||||
* List all users associated to a studyplan
|
||||
* @param mixed $studyplanid Id of studyplan
|
||||
* @return array
|
||||
*/
|
||||
public static function associated_coaches($studyplanid) {
|
||||
global $CFG, $DB;
|
||||
$studyplan = studyplan::find_by_id($studyplanid);
|
||||
webservicehelper::require_capabilities(self::CAP_VIEW, $studyplan->context());
|
||||
|
||||
$sql = "SELECT DISTINCT u.* FROM {user} u INNER JOIN {local_treestudyplan_coach} j ON j.user_id = u.id
|
||||
WHERE j.studyplan_id = :studyplan_id
|
||||
ORDER BY u.lastname, u.firstname";
|
||||
$rs = $DB->get_recordset_sql($sql, ['studyplan_id' => $studyplanid]);
|
||||
|
||||
$users = [];
|
||||
foreach ($rs as $u) {
|
||||
$user = self::make_user_model($u);
|
||||
$users[] = $user;
|
||||
}
|
||||
$rs->close();
|
||||
self::sortusermodels($users);
|
||||
return $users;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -325,6 +325,7 @@ class studyline {
|
|||
"enrolled" => new \external_value(PARAM_BOOL, 'student is enrolled',VALUE_OPTIONAL),
|
||||
"enrolled_time" => new \external_value(PARAM_INT, 'moment of enrollment',VALUE_OPTIONAL),
|
||||
"enrolled_by" => new \external_value(PARAM_TEXT, 'Name of enrolling user',VALUE_OPTIONAL),
|
||||
"selfview" => new \external_value(PARAM_BOOL, 'viewing user is the student',VALUE_OPTIONAL),
|
||||
],"Enrollment info",$value);
|
||||
}
|
||||
|
||||
|
@ -597,6 +598,7 @@ class studyline {
|
|||
"enrolled" => $enrolled,
|
||||
"enrolled_time" => $enrolled_time,
|
||||
"enrolled_by" => $enrolled_by,
|
||||
"selfview" => boolval($userid == $USER->id),
|
||||
];
|
||||
$model = array_merge($model,$usermodel);
|
||||
} else {
|
||||
|
|
201
coach.php
Normal file
201
coach.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?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/>.
|
||||
/**
|
||||
* View study plans - teacher view and student view
|
||||
* @package local_treestudyplan
|
||||
* @copyright 2023 P.M. Kuipers
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
|
||||
use local_treestudyplan\contextinfo;
|
||||
use \local_treestudyplan\courseservice;
|
||||
|
||||
require_once($CFG->libdir.'/weblib.php');
|
||||
|
||||
$systemcontext = context_system::instance();
|
||||
|
||||
$PAGE->set_url("/local/treestudyplan/view-plan.php", array());
|
||||
require_login();
|
||||
|
||||
// Figure out the context (category or system, based on either category or context parameter).
|
||||
$categoryid = optional_param('categoryid', 0, PARAM_INT); // Category id.
|
||||
$contextid = optional_param('contextid', 0, PARAM_INT); // Context id.
|
||||
if ($categoryid > 0) {
|
||||
$studyplancontext = context_coursecat::instance($categoryid);
|
||||
|
||||
} else if ($contextid > 0) {
|
||||
$studyplancontext = context::instance_by_id($contextid);
|
||||
if (in_array($studyplancontext->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT])) {
|
||||
$categoryid = $studyplancontext->instanceid;
|
||||
} else {
|
||||
$studyplancontext = $systemcontext;
|
||||
}
|
||||
} else {
|
||||
// If no context is selected, find the first available one.
|
||||
$availablecontexts = courseservice::list_available_categories("view");
|
||||
$contextid = 1; // Fallback to system context.
|
||||
foreach ($availablecontexts as $ctx) {
|
||||
if ($ctx["studyplancount"] > 0) {
|
||||
$contextid = $ctx["context_id"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Reload page with selected category.
|
||||
$url = new \moodle_url('/local/treestudyplan/view-plan.php', ["contextid" => $contextid]);
|
||||
header('Location: '.$url->out(false), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
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
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
if(!has_capability('local/treestudyplan:viewuserreports', $studyplancontext)) {
|
||||
throw new \moodle_exception("error:nostudyplanviewaccess","local_treestudyplan","/local/treestudyplan/view_plan.php",$contextname);
|
||||
}
|
||||
|
||||
// Load javascripts and specific css.
|
||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
|
||||
if ($CFG->debugdeveloper) {
|
||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
|
||||
}
|
||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-view-plan', 'init', [$studyplancontext->id, $categoryid]);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return string Translation of key
|
||||
*/
|
||||
function t($str, $param = null, $plugin = 'local_treestudyplan') {
|
||||
print get_string($str, $plugin, $param);
|
||||
}
|
||||
|
||||
print $OUTPUT->header();
|
||||
?>
|
||||
<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>
|
||||
</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'
|
||||
: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>
|
||||
<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;"
|
||||
class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
<h3 v-else><?php print $contextname; ?></h3>
|
||||
<div class="m-buttonbar" style="margin-bottom: 1em;">
|
||||
<a href='#' v-if='displayedstudyplan' @click.prevent='closeStudyplan'
|
||||
><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
|
||||
<span v-if='displayedstudyplan'><?php t("studyplan_select"); ?></span>
|
||||
<b-form-select v-if='displayedstudyplan' lazy :text='dropdown_title' :value='displayedstudyplan.id'>
|
||||
<b-form-select-option
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
:key='studyplan.id'
|
||||
@click='selectStudyplan(studyplan)'
|
||||
:value='studyplan.id'
|
||||
>{{ studyplan.name }}</b-form-select-option>
|
||||
</b-form-select>
|
||||
<s-studyplan-details
|
||||
v-model="displayedstudyplan"
|
||||
v-if="displayedstudyplan && displayedstudyplan.description"
|
||||
></s-studyplan-details>
|
||||
<div class="flex-grow-1"><!-- Spacer to align student selector right --></div>
|
||||
<div v-if="displayedstudyplan && displayedstudyplan.description">
|
||||
<span><?php t('selectstudent_btn') ?></span>
|
||||
<s-prevnext-selector
|
||||
:options="associatedstudents"
|
||||
title="firstname"
|
||||
v-model="selectedstudent"
|
||||
defaultselectable
|
||||
grouped
|
||||
optionsfield='users'
|
||||
arrows
|
||||
@change="showStudentView"
|
||||
class="ml-2"
|
||||
variant="primary"
|
||||
>
|
||||
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
|
||||
<template #defaultlabel><span class='text-primary'><?php t("showoverview"); ?></span></template>
|
||||
</s-prevnext-selector>
|
||||
</div>
|
||||
|
||||
</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>
|
||||
<r-studyplan v-if='!loadingstudyplan && displayedstudyplan'
|
||||
v-model='displayedstudyplan' :teachermode='!selectedstudent'
|
||||
></r-studyplan>
|
||||
<div v-else-if='loadingstudyplan' class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<div v-else class='t-studyplan-notselected'>
|
||||
<p><?php t("studyplan_noneselected"); ?></p>
|
||||
<b-card-group deck>
|
||||
<s-studyplan-card
|
||||
v-for='(studyplan, planindex) in studyplans'
|
||||
:key='studyplan.id'
|
||||
v-model='studyplans[planindex]'
|
||||
open
|
||||
@open='selectStudyplan(studyplan)'
|
||||
></s-studyplan-card>
|
||||
</b-card-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
print $OUTPUT->footer();
|
|
@ -77,4 +77,13 @@ $capabilities = [
|
|||
),
|
||||
],
|
||||
|
||||
'local/treestudyplan:coach' => [
|
||||
'riskbitmask' => RISK_PERSONAL ,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="local/treestudyplan/db" VERSION="20240225" COMMENT="XMLDB file for Moodle local/treestudyplan"
|
||||
<XMLDB PATH="local/treestudyplan/db" VERSION="20240308" COMMENT="XMLDB file for Moodle local/treestudyplan"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
|
@ -196,5 +196,17 @@
|
|||
<KEY NAME="enrolledby-id" TYPE="foreign" FIELDS="enrolledby" REFTABLE="user" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="local_treestudyplan_coach" COMMENT="Default comment for the table, please edit me">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="user_id" TYPE="int" LENGTH="18" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="studyplan_id" TYPE="int" LENGTH="18" NOTNULL="true" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="user_id-id" TYPE="foreign" FIELDS="user_id" REFTABLE="user" REFFIELDS="id"/>
|
||||
<KEY NAME="studyplan_id-id" TYPE="foreign" FIELDS="studyplan_id" REFTABLE="local_treestudyplan" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
</XMLDB>
|
||||
|
|
|
@ -277,7 +277,6 @@ $functions = [
|
|||
'capabilities' => 'local/treestudyplan:editstudyplan',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
|
||||
'local_treestudyplan_connect_user' => [ // Web service function name.
|
||||
'classname' => '\local_treestudyplan\associationservice', // Class containing the external function.
|
||||
'methodname' => 'connect_user', // External function name.
|
||||
|
@ -314,6 +313,42 @@ $functions = [
|
|||
'capabilities' => 'local/treestudyplan:editstudyplan',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'local_treestudyplan_associated_coaches' => [ // Web service function name.
|
||||
'classname' => '\local_treestudyplan\associationservice', // Class containing the external function.
|
||||
'methodname' => 'associated_coaches', // External function name.
|
||||
'description' => 'List coaches associated with a studyplan',
|
||||
'type' => 'read', // Database rights of the web service function (read, write).
|
||||
'ajax' => true,
|
||||
'capabilities' => 'local/treestudyplan:editstudyplan',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'local_treestudyplan_find_coach' => [ // Web service function name.
|
||||
'classname' => '\local_treestudyplan\associationservice', // Class containing the external function.
|
||||
'methodname' => 'find_coach', // External function name.
|
||||
'description' => 'Find available coach',
|
||||
'type' => 'read', // Database rights of the web service function (read, write).
|
||||
'ajax' => true,
|
||||
'capabilities' => 'local/treestudyplan:editstudyplan',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'local_treestudyplan_connect_coach' => [ // Web service function name.
|
||||
'classname' => '\local_treestudyplan\associationservice', // Class containing the external function.
|
||||
'methodname' => 'connect_coach', // External function name.
|
||||
'description' => 'Connect coach to study plan',
|
||||
'type' => 'read', // Database rights of the web service function (read, write).
|
||||
'ajax' => true,
|
||||
'capabilities' => 'local/treestudyplan:editstudyplan',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'local_treestudyplan_disconnect_coach' => [ // Web service function name.
|
||||
'classname' => '\local_treestudyplan\associationservice', // Class containing the external function.
|
||||
'methodname' => 'disconnect_coach', // External function name.
|
||||
'description' => 'Disconnect coach from studyplan',
|
||||
'type' => 'read', // Database rights of the web service function (read, write).
|
||||
'ajax' => true,
|
||||
'capabilities' => 'local/treestudyplan:editstudyplan',
|
||||
'loginrequired' => true,
|
||||
],
|
||||
/***************************
|
||||
* Category mapping
|
||||
***************************/
|
||||
|
|
|
@ -580,5 +580,30 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
upgrade_plugin_savepoint(true, 2024022504, 'local', 'treestudyplan');
|
||||
}
|
||||
|
||||
if ($oldversion < 2024030801) {
|
||||
|
||||
// Define table local_treestudyplan_coach to be created.
|
||||
$table = new xmldb_table('local_treestudyplan_coach');
|
||||
|
||||
// Adding fields to table local_treestudyplan_coach.
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('user_id', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('studyplan_id', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null);
|
||||
|
||||
// Adding keys to table local_treestudyplan_coach.
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
|
||||
$table->add_key('user_id-id', XMLDB_KEY_FOREIGN, ['user_id'], 'user', ['id']);
|
||||
$table->add_key('studyplan_id-id', XMLDB_KEY_FOREIGN, ['studyplan_id'], 'local_treestudyplan', ['id']);
|
||||
|
||||
// Conditionally launch create table for local_treestudyplan_coach.
|
||||
if (!$dbman->table_exists($table)) {
|
||||
$dbman->create_table($table);
|
||||
}
|
||||
|
||||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2024030801, 'local', 'treestudyplan');
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ $string["treestudyplan:configure"] = "Configure study plans";
|
|||
$string["treestudyplan:viewuserreports"] = "View study plan of others";
|
||||
$string["treestudyplan:forcescales"] = 'Advanced: Allow study plan manager to force assignment scales to setting (manual modes only)';
|
||||
$string["treestudyplan:selectowngradables"] = 'Teachers can select gradables in their own courses in study plan view mode (manual modes only)';
|
||||
$string["treestudyplan:lineunenrol"] = "Manage student registration in lines";
|
||||
$string["treestudyplan:coach"] = "Available as coach";
|
||||
|
||||
$string["report"] = 'Progress report';
|
||||
$string["report_invited"] = 'Progress report for {$a}';
|
||||
|
@ -253,14 +255,17 @@ $string["nav_invited"] = "View study plan by invitation";
|
|||
$string["associations"] = 'Associations';
|
||||
$string["associated_cohorts"] = 'Linked cohorts';
|
||||
$string["associated_users"] = 'Linked users';
|
||||
$string["associated_coaches"] = 'Linked coaches';
|
||||
$string["associate_cohorts"] = 'Search cohorts to add';
|
||||
$string["associate_users"] = 'Search users to add';
|
||||
$string["associate_coaches"] = 'Search coaches to add';
|
||||
$string["add_association"] = 'Add';
|
||||
$string["delete_association"] = 'Delete';
|
||||
$string["associations_empty"] = 'No active associations';
|
||||
$string["associations_search"] = 'Search';
|
||||
$string["users"] = 'Users';
|
||||
$string["cohorts"] = 'Cohorts';
|
||||
$string["coaches"] = 'Coaches';
|
||||
$string["selected"] = 'Select';
|
||||
$string["name"] = 'Name';
|
||||
$string["context"] = 'Category';
|
||||
|
|
|
@ -38,6 +38,9 @@ $string["treestudyplan:configure"] = "Studieplannen configureren";
|
|||
$string["treestudyplan:viewuserreports"] = "Studieplannen van anderen bekijken";
|
||||
$string["treestudyplan:forcescales"] = 'Gevorderd: Studieplanbeheerder kan alle opdrachten in studieplan instellen op specifieke resultaatschaal (alleen handmatige modes)';
|
||||
$string["treestudyplan:selectowngradables"] = 'Docenten kunnen in hun eigen cursussen zelf activiteiten selecteren in een studieplan (docentenweergave, alleen handmatige modes)';
|
||||
$string["treestudyplan:lineunenrol"] = "Beheer inschrijvingen van studenten in leerlijnen";
|
||||
$string["treestudyplan:coach"] = "Beschikbaar als coach";
|
||||
|
||||
|
||||
$string["report"] = 'Voortgangsrapport';
|
||||
$string["report_invited"] = 'Voortgang van {$a}';
|
||||
|
@ -253,14 +256,17 @@ $string["nav_invited"] = "Studieplan op uitnodiging bekijken";
|
|||
$string["associations"] = 'Koppelingen';
|
||||
$string["associated_cohorts"] = 'Gekoppelde site-groepen';
|
||||
$string["associated_users"] = 'Gekoppelde gebruikers';
|
||||
$string["associated_coaches"] = 'Gekoppelde coaches';
|
||||
$string["associate_cohorts"] = 'Zoek om te koppelen';
|
||||
$string["associate_users"] = 'Zoek om te koppelen';
|
||||
$string["associate_coaches"] = 'Zoek coaches';
|
||||
$string["add_association"] = 'Toevoegen';
|
||||
$string["delete_association"] = 'Verwijderen';
|
||||
$string["associations_empty"] = 'Geen koppelingen';
|
||||
$string["associations_search"] = 'Zoeken';
|
||||
$string["users"] = 'Gebruikers';
|
||||
$string["cohorts"] = 'Site-groepen';
|
||||
$string["coaches"] = 'Coaches';
|
||||
$string["selected"] = 'Kies';
|
||||
$string["name"] = 'Naam';
|
||||
$string["context"] = 'Categorie';
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
|
||||
$plugin->version = 2024030200; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->version = 2024030804; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
||||
|
||||
$plugin->release = "1.1.5";
|
||||
$plugin->release = "1.1.6";
|
||||
$plugin->maturity = MATURITY_BETA; /*MATURITY_STABLE;*/
|
||||
|
||||
// Supported from Moodle 3.11 to 4.3.
|
||||
|
|
Loading…
Reference in New Issue
Block a user