Added code to edit enrolment config on study lines

This commit is contained in:
PMKuipers 2024-02-25 23:45:39 +01:00
parent 9619fd17de
commit 1fe4db6328
15 changed files with 471 additions and 51 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

@ -1,3 +1,3 @@
define("local_treestudyplan/util/premium",["exports","core/ajax"],(function(_exports,_ajax){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.enabled=function(){return!!premiumstatus.enabled};let premiumstatus={enabled:!1,website:"",name:"",expires:""}})); define("local_treestudyplan/util/premium",["exports","core/ajax","core/notification"],(function(_exports,_ajax,_notification){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.premiumenabled=function(){return!!premiumstatus().enabled},_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let premiumstatus_cache=null;function premiumstatus(){return premiumstatus_cache||(0,_ajax.call)([{methodname:"local_treestudyplan_premiumstatus",args:{}}])[0].then((function(response){premiumstatus_cache=response})).catch(_notification.default.exception),premiumstatus_cache}premiumstatus()}));
//# sourceMappingURL=premium.min.js.map //# sourceMappingURL=premium.min.js.map

View File

@ -1 +1 @@
{"version":3,"file":"premium.min.js","sources":["../../src/util/premium.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\n\n// Prepare default value.\nlet premiumstatus = {\n enabled: false,\n website: \"\",\n name: \"\",\n expires: \"\",\n};\n\n/**\n * Check if premium status is enabled.\n * @returns {Object} The map with strings loaded in\n */\nexport function enabled (){\n return !!premiumstatus.enabled;\n}\n"],"names":["premiumstatus","enabled","website","name","expires"],"mappings":"wLAqBaA,cAAcC,aAZvBD,cAAgB,CAChBC,SAAS,EACTC,QAAS,GACTC,KAAM,GACNC,QAAS"} {"version":3,"file":"premium.min.js","sources":["../../src/util/premium.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\n// Prepare default value.\nlet premiumstatus_cache = null;\n\n/**\n * Check if premium status is enabled.\n * @returns {Bool} True/False\n */\nexport function premiumenabled (){\n const premium = premiumstatus();\n return (premium.enabled)?true:false;\n}\n\n/**\n * Get premium status object\n * @returns {Object} The premium status object\n */\nfunction premiumstatus() {\n if (!premiumstatus_cache) {\n // Retrieve premium status if needed.\n call([{\n methodname: 'local_treestudyplan_premiumstatus',\n args: {}\n }])[0].then(function(response){\n premiumstatus_cache = response;\n }).catch(notification.exception);\n }\n return premiumstatus_cache;\n}\n\n// Preload premium status.\npremiumstatus();"],"names":["premiumstatus","enabled","premiumstatus_cache","methodname","args","then","response","catch","notification","exception"],"mappings":"yOAcoBA,gBACAC,iFARhBC,oBAAsB,cAejBF,uBACAE,oCAEI,CAAC,CACFC,WAAY,oCACZC,KAAM,MACN,GAAGC,MAAK,SAASC,UACjBJ,oBAAsBI,YACvBC,MAAMC,sBAAaC,WAEnBP,oBAIXF"}

View File

