Implemended studyplan suspension

This commit is contained in:
PMKuipers 2024-03-10 15:56:35 +01:00
parent f2fac43139
commit 747e1decd1
21 changed files with 144 additions and 43 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -438,7 +438,7 @@ export default {
const list = self.studyplans[k];
for (const idx in list) {
const plan = list[idx];
if (plan.id == parts[0]){
if (plan.id == parts[0] && !plan.suspended){
self.selectStudyplan(plan);
return;
}
@ -448,14 +448,23 @@ export default {
if (self.studyplans.present.length == 1) {
// Directly show the current study plan if it's the only current one
self.selectStudyplan(self.studyplans.present[0]);
const plan = self.studyplans.present[0];
if (!plan.suspended) {
self.selectStudyplan(plan);
}
} else {
// If there is but a single studyplan, select it anyway, even if it is not current...
if (this.studyplancount == 1) {
if(self.studyplans.future.lengh > 0) {
self.selectStudyplan(self.studyplans.future[0]);
const plan = self.studyplans.future[0];
if (!plan.suspended) {
self.selectStudyplan(plan);
}
} else {
self.selectStudyplan(self.studyplans.past[0]);
const plan = self.studyplans.past[0];
if (!plan.suspended) {
self.selectStudyplan(plan);
}
}
}
}

View File

@ -721,7 +721,6 @@ export default {
methods: {
planSaved(updatedpage){
const self = this;
debug.info("Got new page data",updatedpage);
if(self.mode == 'create'){
// Inform parent of the details of the newly created plan

View File

@ -6,10 +6,14 @@
import {load_strings} from './util/string-helper';
import {format_date, studyplanDates, studyplanTiming} from './util/date-helper';
import FitTextVue from './util/fittext-vue';
export default {
install(Vue/*,options*/){
Vue.use(FitTextVue);
let strings = load_strings({
studyplancard: {
open: "open",
@ -18,6 +22,7 @@ export default {
description: "studyplan_description",
completed: "completed",
details: "studyplan_details",
suspended: "suspended",
},
details: {
details: "studyplan_details",
@ -43,6 +48,10 @@ export default {
open: {
type: Boolean
},
ignoresuspend: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -60,6 +69,9 @@ export default {
end: (dates.end)?format_date(dates.end):this.text.noenddate,
};
},
suspended() {
return (this.value.suspended && !this.ignoresuspend);
}
},
methods: {
@ -69,7 +81,7 @@ export default {
},
template: `
<b-card
:class="'s-studyplan-card timing-' + timing"
:class="'s-studyplan-card timing-' + timing + (suspended?' s-suspended':'')"
>
<template #header></template>
@ -78,8 +90,11 @@ export default {
<div class='s-studyplan-card-info'>
<div class='s-studyplan-card-titlebar'>
<b-card-title>
<a class='title' v-if='open' href='#' @click.prevent='onOpenClick($event)'>{{value.name}}</a>
<a class='title' v-if='open && !suspended'
href='#' @click.prevent='onOpenClick($event)'>{{value.name}}</a>
<template v-else>{{value.name}}</template>
<div v-if="suspended" class='text-danger'
><fittext maxsize="12pt">{{text.suspended}}</fittext></div>
</b-card-title>
<div class='s-studyplan-card-titleslot'><slot name='title'></slot></div>
</div>
@ -87,7 +102,7 @@ export default {
{{ text.idnumber }}: {{ value.idnumber }}
</div>
<s-progress-bar
v-if='value.progress !== undefined && value.progress !== null'
v-if='!suspended && value.progress !== undefined && value.progress !== null'
v-model="value.progress"
></s-progress-bar>
</div>
@ -102,7 +117,7 @@ export default {
v-model="value"
v-if="value.description"
><i class='fa fa-info-circle'></i></s-studyplan-details>
<b-button style="float:right;" v-if='open' variant='primary'
<b-button style="float:right;" v-if='open && !suspended' variant='primary'
@click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>
</span>
</template>

View File

@ -6,6 +6,7 @@ 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\studyplanservice;
use local_treestudyplan\courseservice;
use local_treestudyplan\form\text_integer;
@ -214,6 +215,14 @@ class studyplan_editform extends formbase {
$mform->setType($field, PARAM_INT);
$mform->addRule($field, null, 'required', null, 'client');
} else {
// These fields are only relevant if the studyplan is edited
$field = 'suspended';
$mform->addElement('advcheckbox',$field,
get_string('studyplan_suspend','local_treestudyplan'),
get_string('studyplan_suspend_details','local_treestudyplan'),
[],
);
}
$aggregators = [];
@ -339,6 +348,7 @@ class studyplan_editform extends formbase {
'descriptionformat' => $entry->descriptionformat,
'aggregation' => $entry->aggregation,
'aggregation_config' => $aggregation_config,
'suspended' => $entry->suspended,
]);
}

View File

@ -119,7 +119,7 @@ class studentstudyplanservice extends \external_api {
global $CFG, $DB;
$studyplan = studyplan::find_by_id($studyplanid);
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_VIEWOTHER, $studyplan->context());
@ -285,7 +285,12 @@ class studentstudyplanservice extends \external_api {
$userid = $invite->user_id;
if ($studyplan->exist_for_user($userid)) {
return $studyplan->user_model($userid);
if (!$studyplan->suspended()) {
return $studyplan->user_model($userid);
} else {
throw new \moodle_exception("The selected studyplan is suspended");
}
} else {
throw new \moodle_exception("Invitation's user is not linked to this studyplan");
}
@ -424,7 +429,7 @@ class studentstudyplanservice extends \external_api {
$studyplan = studyplan::find_by_id($studyplanid);
if ($studyplan->exist_for_user($userid)) {
if ($studyplan->exist_for_user($userid) && !$studyplan->suspended()) {
$model = $studyplan->user_model($userid);
return $model;
} else {
@ -470,7 +475,7 @@ class studentstudyplanservice extends \external_api {
$page = studyplanpage::find_by_id($pageid);
$studyplan = $page->studyplan();
if ($studyplan->exist_for_user($userid)) {
if ($studyplan->exist_for_user($userid && !$studyplan->suspended())) {
return $page->user_model($userid);
} else {
throw new \moodle_exception("You do not have access to this studyplan page");

View File

@ -122,6 +122,15 @@ class studyplan {
return $this->r->name;
}
/**
* True if studyplan is suspended
* @return string
*/
public function suspended() {
return boolval($this->r->suspended);
}
private function icon() {
global $CFG;
$fs = \get_file_storage();
@ -235,6 +244,7 @@ class studyplan {
"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),
"suspended" => new \external_value(PARAM_BOOL, 'if studyplan is suspended',VALUE_OPTIONAL),
], 'Basic studyplan info', $value);
}
@ -262,6 +272,7 @@ class studyplan {
'aggregation_config' => $this->aggregator->config_string(),
'aggregation_info' => $this->aggregator->basic_model(),
'pages' => $pages,
'suspended' => boolval($this->r->suspended),
];
if(isset($userid)) {
$model["userid"] = $userid;
@ -297,7 +308,8 @@ class studyplan {
])),
], "Scale forcing on stuff", VALUE_OPTIONAL),
], "Advanced features available", VALUE_OPTIONAL),
], 'Studyplan full structure', $value);
"suspended" => new \external_value(PARAM_BOOL, 'if studyplan is suspended',VALUE_OPTIONAL),
], 'Studyplan full structure', $value);
}
/**
@ -320,6 +332,7 @@ class studyplan {
"aggregation_config" => $this->aggregator->config_string(),
'aggregation_info' => $this->aggregator->basic_model(),
'pages' => [],
'suspended' => boolval($this->r->suspended),
];
foreach ($this->pages() as $p) {
@ -392,12 +405,22 @@ class studyplan {
/**
* Edit study line properties
* @param array $fields Changed roperties for study line ['name', 'shortname', 'description', 'idnumber',
* 'context_id', 'aggregation', 'aggregation_config']
* @param array $fields Changed properties for study line ['name', 'shortname', 'description', 'idnumber',
* 'context_id', 'aggregation', 'aggregation_config', 'suspended']
*/
public function edit($fields) : self {
global $DB;
$editable = ['name', 'shortname', 'description', 'descriptionformat', 'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
$editable = [
'name',
'shortname',
'description',
'descriptionformat',
'idnumber',
'context_id',
'aggregation',
'aggregation_config',
'suspended'
];
$info = ['id' => $this->id, ];
foreach ($editable as $f) {
if (array_key_exists($f, $fields)) {

View File

@ -127,15 +127,19 @@ class studyplanservice extends \external_api {
if (isset($id) && $id > 0) {
$studyplan = studyplan::find_by_id($id);
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities([self::CAP_EDIT, self::CAP_VIEW], $studyplan->context());
}
$model = $studyplan->editor_model();
debug::dump($model);
return $model;
// 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 {
$model = $studyplan->editor_model();
return $model;
}
} else {
return null;
}
@ -635,7 +639,7 @@ class studyplanservice extends \external_api {
public static function add_studyitem($lineid, $type, $details, $slot = -1, $layer = 0) {
$line = studyline::find_by_id($lineid);
$studyplan = $line->studyplan();
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
@ -689,7 +693,7 @@ class studyplanservice extends \external_api {
$o = studyitem::find_by_id($id);
$studyplan = $o->studyline()->studyplan();
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $o->context());
@ -747,7 +751,7 @@ class studyplanservice extends \external_api {
$item = studyitem::find_by_id(($sq['id']));
$studyplan = $item->studyline()->studyplan();
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
@ -787,7 +791,7 @@ class studyplanservice extends \external_api {
public static function delete_studyitem($id) {
$o = studyitem::find_by_id($id);
$studyplan = $o->studyline()->studyplan();
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $o->context());
@ -832,7 +836,7 @@ class studyplanservice extends \external_api {
if ($toplan->id() != $studyplan->id()) {
throw new \webservice_access_exception("The items to connect need to be in the same studyplan" );
}
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
@ -878,7 +882,7 @@ class studyplanservice extends \external_api {
if ($toplan->id() != $studyplan->id()) {
throw new \webservice_access_exception("The items to connect need to be in the same studyplan" );
}
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
@ -1001,7 +1005,7 @@ class studyplanservice extends \external_api {
$results = [];
$page = studyplanpage::find_by_id(($pageid));
$studyplan = $page->studyplan();
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
@ -1059,8 +1063,9 @@ class studyplanservice extends \external_api {
$studyplan = studyitem::find_by_id($itemid)->studyline()->studyplan();
\external_api::validate_context($studyplan->context());
// Check correct capabilities.
if ($studyplan->is_coach() || has_capability(self::CAP_EDIT, $studyplan->context()) ||
is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables')) {
if (($studyplan->is_coach() && !$studyplan->suspended())
|| has_capability(self::CAP_EDIT, $studyplan->context())
|| is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables')) {
return gradeinfo::include_grade($gradeid, $itemid, $include, $required)->model();
} else {
return success::fail("Access denied")->model();
@ -1114,8 +1119,9 @@ class studyplanservice extends \external_api {
// Check correct capabilities.
$studyplan = $item->studyline()->studyplan();
if ($studyplan->is_coach() || has_capability('local/treestudyplan:editstudyplan', $studyplan->context()) ||
($coursecontext && is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables'))) {
if (($studyplan->is_coach() && !$studyplan->suspended())
|| has_capability('local/treestudyplan:editstudyplan', $studyplan->context())
|| ($coursecontext && is_enrolled($coursecontext, $USER, 'local/treestudyplan:selectowngradables'))) {
return coursecompetencyinfo::require_competency($competencyid, $itemid, $required)->model();
} else {
return success::fail("Access denied")->model();
@ -1841,7 +1847,7 @@ class studyplanservice extends \external_api {
public static function set_studyitem_span($id, $span = null) {
$o = studyitem::find_by_id($id);
$studyplan = $o->studyline()->studyplan();
if ($studyplan->is_coach()) {
if ($studyplan->is_coach() && !$studyplan->suspended()) {
\external_api::validate_context($studyplan->context());
} else {
webservicehelper::require_capabilities(self::CAP_EDIT, $o->context());

View File

@ -1487,6 +1487,14 @@ body.path-local-treestudyplan .editmode-switch-form > * {
padding: 0.75rem;
padding-bottom: 0.5rem;
}
.path-local-treestudyplan .card.s-studyplan-card.s-suspended .card-body,
.features-treestudyplan .card.s-studyplan-card.s-suspended .card-body {
background-color: #eee;
}
.path-local-treestudyplan .card.s-studyplan-card.s-suspended .card-footer,
.features-treestudyplan .card.s-studyplan-card.s-suspended .card-footer {
background-color: #ddd;
}
.path-local-treestudyplan .card.s-studyplan-card.timing-past .card-header,
.features-treestudyplan .card.s-studyplan-card.timing-past .card-header {
background-color: var(--past);

View File

@ -603,7 +603,7 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
// Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2024030801, 'local', 'treestudyplan');
}
if ($oldversion < 2024030900) {
if ($oldversion < 2024031000) {
// Define field suspended to be added to local_treestudyplan.
$table = new xmldb_table('local_treestudyplan');
@ -615,7 +615,7 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
}
// Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2024030900, 'local', 'treestudyplan');
upgrade_plugin_savepoint(true, 2024031000, 'local', 'treestudyplan');
}
return true;

View File

@ -178,6 +178,7 @@ print $OUTPUT->header();
:key='studyplan.id'
v-model='studyplans[planindex]'
open
ignoresuspend
@open='selectStudyplan(studyplan.id)'
>
<template #title>

View File

@ -170,6 +170,8 @@ $string["studyplan_enddate"] = 'End date of plan';
$string["studyplan_noneselected"] = "Pick a study plan to start editing";
$string["studyplan_select"] = "Study plan:";
$string["studyplan_select_placeholder"] = "Select study plan";
$string["studyplan_suspend"] = 'Suspend studyplan';
$string["studyplan_suspend_details"] = 'Suspend studyplan for all except studyplan managers';
$string["studyline_add"] = 'Add study line';
$string["studyline_edit"] = 'Edit study line';
@ -497,3 +499,4 @@ $string["line_can_enrol"] = 'You can register for this line';
$string["line_is_enrolled"] = 'You are registered for this line';
$string["line_enrolled_in"] = 'Registered in {$a}';
$string["switch_coach_editmode"] = "Edit studyplan";
$string["suspended"] = "Suspended";

View File

@ -170,6 +170,9 @@ $string["studyplan_enddate"] = 'Einddatum';
$string["studyplan_noneselected"] = "Kies een studieplan uit de lijst";
$string["studyplan_select"] = "Studieplan";
$string["studyplan_select_placeholder"] = "Selecteer studieplan";
$string["studyplan_suspend"] = 'Studieplan tijdelijk uitschakelen';
$string["studyplan_suspend_details"] = 'Inkijken van studieplan tijdelijk uitschakelen, behalve voor beheerders.';
$string["studyline_add"] = 'Nieuwe leerlijn';
$string["studyline_edit"] = 'Leerlijn bewerken';
@ -496,4 +499,5 @@ $string["line_cannot_enrol"] = 'Je kunt je niet zelf inschrijven voor deze leerl
$string["line_can_enrol"] = 'Je kunt jezelf inschrijven voor deze leerlijn';
$string["line_is_enrolled"] = 'Je bent ingeschreven voor deze leerlijn';
$string["line_enrolled_in"] = 'Ingeschreven in {$a}';
$string["switch_coach_editmode"] = "Studieplan bewerken";
$string["switch_coach_editmode"] = "Studieplan bewerken";
$string["suspended"] = "Tijdelijk uitgeschakeld";

View File

@ -15,6 +15,14 @@
padding: 0.75rem;
padding-bottom: 0.5rem;
}
.card.s-studyplan-card.s-suspended {
.card-body {
background-color: #eee;
}
.card-footer {
background-color: #ddd;
}
}
.card.s-studyplan-card.timing-past .card-header {
background-color: var(--past);
@ -157,4 +165,6 @@
}
}
}

View File

@ -1487,6 +1487,14 @@ body.path-local-treestudyplan .editmode-switch-form > * {
padding: 0.75rem;
padding-bottom: 0.5rem;
}
.path-local-treestudyplan .card.s-studyplan-card.s-suspended .card-body,
.features-treestudyplan .card.s-studyplan-card.s-suspended .card-body {
background-color: #eee;
}
.path-local-treestudyplan .card.s-studyplan-card.s-suspended .card-footer,
.features-treestudyplan .card.s-studyplan-card.s-suspended .card-footer {
background-color: #ddd;
}
.path-local-treestudyplan .card.s-studyplan-card.timing-past .card-header,
.features-treestudyplan .card.s-studyplan-card.timing-past .card-header {
background-color: var(--past);

View File

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