@ -18,6 +18,7 @@ import Config from 'core/config';
import {download,upload} from './downloader'; import {download,upload} from './downloader';
import {ProcessStudyplan, ProcessStudyplanPage} from './studyplan-processor'; import {ProcessStudyplan, ProcessStudyplanPage} from './studyplan-processor';
import {eventTypes as editSwEventTypes} from 'core/edit_switch'; import {eventTypes as editSwEventTypes} from 'core/edit_switch';
import { premiumenabled, premiumstatus } from "./util/premium";
import TSComponents from './treestudyplan-components'; import TSComponents from './treestudyplan-components';
import mFormComponents from "./util/mform-helper"; import mFormComponents from "./util/mform-helper";
@ -87,6 +88,8 @@ export default {
studyline_name_ph: 'studyline_name_ph', studyline_name_ph: 'studyline_name_ph',
studyline_shortname: 'studyline_shortname', studyline_shortname: 'studyline_shortname',
studyline_shortname_ph: 'studyline_shortname_ph', studyline_shortname_ph: 'studyline_shortname_ph',
studyline_enrollable: 'studyline_enrollable',
studyline_enrolroles: 'studyline_enrolroles',
studyline_color: 'studyline_color', studyline_color: 'studyline_color',
associations: 'associations', associations: 'associations',
associated_cohorts: 'associated_cohorts', associated_cohorts: 'associated_cohorts',
@ -103,6 +106,11 @@ export default {
studyplan_slots: 'studyplan_slots', studyplan_slots: 'studyplan_slots',
studyplan_startdate: 'studyplan_startdate', studyplan_startdate: 'studyplan_startdate',
studyplan_enddate: 'studyplan_enddate', studyplan_enddate: 'studyplan_enddate',
line_enrollable_0: 'line_enrollable:0',
line_enrollable_1: 'line_enrollable:1',
line_enrollable_2: 'line_enrollable:2',
line_enrollable_3: 'line_enrollable:3',
}, },
studyplan_advanced: { studyplan_advanced: {
advanced_tools: 'advanced_tools', advanced_tools: 'advanced_tools',
@ -1215,14 +1223,16 @@ export default {
}, },
create: { create: {
studyline: { studyline: {
'name': '', name: '',
'shortname': '', shortname: '',
'color': '#DDDDDD', color: '#DDDDDD',
enrollable: 0,
enrolroles: [],
}, },
page: { page: {
'id': -1, id: -1,
'name' : '', name : '',
'shortname' : '' shortname : ''
} }
}, },
edit: { edit: {
@ -1233,8 +1243,11 @@ export default {
name: '', name: '',
shortname: '', shortname: '',
color: '#DDDDDD', color: '#DDDDDD',
enrollable: 0,
enrolroles: [],
}, },
original: {}, original: {},
availableroles: [],
}, },
studyplan: { studyplan: {
data: { data: {
@ -1267,7 +1280,8 @@ export default {
color: '#FF0000', color: '#FF0000',
filterslots: [{}], filterslots: [{}],
courseslots: [{}] courseslots: [{}]
} },
availableroles: [],
}; };
}, },
created() { created() {
@ -1279,10 +1293,22 @@ export default {
}); });
}, },
mounted() { mounted() {
const self=this;
if(this.value.pages[0].studylines.length == 0){ if(this.value.pages[0].studylines.length == 0){
// start in editmode if studylines on first page are empty // start in editmode if studylines on first page are empty
this.edit.studyline.editmode = true; this.edit.studyline.editmode = true;
} }
// Retrieve available roles
call([{
methodname: 'local_treestudyplan_list_roles',
args: {
'studyplan_id': this.value.id,
}
}])[0].then(function(response){
self.availableroles = response;
}).catch(notification.exception);
this.$root.$emit('redrawLines'); this.$root.$emit('redrawLines');
this.$emit('pagechanged',this.selectedpage); this.$emit('pagechanged',this.selectedpage);
}, },
@ -1296,6 +1322,7 @@ export default {
} }
}, },
methods: { methods: {
premiumenabled,
columns(page) { columns(page) {
return 1+ (page.periods * 2); return 1+ (page.periods * 2);
}, },
@ -1379,18 +1406,24 @@ export default {
'shortname': newlineinfo.shortname, 'shortname': newlineinfo.shortname,
'color': newlineinfo.color, 'color': newlineinfo.color,
'sequence': page.studylines.length, 'sequence': page.studylines.length,
'enrollable': newlineinfo.enrollable,
'enrolroles': newlineinfo.enrolroles
} }
}])[0].then(function(response){ }])[0].then(function(response){
page.studylines.push(response); page.studylines.push(response);
newlineinfo.name = ''; newlineinfo.name = '';
newlineinfo.shortname = ''; newlineinfo.shortname = '';
newlineinfo.color = "#dddddd"; newlineinfo.color = "#dddddd";
newlineinfo.enrollable = 0;
newlineinfo.enrolroles = [];
}).catch(notification.exception); }).catch(notification.exception);
}, },
editLineStart(line) { editLineStart(line) {
const page = this.value.pages[this.selectedpageindex];
debug.info("Starting line edit", line);
Object.assign(this.edit.studyline.data,line); Object.assign(this.edit.studyline.data,line);
this.edit.studyline.original = line; this.edit.studyline.original = line;
this.$bvModal.show('modal-edit-studyline-'+this.value.id); this.$bvModal.show('modal-edit-studyline-'+page.id);
}, },
editLineFinish() { editLineFinish() {
let editedline = this.edit.studyline.data; let editedline = this.edit.studyline.data;
@ -1400,11 +1433,16 @@ export default {
args: { 'id': editedline.id, args: { 'id': editedline.id,
'name': editedline.name, 'name': editedline.name,
'shortname': editedline.shortname, 'shortname': editedline.shortname,
'color': editedline.color,} 'color': editedline.color,
'enrollable': editedline.enrollable,
'enrolroles': editedline.enrolroles
}
}])[0].then(function(response){ }])[0].then(function(response){
originalline['name'] = response['name']; originalline['name'] = response['name'];
originalline['shortname'] = response['shortname']; originalline['shortname'] = response['shortname'];
originalline['color'] = response['color']; originalline['color'] = response['color'];
originalline['enrollable'] = response['enrollable'];
originalline['enrolroles'] = response['enrolroles'];
}).catch(notification.exception); }).catch(notification.exception);
}, },
deleteLine(page,line) { deleteLine(page,line) {
@ -1764,6 +1802,32 @@ export default {
<!-- hsluv-picker v-model="create.studyline.color" horizontal displaysize="175" ></hsluv-picker --> <!-- hsluv-picker v-model="create.studyline.color" horizontal displaysize="175" ></hsluv-picker -->
</b-col> </b-col>
</b-row> </b-row>
<template v-if="premiumenabled()">
<b-row>
<b-col cols="3">{{ text.studyline_enrollable}}</b-col>
<b-col>
<b-form-select v-model="create.studyline.enrollable">
<b-form-select-option
v-for="(nr,n) in 4"
:value="n"
>{{text['line_enrollable_'+n]}}</b-form-select-option>
</b-form-select>
</b-col>
</b-row>
<b-row v-if='[2,3].includes(create.studyline.enrollable)'>
<b-col cols="3">{{ text.studyline_enrolroles}}</b-col>
<b-col>
<b-form-select
v-model="create.studyline.enrolroles"
:options="availableroles"
multiple
value-field="id"
text-field="name"
:select-size="6"
></b-form-select>
</b-col>
</b-row>
</template>
</b-container> </b-container>
</b-modal> </b-modal>
<b-modal <b-modal
@ -1797,6 +1861,32 @@ export default {
<input type="color" v-model="edit.studyline.data.color" /> <input type="color" v-model="edit.studyline.data.color" />
</b-col> </b-col>
</b-row> </b-row>
<template v-if="premiumenabled()">
<b-row>
<b-col cols="3">{{ text.studyline_enrollable}}</b-col>
<b-col>
<b-form-select v-model="edit.studyline.data.enrollable">
<b-form-select-option
v-for="(nr,n) in 4"
:value="n"
>{{text['line_enrollable_'+n]}}</b-form-select-option>
</b-form-select>
</b-col>
</b-row>
<b-row v-if='[2,3].includes(edit.studyline.data.enrollable)'>
<b-col cols="3">{{ text.studyline_enrolroles}}</b-col>
<b-col>
<b-form-select
v-model="edit.studyline.data.enrolroles"
:options="availableroles"
multiple
value-field="id"
text-field="name"
:select-size="6"
></b-form-select>
</b-col>
</b-row>
</template>
</b-container> </b-container>
</b-modal> </b-modal>
</b-tab> </b-tab>

View File

@ -1,23 +1,37 @@
/*eslint no-var: "error" */ /*eslint no-var: "error" */
/*eslint no-unused-vars: "off" */
/*eslint linebreak-style: "off" */
/*eslint no-trailing-spaces: "off" */
/*eslint-env es6*/ /*eslint-env es6*/
import {call} from 'core/ajax'; import {call} from 'core/ajax';
import notification from 'core/notification';
// Prepare default value. // Prepare default value.
let premiumstatus = { let premiumstatus_cache = null;
enabled: false,
website: "",
name: "",
expires: "",
};
/** /**
* Check if premium status is enabled. * Check if premium status is enabled.
* @returns {Object} The map with strings loaded in * @returns {Bool} True/False
*/ */
export function enabled (){ export function premiumenabled (){
return !!premiumstatus.enabled; const premium = premiumstatus();
return (premium.enabled)?true:false;
} }
/**
* Get premium status object
* @returns {Object} The premium status object
*/
function premiumstatus() {
if (!premiumstatus_cache) {
// Retrieve premium status if needed.
call([{
methodname: 'local_treestudyplan_premiumstatus',
args: {}
}])[0].then(function(response){
premiumstatus_cache = response;
}).catch(notification.exception);
}
return premiumstatus_cache;
}
// Preload premium status.
premiumstatus();

View File

@ -167,6 +167,7 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
*/ */
protected static function premiumStatus() { protected static function premiumStatus() {
if (!isset(self::$cachedpremiumstatus)) { if (!isset(self::$cachedpremiumstatus)) {
// Initialize default object. // Initialize default object.
$o = new \stdClass; $o = new \stdClass;
$o->enabled = false; $o->enabled = false;
@ -343,21 +344,32 @@ Klc5I28bGbvxIV5pnL6ZSjHEDp2WreM8HB0XFJwU+Q==
* @return object * @return object
*/ */
public static function get_premiumstatus() { public static function get_premiumstatus() {
$status = self::premiumStatus(); if (self::$premium_supported) {
$keys = [ $status = self::premiumStatus();
"enabled", $keys = [
"website", "enabled",
"name", "website",
"expires", "name",
"expired", "expires",
"intents", "expired",
]; "intents",
];
$result = [];
foreach ( $keys as $param) { $result = [];
$result[$param] = $status->$param; foreach ( $keys as $param) {
$result[$param] = $status->$param;
}
return $result;
} else {
return [
"enabled" => true,
"website" => '*',
"name" => '*',
"expires" => '',
"expired" => false,
"intents" => [],
];
} }
return $result;
} }
/** /**

View File

@ -28,6 +28,30 @@ require_once($CFG->libdir.'/externallib.php');
* Model class for study lines * Model class for study lines
*/ */
class studyline { class studyline {
/**
* Studyline is not enrollable
* @var int
*/
public const ENROLLABLE_NONE = 0;
/**
* Studyline can be enrolled into by the student
* @var int
*/
public const ENROLLABLE_SELF = 1;
/**
* Studyline can be enrolled into by specific role(s)
* @var int
*/
public const ENROLLABLE_ROLE = 2;
/**
* Studyline can be enrolled by user and/or role
* @var int
*/
public const ENROLLABLE_SELF_ROLE = 3;
/** /**
* Handle for course slots in the period for webservice export * Handle for course slots in the period for webservice export
* @var string * @var string
@ -152,6 +176,81 @@ class studyline {
return $this->r->shortname; return $this->r->shortname;
} }
/**
* Whether this line is enrollable by the student
*/
public function self_enrollable() : bool {
return ($this->r->enrollable == self::ENROLLABLE_SELF || $this->r->enrollable == self::ENROLLABLE_SELF_ROLE);
}
/**
* Whether this line is enrollable by a role
*/
public function role_enrollable() : bool {
return ($this->r->enrollable == self::ENROLLABLE_ROLE || $this->r->enrollable == self::ENROLLABLE_SELF_ROLE);
}
/**
* Whether this line is enrollable at all
*/
public function enrollable() : bool {
return $this->r->enrollable != self::ENROLLABLE_NONE;
}
/**
* List the available roles that can enrol the student
*/
public function enrol_roles() : array {
global $DB;
$list = [];
$roles = explode(",",$this->r->enrolrole);
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) {}
}
}
return $list;
}
/**
* List the available roles that can enrol the student
*/
public function enrol_roleids() : array {
global $DB;
$list = [];
$roles = explode(",",$this->r->enrolrole);
foreach($roles as $r) {
$roleid = intval($r);
if ($roleid > 0) {
$list[] = $roleid;
}
}
return $list;
}
/**
* List the available roles that can enrol the student in a proper model
*/
public function enrol_roles_model() : array {
$list = [];
foreach($this->enrol_roles as $role) {
$name = role_get_name($role,$this->context()); // Get localized role name.
$list[] = [
"id" => $role->id,
"name" => $name,
];
}
return $list;
}
/** /**
* Webservice structure for editor info * Webservice structure for editor info
* @param int $value Webservice requirement constant * @param int $value Webservice requirement constant
@ -163,6 +262,8 @@ class studyline {
"shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'), "shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'),
"color" => new \external_value(PARAM_TEXT, 'description of studyline'), "color" => new \external_value(PARAM_TEXT, 'description of studyline'),
"sequence" => new \external_value(PARAM_INT, 'order of studyline'), "sequence" => new \external_value(PARAM_INT, 'order of studyline'),
"enrollable" => new \external_value(PARAM_INT, 'type of enrollable'),
"enrolroles" => new \external_multiple_structure(new \external_value(PARAM_INT, 'id of role')),
"slots" => new \external_multiple_structure( "slots" => new \external_multiple_structure(
new \external_single_structure([ new \external_single_structure([
self::SLOTSET_COURSES => new \external_multiple_structure( self::SLOTSET_COURSES => new \external_multiple_structure(
@ -185,6 +286,12 @@ class studyline {
"shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'), "shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'),
"color" => new \external_value(PARAM_TEXT, 'description of studyline'), "color" => new \external_value(PARAM_TEXT, 'description of studyline'),
"sequence" => new \external_value(PARAM_INT, 'order of studyline'), "sequence" => new \external_value(PARAM_INT, 'order of studyline'),
"enrollable_self" => new \external_value(PARAM_BOOL, 'enrollable by student'),
"enrollable_role" => new \external_value(PARAM_BOOL, 'enrollable by student'),
"enrolroles" => new \external_multiple_structure(new \external_single_structure([
"id" => new \external_value(PARAM_INT, 'id of role'),
"name" => new \external_value(PARAM_TEXT, 'name of role'),
])),
],"Study line simple structure",$value); ],"Study line simple structure",$value);
} }
@ -199,6 +306,9 @@ class studyline {
'shortname' => $this->r->shortname, 'shortname' => $this->r->shortname,
'color' => $this->r->color, 'color' => $this->r->color,
'sequence' => $this->r->sequence, 'sequence' => $this->r->sequence,
'enrollable_self' => $this->self_enrollable(),
'enrollable_role' => $this->role_enrollable(),
'enrolroles' => $this->enrol_roles_model(),
]; ];
} }
@ -225,6 +335,8 @@ class studyline {
'shortname' => $this->r->shortname, 'shortname' => $this->r->shortname,
'color' => $this->r->color, 'color' => $this->r->color,
'sequence' => $this->r->sequence, 'sequence' => $this->r->sequence,
'enrollable' => $this->r->enrollable,
'enrolroles' => $this->enrol_roleids(),
'slots' => [], 'slots' => [],
]; ];
if ($mode == "export") { if ($mode == "export") {
@ -293,7 +405,7 @@ class studyline {
$pageid = $fields['page_id']; $pageid = $fields['page_id'];
$sqmax = $DB->get_field_select(self::TABLE, "MAX(sequence)", "page_id = :page_id", ['page_id' => $pageid]); $sqmax = $DB->get_field_select(self::TABLE, "MAX(sequence)", "page_id = :page_id", ['page_id' => $pageid]);
$addable = ['page_id', 'name', 'shortname', 'color']; $addable = ['page_id', 'name', 'shortname', 'color','enrollable','enrolrole'];
$info = ['sequence' => $sqmax + 1]; $info = ['sequence' => $sqmax + 1];
foreach ($addable as $f) { foreach ($addable as $f) {
if (array_key_exists($f, $fields)) { if (array_key_exists($f, $fields)) {
@ -310,7 +422,7 @@ class studyline {
*/ */
public function edit($fields) : self { public function edit($fields) : self {
global $DB; global $DB;
$editable = ['name', 'shortname', 'color']; $editable = ['name', 'shortname', 'color','enrollable','enrolrole'];
$info = ['id' => $this->id, ]; $info = ['id' => $this->id, ];
foreach ($editable as $f) { foreach ($editable as $f) {
if (array_key_exists($f, $fields)) { if (array_key_exists($f, $fields)) {
@ -389,6 +501,12 @@ class studyline {
"shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'), "shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'),
"color" => new \external_value(PARAM_TEXT, 'description of studyline'), "color" => new \external_value(PARAM_TEXT, 'description of studyline'),
"sequence" => new \external_value(PARAM_INT, 'order of studyline'), "sequence" => new \external_value(PARAM_INT, 'order of studyline'),
"enrollable_self" => new \external_value(PARAM_BOOL, 'enrollable by student'),
"enrollable_role" => new \external_value(PARAM_BOOL, 'enrollable by student'),
"enrol_roles" => new \external_multiple_structure(new \external_single_structure([
"id" => new \external_value(PARAM_INT, 'id of studyline'),
"name" => new \external_value(PARAM_TEXT, 'shortname of studyline'),
])),
"slots" => new \external_multiple_structure( "slots" => new \external_multiple_structure(
new \external_single_structure([ new \external_single_structure([
self::SLOTSET_COURSES => new \external_multiple_structure( self::SLOTSET_COURSES => new \external_multiple_structure(
@ -416,6 +534,9 @@ class studyline {
'shortname' => $this->r->shortname, 'shortname' => $this->r->shortname,
'color' => $this->r->color, 'color' => $this->r->color,
'sequence' => $this->r->sequence, 'sequence' => $this->r->sequence,
'enrollable_self' => $this->self_enrollable(),
'enrollable_role' => $this->role_enrollable(),
'enrol_roles' => $this->enrol_roles_model(),
'slots' => [], 'slots' => [],
]; ];

View File

@ -352,6 +352,8 @@ class studyplanservice extends \external_api {
"shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'), "shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'),
"color" => new \external_value(PARAM_TEXT, 'description of studyline'), "color" => new \external_value(PARAM_TEXT, 'description of studyline'),
"sequence" => new \external_value(PARAM_INT, 'sequence of studyline'), "sequence" => new \external_value(PARAM_INT, 'sequence of studyline'),
"enrollable" => new \external_value(PARAM_INT, 'type of enrollable', VALUE_DEFAULT),
"enrolroles" => new \external_multiple_structure(new \external_value(PARAM_INT), 'enrolling roles', VALUE_DEFAULT),
] ); ] );
} }
@ -371,18 +373,30 @@ class studyplanservice extends \external_api {
* @param mixed $sequence * @param mixed $sequence
* @return array * @return array
*/ */
public static function add_studyline($pageid, $name, $shortname, $color, $sequence) { public static function add_studyline($pageid, $name, $shortname, $color, $sequence, $enrollable=null, $enrolroles=null) {
// Validate if the requesting user has the right to edit the plan in it's current context. // Validate if the requesting user has the right to edit the plan in it's current context.
$page = studyplanpage::find_by_id($pageid); $page = studyplanpage::find_by_id($pageid);
webservicehelper::require_capabilities(self::CAP_EDIT, $page->studyplan()->context()); webservicehelper::require_capabilities(self::CAP_EDIT, $page->studyplan()->context());
$o = studyline::add([ $add = [
'page_id' => $pageid, 'page_id' => $pageid,
'name' => $name, 'name' => $name,
'shortname' => $shortname, 'shortname' => $shortname,
'color' => $color, 'color' => $color,
'sequence' => $sequence, 'sequence' => $sequence,
]); ];
if (isset($enrollable) &&
$enrollable >= studyline::ENROLLABLE_NONE &&
$enrollable <= studyline::ENROLLABLE_SELF_ROLE
) {
$add['enrollable'] = $enrollable;
}
if (isset($enrolroles)) {
$add['enrolrole'] = implode(",",$enrolroles);
}
$o = studyline::add($add);
return $o->editor_model(); return $o->editor_model();
} }
@ -401,6 +415,8 @@ class studyplanservice extends \external_api {
"name" => new \external_value(PARAM_TEXT, 'shortname of studyline'), "name" => new \external_value(PARAM_TEXT, 'shortname of studyline'),
"shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'), "shortname" => new \external_value(PARAM_TEXT, 'idnumber of studyline'),
"color" => new \external_value(PARAM_TEXT, 'description of studyline'), "color" => new \external_value(PARAM_TEXT, 'description of studyline'),
"enrollable" => new \external_value(PARAM_INT, 'type of enrollable', VALUE_DEFAULT),
"enrolroles" => new \external_multiple_structure(new \external_value(PARAM_INT), 'enrolling roles', VALUE_DEFAULT),
] ); ] );
} }
@ -419,16 +435,30 @@ class studyplanservice extends \external_api {
* @param mixed $color * @param mixed $color
* @return [type] * @return [type]
*/ */
public static function edit_studyline($id, $name, $shortname, $color) { public static function edit_studyline($id, $name, $shortname, $color, $enrollable=null, $enrolroles=null) {
$o = studyline::find_by_id($id); $o = studyline::find_by_id($id);
// Validate if the requesting user has the right to edit the plan in it's current context. // Validate if the requesting user has the right to edit the plan in it's current context.
webservicehelper::require_capabilities(self::CAP_EDIT, $o->context()); webservicehelper::require_capabilities(self::CAP_EDIT, $o->context());
$o->edit([ $edit = [
'name' => $name, 'name' => $name,
'shortname' => $shortname, 'shortname' => $shortname,
'color' => $color, 'color' => $color,
]); ];
if (isset($enrollable) &&
$enrollable >= studyline::ENROLLABLE_NONE &&
$enrollable <= studyline::ENROLLABLE_SELF_ROLE
) {
$edit['enrollable'] = $enrollable;
}
if (isset($enrolroles)) {
$edit['enrolrole'] = implode(",",$enrolroles);
}
$o->edit($edit);
return $o->editor_model(); return $o->editor_model();
} }
@ -1858,6 +1888,61 @@ class studyplanservice extends \external_api {
return $o->delete(!!$force)->model(); return $o->delete(!!$force)->model();
} }
/************************
* *
* list_roles *
* *
************************/
/**
* Parameter description for webservice function delete_studyplan
*/
public static function list_roles_parameters() : \external_function_parameters {
return new \external_function_parameters( [
"studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan'),
] );
}
/**
* Return value description for webservice function delete_studyplan
*/
public static function list_roles_returns() : \external_description {
return new \external_multiple_structure(new \external_single_structure([
"id" => new \external_value(PARAM_INT, 'id of studyline'),
"name" => new \external_value(PARAM_TEXT, 'shortname of studyline'),
]));
}
/**
* Delete a studyplan
* @param mixed $id Id of the studyplan
* @param bool $force Force deletion, even though studyplan is not empty
* @return array Succes/fail model
*/
public static function list_roles($studyplan_id) {
global $DB;
$p = studyplan::find_by_id($studyplan_id);
$context = $p->context();
// Validate if the requesting user has the right to edit the plan in it's current context.
webservicehelper::require_capabilities(self::CAP_EDIT, $context);
$contextlevels = [CONTEXT_SYSTEM,CONTEXT_COURSECAT];
$list = [];
$roles = \get_all_roles();
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 ){
$list[] = [
'id' => $role->id,
'name' => $name,
];
}
}
return $list;
}
} }

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="local/treestudyplan/db" VERSION="20231019" COMMENT="XMLDB file for Moodle local/treestudyplan" <XMLDB PATH="local/treestudyplan/db" VERSION="20240225" COMMENT="XMLDB file for Moodle local/treestudyplan"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
> >
@ -68,6 +68,8 @@
<FIELD NAME="shortname" TYPE="text" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="shortname" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="color" TYPE="char" LENGTH="12" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="color" TYPE="char" LENGTH="12" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="sequence" TYPE="int" LENGTH="18" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="sequence" TYPE="int" LENGTH="18" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="enrollable" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Let's student enroll in a study plan line, only after which the line is active for this user."/>
<FIELD NAME="enrolrole" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Roles allowed to enrol a student in this course (in the studyplan context)"/>
</FIELDS> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/> <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
@ -178,5 +180,21 @@
<KEY NAME="primary" TYPE="primary" FIELDS="id"/> <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS> </KEYS>
</TABLE> </TABLE>
<TABLE NAME="local_treestudyplan_lineuser" COMMENT="Default comment for the table, please edit me">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="line_id" TYPE="int" LENGTH="18" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="user_id" TYPE="int" LENGTH="18" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timeenrolled" TYPE="int" LENGTH="12" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="enrolled" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="enrolledby" TYPE="int" LENGTH="18" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="line_id-id" TYPE="foreign" FIELDS="line_id" REFTABLE="local_treestudyplan_line" REFFIELDS="id"/>
<KEY NAME="user_id-id" TYPE="foreign" FIELDS="user_id" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="enrolledby-id" TYPE="foreign" FIELDS="enrolledby" REFTABLE="user" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES> </TABLES>
</XMLDB> </XMLDB>

View File

@ -722,4 +722,16 @@ $functions = [
'capabilities' => '', 'capabilities' => '',
'loginrequired' => false, 'loginrequired' => false,
], ],
/***************************
* Studyline enrollment functions
***************************/
'local_treestudyplan_list_roles' => [ // Web service function name.
'classname' => '\local_treestudyplan\studyplanservice', // Class containing the external function.
'methodname' => 'list_roles', // External function name.
'description' => 'Retrieve list of available roles in the studyplan\'s context',
'type' => 'read', // Database rights of the web service function (read, write).
'ajax' => true,
'capabilities' => 'local/treestudyplan:editstudyplan',
'loginrequired' => true,
],
]; ];

View File

@ -521,5 +521,64 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
// Treestudyplan savepoint reached. // Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2023102200, 'local', 'treestudyplan'); upgrade_plugin_savepoint(true, 2023102200, 'local', 'treestudyplan');
} }
if ($oldversion < 2024022502) {
// Define field enrollable to be added to local_treestudyplan_line.
$table = new xmldb_table('local_treestudyplan_line');
$field = new xmldb_field('enrollable', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'sequence');
// Conditionally launch add field enrollable.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2024022502, 'local', 'treestudyplan');
}
if ($oldversion < 2024022503) {
// Define field enrolrole to be added to local_treestudyplan_line.
$table = new xmldb_table('local_treestudyplan_line');
$field = new xmldb_field('enrolrole', XMLDB_TYPE_TEXT, null, null, null, null, null, 'enrollable');
// Conditionally launch add field enrolrole.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2024022503, 'local', 'treestudyplan');
}
if ($oldversion < 2024022504) {
// Define table local_treestudyplan_lineuser to be created.
$table = new xmldb_table('local_treestudyplan_lineuser');
// Adding fields to table local_treestudyplan_lineuser.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('line_id', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null);
$table->add_field('user_id', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null);
$table->add_field('timeenrolled', XMLDB_TYPE_INTEGER, '12', null, null, null, null);
$table->add_field('enrolled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
$table->add_field('enrolledby', XMLDB_TYPE_INTEGER, '18', null, null, null, null);
// Adding keys to table local_treestudyplan_lineuser.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$table->add_key('line_id-id', XMLDB_KEY_FOREIGN, ['line_id'], 'local_treestudyplan_line', ['id']);
$table->add_key('user_id-id', XMLDB_KEY_FOREIGN, ['user_id'], 'user', ['id']);
$table->add_key('enrolledby-id', XMLDB_KEY_FOREIGN, ['enrolledby'], 'user', ['id']);
// Conditionally launch create table for local_treestudyplan_lineuser.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2024022504, 'local', 'treestudyplan');
}
return true; return true;
} }

View File

@ -465,3 +465,7 @@ $string["studyplan_report"] = 'Studyplan result overview';
$string["overviewreport:all"] = 'Result overview'; $string["overviewreport:all"] = 'Result overview';
$string["overviewreport:period"] = 'Result overview for this period'; $string["overviewreport:period"] = 'Result overview for this period';
$string["line_enrollable:0"] = 'No registration needed';
$string["line_enrollable:1"] = 'Registration by students themselves.';
$string["line_enrollable:2"] = 'Registration by user with role';
$string["line_enrollable:3"] = 'Registration by students themeselves or user with role';

View File

@ -463,4 +463,9 @@ $string["premiumfeature:morecategories"] = 'In meer dan 20 categoriën een studi
$string["overall"] = 'Cursus voltooid'; $string["overall"] = 'Cursus voltooid';
$string["studyplan_report"] = 'Studieplan resultatenoverzicht'; $string["studyplan_report"] = 'Studieplan resultatenoverzicht';
$string["overviewreport:all"] = 'Resultatenoverzicht'; $string["overviewreport:all"] = 'Resultatenoverzicht';
$string["overviewreport:period"] = 'Resultatenoverzicht voor deze periode'; $string["overviewreport:period"] = 'Resultatenoverzicht voor deze periode';
$string["line_enrollable:0"] = 'Geen inschrijving nodig';
$string["line_enrollable:1"] = 'Inschrijving door student zelf';
$string["line_enrollable:2"] = 'Inschrijving door gebruiker met rol';
$string["line_enrollable:3"] = 'Inschrijving door student zelf of gebruiker met rol';

View File

@ -22,7 +22,7 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494). $plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
$plugin->version = 2024022500; // YYYYMMDDHH (year, month, day, iteration). $plugin->version = 2024022505; // YYYYMMDDHH (year, month, day, iteration).
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11). $plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
$plugin->release = "1.1.5"; $plugin->release = "1.1.5";