Finalized functionality for period spanning and period matching
This commit is contained in:
parent
7b36ee3284
commit
5146ebdf0a
2
amd/build/page-edit-plan.min.js
vendored
2
amd/build/page-edit-plan.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
File diff suppressed because one or more lines are too long
|
@ -194,7 +194,6 @@ export function init(contextid,categoryid) {
|
|||
window.location.search = params.toString();
|
||||
},
|
||||
onStudyPlanCreated(newstudyplan){
|
||||
debug.info("New studyplan:",newstudyplan);
|
||||
app.studyplans.push(newstudyplan);
|
||||
app.selectStudyplan(newstudyplan);
|
||||
|
||||
|
@ -213,7 +212,6 @@ export function init(contextid,categoryid) {
|
|||
methodname: 'local_treestudyplan_get_studyplan_map',
|
||||
args: { id: studyplan.id}
|
||||
}])[0].done(function(response){
|
||||
debug.info(response);
|
||||
app.activestudyplan = ProcessStudyplan(response,true);
|
||||
debug.info('studyplan processed');
|
||||
app.loadingstudyplan = false;
|
||||
|
|
|
@ -24,6 +24,12 @@ const PERIOD_EDITOR_FIELDS =
|
|||
|
||||
const LINE_GRAVITY = 1.3;
|
||||
|
||||
const datechanger_globals = {
|
||||
default: false,
|
||||
defaultchoice: false,
|
||||
hidewarn: false,
|
||||
};
|
||||
|
||||
export default {
|
||||
STUDYPLAN_EDITOR_FIELDS: STUDYPLAN_EDITOR_FIELDS, // make copy available in plugin
|
||||
install(Vue/*,options*/){
|
||||
|
@ -158,6 +164,8 @@ export default {
|
|||
desc: 'course_timing_desc',
|
||||
question: 'course_timing_question',
|
||||
warning: 'course_timing_warning',
|
||||
timing_ok: 'course_timing_ok',
|
||||
timing_off: 'course_timing_off',
|
||||
course: 'course$core',
|
||||
period: 'period',
|
||||
yes: 'yes$core',
|
||||
|
@ -171,6 +179,7 @@ export default {
|
|||
day: 'day$core',
|
||||
rememberchoice: 'course_timing_rememberchoice',
|
||||
hidewarning: 'course_timing_hidewarning',
|
||||
periodspan: 'course_period_span',
|
||||
},
|
||||
studyplan_associate: {
|
||||
associations: 'associations',
|
||||
|
@ -199,7 +208,8 @@ export default {
|
|||
coursetiming_present: "coursetiming_present",
|
||||
coursetiming_future: "coursetiming_future",
|
||||
grade_include: "grade_include",
|
||||
grade_require: "grade_require",
|
||||
grade_require: "grade_require",
|
||||
ok: "ok$core",
|
||||
},
|
||||
invalid: {
|
||||
error: 'error',
|
||||
|
@ -1262,7 +1272,6 @@ export default {
|
|||
this.$root.$emit('redrawLines');
|
||||
},
|
||||
updated() {
|
||||
console.info("UPDATED Studyplan");
|
||||
this.$root.$emit('redrawLines');
|
||||
ItemEventBus.$emit('redrawLines');
|
||||
},
|
||||
|
@ -1334,7 +1343,6 @@ export default {
|
|||
'sequence': page.studylines.length,
|
||||
}
|
||||
}])[0].done(function(response){
|
||||
debug.info("New studyline:",response);
|
||||
page.studylines.push(response);
|
||||
newlineinfo.name = '';
|
||||
newlineinfo.shortname = '';
|
||||
|
@ -1349,7 +1357,6 @@ export default {
|
|||
editLineFinish() {
|
||||
let editedline = this.edit.studyline.data;
|
||||
let originalline = this.edit.studyline.original;
|
||||
debug.info('Edit Line',this.edit.studyline);
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_edit_studyline',
|
||||
args: { 'id': editedline.id,
|
||||
|
@ -1357,14 +1364,12 @@ export default {
|
|||
'shortname': editedline.shortname,
|
||||
'color': editedline.color,}
|
||||
}])[0].done(function(response){
|
||||
debug.info('Edit response:', response);
|
||||
originalline['name'] = response['name'];
|
||||
originalline['shortname'] = response['shortname'];
|
||||
originalline['color'] = response['color'];
|
||||
}).fail(notification.exception);
|
||||
},
|
||||
deleteLine(page,line) {
|
||||
debug.info('Delete Line',line);
|
||||
const self=this;
|
||||
get_strings([
|
||||
{key: 'studyline_confirm_remove', param: line.name, component: 'local_treestudyplan' },
|
||||
|
@ -1379,7 +1384,6 @@ export default {
|
|||
methodname: 'local_treestudyplan_delete_studyline',
|
||||
args: { 'id': line.id, }
|
||||
}])[0].done(function(response){
|
||||
debug.info('Delete response:', response);
|
||||
if(response.success == true){
|
||||
let index = page.studylines.indexOf(line);
|
||||
page.studylines.splice(index, 1);
|
||||
|
@ -1390,7 +1394,6 @@ export default {
|
|||
});
|
||||
},
|
||||
reorderLines(event,lines){
|
||||
debug.info("Reorder lines",event,lines);
|
||||
|
||||
// apply reordering
|
||||
event.apply(lines);
|
||||
|
@ -1404,12 +1407,10 @@ export default {
|
|||
methodname: 'local_treestudyplan_reorder_studylines',
|
||||
args: { 'sequence': sequence }
|
||||
}])[0].done(function(response){
|
||||
debug.info('Reorder response:', response);
|
||||
}).fail(notification.exception);
|
||||
},
|
||||
deletePlan(studyplan){
|
||||
const self=this;
|
||||
debug.info('Delete studyplan:', studyplan);
|
||||
get_strings([
|
||||
{key: 'studyplan_confirm_remove', param: studyplan.name, component: 'local_treestudyplan' },
|
||||
{key: 'delete', component: 'core' },
|
||||
|
@ -1423,7 +1424,6 @@ export default {
|
|||
methodname: 'local_treestudyplan_delete_studyplan',
|
||||
args: { 'id': studyplan.id, }
|
||||
}])[0].done(function(response){
|
||||
debug.info('Delete response:', response);
|
||||
if(response.success == true){
|
||||
self.$root.$emit("studyplanRemoved",studyplan);
|
||||
}
|
||||
|
@ -1433,7 +1433,6 @@ export default {
|
|||
});
|
||||
},
|
||||
deleteStudyItem(event){
|
||||
debug.info('Delete studyitem:', event);
|
||||
//const self = this;
|
||||
let item = event.data;
|
||||
|
||||
|
@ -1441,7 +1440,6 @@ export default {
|
|||
methodname: 'local_treestudyplan_delete_studyitem',
|
||||
args: { 'id': item.id, }
|
||||
}])[0].done(function(response){
|
||||
debug.info('Delete response:', response);
|
||||
if(response.success == true){
|
||||
event.source.$emit('cut',event);
|
||||
}
|
||||
|
@ -1952,10 +1950,10 @@ export default {
|
|||
// then on the next tick, we inform the back end
|
||||
// Since moving things around has never been unsuccessful, unless you have other problems,
|
||||
// it's better to have nice visuals.
|
||||
this.$nextTick(() => {
|
||||
self.relocateStudyItem(item).done(()=>{
|
||||
self.validate_course_period();
|
||||
});
|
||||
self.relocateStudyItem(item).done(()=>{
|
||||
if(this.$refs.timingChecker){
|
||||
this.$refs.timingChecker.validate_course_period();
|
||||
}
|
||||
});
|
||||
}
|
||||
else if(event.type.component){
|
||||
|
@ -1977,14 +1975,18 @@ export default {
|
|||
}
|
||||
}
|
||||
}])[0].done((response) => {
|
||||
console.info('Add item response:', response);
|
||||
let item = response;
|
||||
self.relocateStudyItem(item).done(()=>{
|
||||
self.value.push(item);
|
||||
self.$emit("input",self.value);
|
||||
|
||||
// call the validate period function on next tick,
|
||||
// since it paints the item in the slot first
|
||||
this.$nextTick(self.validate_course_period);
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.timingChecker){
|
||||
this.$refs.timingChecker.validate_course_period();
|
||||
}
|
||||
});
|
||||
});
|
||||
}).fail(notification.exception);
|
||||
}
|
||||
|
@ -2000,7 +2002,6 @@ export default {
|
|||
}
|
||||
}
|
||||
}])[0].done((response) => {
|
||||
console.info('Add item response:', response);
|
||||
let item = response;
|
||||
self.relocateStudyItem(item).done(()=>{
|
||||
self.value.push(item);
|
||||
|
@ -2037,58 +2038,6 @@ export default {
|
|||
this.hover.component = null;
|
||||
this.hover.type = null;
|
||||
},
|
||||
validate_course_period() {
|
||||
const self = this;
|
||||
debug.info("Validating course and period",self.item,self.period);
|
||||
if(self.item && self.item.type == 'course'){
|
||||
self.datechanger.coursespan = datespaninfo(self.item.course.startdate,self.item.course.enddate);
|
||||
self.datechanger.periodspan = datespaninfo(self.period.startdate,self.period.enddate);
|
||||
if( self.datechanger.coursespan.first != self.datechanger.periodspan.first
|
||||
|| self.datechanger.coursespan.last != self.datechanger.periodspan.last ){
|
||||
debug.info("Course timing does not match period timing");
|
||||
|
||||
if(self.item.course.canupdatecourse){
|
||||
if(!self.datechanger.default){
|
||||
// Periods do not match, pop up the date change request
|
||||
this.$bvModal.show("t-course-date-matching-"+this.slotkey);
|
||||
} else if (self.datechanger.defaultvalue){
|
||||
// go for it without asking
|
||||
self.change_course_period();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// user is not able to change course timing - show a warning
|
||||
if(!self.datechanger.hidewarn){
|
||||
this.$bvModal.show("t-course-date-warning-"+this.slotkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug.info("Course timing matches period",self.datechanger);
|
||||
}
|
||||
}
|
||||
},
|
||||
change_course_period() {
|
||||
const self=this;
|
||||
return call([{
|
||||
methodname: 'local_treestudyplan_course_period_timing',
|
||||
args: { page_id: self.page_id,
|
||||
period: self.period.period,
|
||||
course_id: this.item.course.id,
|
||||
}
|
||||
}])[0].fail(notification.exception);
|
||||
},
|
||||
format_duration(dsi){
|
||||
let s = "";
|
||||
if(dsi.years == 1){ s += `1 ${this.text.year}, `;}
|
||||
else if(dsi.years > 1){ s += `${dsi.years} ${this.text.years}, `;}
|
||||
if(dsi.weeks == 1){ s += `1 ${this.text.week}, `;}
|
||||
else if(dsi.weeks > 1){ s += `${dsi.weeks} ${this.text.weeks}, `;}
|
||||
if(dsi.days == 1){ s += `1 ${this.text.day}, `;}
|
||||
else if(dsi.days > 1){ s += `${dsi.days} ${this.text.days}, `;}
|
||||
|
||||
return s.toLocaleLowerCase();
|
||||
},
|
||||
maxSpan(){
|
||||
// Determine the maximum span for components in this slot
|
||||
// Used for setting the max in the timing adjustment screen (s)
|
||||
|
@ -2100,7 +2049,8 @@ export default {
|
|||
for(let i = this.slotindex + 1; i <= this.page.periods; i++){
|
||||
if(this.line.slots && this.line.slots[i] && this.line.slots[i].competencies){
|
||||
const l = this.line.slots[i].competencies;
|
||||
if(l[this.layer]) {
|
||||
const f = this.line.slots[i-1].filters; // next filter is in the same slot
|
||||
if(l[this.layer] || f[this.layer]) {
|
||||
// slot is busy in this layer.
|
||||
break;
|
||||
} else {
|
||||
|
@ -2148,7 +2098,7 @@ export default {
|
|||
:data="item"
|
||||
:type="makeType(item)"
|
||||
@cut="onCut"
|
||||
><t-item v-model="item" :plan="plan"></t-item
|
||||
><t-item v-model="item" :plan="plan" :page='page' :period='period' :maxspan='maxSpan()'></t-item
|
||||
></drag
|
||||
><drop v-else
|
||||
:class="'t-slot-drop '+type + (layer > 0?' secondary':' primary')"
|
||||
|
@ -2180,8 +2130,171 @@ export default {
|
|||
:key="hover.type">--{{ hover.type }}--</div
|
||||
></template
|
||||
></drop>
|
||||
<t-item-timing-checker hidden
|
||||
v-if="item && item.course"
|
||||
ref="timingChecker"
|
||||
:maxspan="maxSpan()"
|
||||
:page="page"
|
||||
:period="period"
|
||||
v-model="item"
|
||||
></t-item-timing-checker>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
|
||||
Vue.component('t-item-timing-checker', {
|
||||
props: {
|
||||
value: {
|
||||
type: Object, // t-item model
|
||||
},
|
||||
page: {
|
||||
type: Object, // Studyplan data
|
||||
},
|
||||
period: {
|
||||
type: Object, // Studyplan data
|
||||
},
|
||||
maxspan: {
|
||||
type: Number,
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
endperiod() {
|
||||
const endperiodnr = Math.min(this.page.periods,this.period.period + (this.value.span - 1));
|
||||
return this.page.perioddesc[endperiodnr-1];
|
||||
},
|
||||
course_period_matches() {
|
||||
const self=this;
|
||||
if(self.value && self.value.type == 'course'){
|
||||
self.datechanger.coursespan = datespaninfo(self.value.course.startdate,self.value.course.enddate);
|
||||
self.datechanger.periodspan = datespaninfo(self.period.startdate,self.endperiod.enddate);
|
||||
if( self.datechanger.coursespan.first.getTime() == self.datechanger.periodspan.first.getTime()
|
||||
&& self.datechanger.coursespan.last.getTime() == self.datechanger.periodspan.last.getTime() ){
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
debug.warn("Timing thing not proper configured",self.value,self.period,self.maxspan);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// Create random id to avoid opening the wrong modals
|
||||
id: Math.floor(Math.random() * Date.now()).toString(16),
|
||||
text: strings.course_timing,
|
||||
datechanger: {
|
||||
coursespan: null,
|
||||
periodspan: null,
|
||||
globals: datechanger_globals,
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
||||
validate_course_period(force) {
|
||||
const self = this;
|
||||
|
||||
debug.info("Validating course and period");
|
||||
if(!(self.course_period_matches)){
|
||||
debug.info("Course timing does not match period timing");
|
||||
|
||||
if(self.value.course.canupdatecourse){
|
||||
if(!self.hidden || !self.datechanger.globals.default){
|
||||
// Periods do not match, pop up the date change request
|
||||
this.$bvModal.show('t-course-timing-matching-'+this.id);
|
||||
} else if (self.datechanger.globals.defaultvalue){
|
||||
// go for it without asking
|
||||
self.change_course_period();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// user is not able to change course timing - show a warning
|
||||
if(!self.hidden || !self.datechanger.globals.hidewarn){
|
||||
this.$bvModal.show('t-course-timing-warning-'+this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug.info("Course timing matches period",self.datechanger);
|
||||
}
|
||||
},
|
||||
change_course_period() {
|
||||
const self=this;
|
||||
// Save the state
|
||||
if(self.datechanger.globals.default){
|
||||
self.datechanger.globals.defaultvalue = true;
|
||||
}
|
||||
return call([{
|
||||
methodname: 'local_treestudyplan_course_period_timing',
|
||||
args: { period_id: self.period.id,
|
||||
course_id: this.value.course.id,
|
||||
span: this.value.span,
|
||||
}
|
||||
}])[0].fail(notification.exception).done((response) => {
|
||||
self.value.course.startdate = response.startdate;
|
||||
self.value.course.enddate = response.enddate;
|
||||
self.value.course.timing = response.timing;
|
||||
self.$emit("input",self.value);
|
||||
});
|
||||
},
|
||||
change_span(span) {
|
||||
const self=this;
|
||||
return call([{
|
||||
methodname: 'local_treestudyplan_set_studyitem_span',
|
||||
args: { id: self.value.id,
|
||||
span: span
|
||||
}
|
||||
}])[0].fail(notification.exception).done((response) => {
|
||||
self.value.span = response.span;
|
||||
self.$emit('input',self.value);
|
||||
self.$nextTick(() => {
|
||||
self.validate_course_period();
|
||||
});
|
||||
} );
|
||||
},
|
||||
format_duration(dsi){
|
||||
let s = "";
|
||||
if(dsi.years == 1){ s += `1 ${this.text.year}, `;}
|
||||
else if(dsi.years > 1){ s += `${dsi.years} ${this.text.years}, `;}
|
||||
if(dsi.weeks == 1){ s += `1 ${this.text.week}, `;}
|
||||
else if(dsi.weeks > 1){ s += `${dsi.weeks} ${this.text.weeks}, `;}
|
||||
if(dsi.days == 1){ s += `1 ${this.text.day}, `;}
|
||||
else if(dsi.days > 1){ s += `${dsi.days} ${this.text.days}, `;}
|
||||
|
||||
return s.toLocaleLowerCase();
|
||||
},
|
||||
},
|
||||
// To avoid the span creeping in the dom where it shouldn't, set display to none if it is hidden
|
||||
// This does not affect the modals, which are rendered outside of this element when needed
|
||||
template: `
|
||||
<span :class="'t-course-timing-matcher'" :style="hidden?'display: none ':''">
|
||||
<span v-if="!hidden">
|
||||
<i v-if="course_period_matches" class="text-success fa fa-calendar-check-o"
|
||||
v-b-tooltip.hover.topright :title="text.timing_ok"
|
||||
></i
|
||||
><a v-else
|
||||
href='#' @click="validate_course_period()" class="text-warning"
|
||||
><i class="fa fa-calendar-times-o" v-b-tooltip.hover.topright :title="text.timing_off"
|
||||
></i
|
||||
></a>
|
||||
<span v-if='value.span > 1 || value.span < maxspan' >
|
||||
{{ text.periodspan}}
|
||||
<b-form-select @change="change_span" v-model="value.span">
|
||||
<b-form-select-option v-for="(n,i) in maxspan" :value='n'
|
||||
>{{ n }}</b-form-select-option>
|
||||
</b-form-select>
|
||||
</span>
|
||||
</span>
|
||||
<b-modal
|
||||
:id="'t-course-date-matching-'+this.slotkey"
|
||||
:id="'t-course-timing-matching-'+this.id"
|
||||
size="lg"
|
||||
:title="text.title"
|
||||
@ok="change_course_period"
|
||||
|
@ -2190,85 +2303,97 @@ export default {
|
|||
:cancel-title="text.no"
|
||||
cancel-variant="primary"
|
||||
>
|
||||
<b-container v-if="datechanger.coursespan && datechanger.periodspan && item && item.course">
|
||||
<b-container v-if="datechanger.coursespan && datechanger.periodspan && value && value.course">
|
||||
<b-row><b-col cols="12">{{ text.desc }}</b-col></b-row>
|
||||
<b-row><b-col cols="12"><div class="generalbox alert alert-warning">{{ text.question }}</div></b-col></b-row>
|
||||
<b-row>
|
||||
<b-col cols="6">
|
||||
<h3> {{ text.course }} </h3>
|
||||
<p><b>{{ item.course.fullname }}</b></p>
|
||||
<p><b>{{ item.course.shortname }}</b></p>
|
||||
<p><b>{{ value.course.fullname }}</b></p>
|
||||
<p><b>{{ value.course.shortname }}</b></p>
|
||||
<p>{{ datechanger.coursespan.formatted.first}} - {{ datechanger.coursespan.formatted.last}}</p>
|
||||
<p><b>{{ text.duration }}</b><br>
|
||||
{{ format_duration(datechanger.coursespan)}}</p>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<h3> {{ text.period }} </h3>
|
||||
<p><b>{{ period.fullname }}</b></p>
|
||||
<p><b>{{ period.shortname }}</b></p>
|
||||
<p><b>{{ period.fullname }}</b><b v-if="value.span > 1"> - {{ endperiod.fullname }}</b></p>
|
||||
<p><b>{{ period.shortname }}</b><b v-if="value.span > 1"> - {{ endperiod.shortname }}</b></p>
|
||||
<p>{{ datechanger.periodspan.formatted.first}} - {{ datechanger.periodspan.formatted.last}}</p>
|
||||
<p><b>{{ text.duration }}</b><br>
|
||||
{{ format_duration(datechanger.periodspan)}}</p>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row><b-col cols="12">
|
||||
<b-form-checkbox type="checkbox" v-model="datechanger.default">{{ text.rememberchoice }}</b-form-checkbox>
|
||||
<b-row v-if='hidden'><b-col cols="12">
|
||||
<b-form-checkbox type="checkbox" v-model="datechanger.globals.default">{{ text.rememberchoice }}</b-form-checkbox>
|
||||
</b-col></b-row>
|
||||
</b-container>
|
||||
</b-modal>
|
||||
<b-modal
|
||||
:id="'t-course-date-warning-'+this.slotkey"
|
||||
:id="'t-course-timing-warning-'+this.id"
|
||||
size="lg"
|
||||
ok-variant="primary"
|
||||
:title="text.title"
|
||||
:ok-title="text.yes"
|
||||
ok-only
|
||||
>
|
||||
<b-container v-if="datechanger.coursespan && datechanger.periodspan && item && item.course">
|
||||
<b-container v-if="datechanger.coursespan && datechanger.periodspan && value && value.course">
|
||||
<b-row><b-col cols="12">{{ text.desc }}</b-col></b-row>
|
||||
<b-row><b-col cols="12"><div class="generalbox alert alert-warning">{{ text.warning }}</div></b-col></b-row>
|
||||
<b-row>
|
||||
<b-col cols="6">
|
||||
<h3> {{ text.course }} </h3>
|
||||
<p><b>{{ item.course.fullname }}</b></p>
|
||||
<p><b>{{ item.course.shortname }}</b></p>
|
||||
<p><b>{{ value.course.fullname }}</b></p>
|
||||
<p><b>{{ value.course.shortname }}</b></p>
|
||||
<p>{{ datechanger.coursespan.formatted.first}} - {{ datechanger.coursespan.formatted.last}}</p>
|
||||
<p><b>{{ text.duration }}</b><br>
|
||||
{{ format_duration(datechanger.coursespan)}}</p>
|
||||
</b-col>
|
||||
<b-col cols=>"6">
|
||||
<h3> {{ text.period }} </h3>
|
||||
<p><b>{{ period.fullname }}</b></p>
|
||||
<p><b>{{ period.shortname }}</b></p>
|
||||
<p><b>{{ period.fullname }}</b><b v-if="value.span > 1"> - {{ endperiod.fullname }}</b></p>
|
||||
<p><b>{{ period.shortname }}</b><b v-if="value.span > 1"> - {{ endperiod.shortname }}</b></p>
|
||||
<p>{{ datechanger.periodspan.formatted.first}} - {{ datechanger.periodspan.formatted.last}}</p>
|
||||
<p><b>{{ text.duration }}</b><br>
|
||||
{{ format_duration(datechanger.periodspan)}}</p>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row><b-col cols="12">
|
||||
<b-form-checkbox type="checkbox" v-model="datechanger.hidewarn">{{ text.hidewarning }}</b-form-checkbox>
|
||||
<b-row v-if='hidden'><b-col cols="12">
|
||||
<b-form-checkbox type="checkbox" v-model="datechanger.globals.hidewarn">{{ text.hidewarning }}</b-form-checkbox>
|
||||
</b-col></b-row>
|
||||
</b-container>
|
||||
</b-modal>
|
||||
</div>
|
||||
</span>
|
||||
`,
|
||||
});
|
||||
|
||||
|
||||
Vue.component('t-item', {
|
||||
props: {
|
||||
'value' :{
|
||||
value :{
|
||||
type: Object,
|
||||
default(){ return null;},
|
||||
},
|
||||
'dummy' :{
|
||||
dummy :{
|
||||
type: Boolean,
|
||||
default() { return false;},
|
||||
},
|
||||
'plan': {
|
||||
plan: {
|
||||
type: Object, // Studyplan page
|
||||
default() { return null;},
|
||||
},
|
||||
page: {
|
||||
type: Object, // Studyplan page
|
||||
default() { return null;},
|
||||
},
|
||||
period: {
|
||||
type: Object, // Studyplan page
|
||||
default() { return null;},
|
||||
},
|
||||
maxspan: {
|
||||
type: Number,
|
||||
default() { return 0;},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -2324,9 +2449,8 @@ export default {
|
|||
call([{
|
||||
methodname: 'local_treestudyplan_connect_studyitems',
|
||||
args: { 'from_id': from_id, 'to_id': to_id }
|
||||
}])[0].done((result)=>{
|
||||
console.info("Drop result",result);
|
||||
let conn = {'id': result.id, 'from_id': result.from_id, 'to_id': result.to_id};
|
||||
}])[0].done((response)=>{
|
||||
let conn = {'id': response.id, 'from_id': response.from_id, 'to_id': response.to_id};
|
||||
ItemEventBus.$emit("createdConnection",conn);
|
||||
this.value.connections.in.push(conn);
|
||||
}).fail(notification.exception);
|
||||
|
@ -2355,12 +2479,11 @@ export default {
|
|||
},
|
||||
deleteLine(conn){
|
||||
const self = this;
|
||||
// console.info("Delete Line",conn);
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_disconnect_studyitems',
|
||||
args: { 'from_id': conn.from_id, 'to_id': conn.to_id }
|
||||
}])[0].done((result)=>{
|
||||
if(result.success){
|
||||
}])[0].done((response)=>{
|
||||
if(response.success){
|
||||
this.removeLine(conn);
|
||||
// send disconnect event on message bus, so the connection on the other end can delete it too
|
||||
ItemEventBus.$emit("connectionDisconnected",conn);
|
||||
|
@ -2397,7 +2520,6 @@ export default {
|
|||
redrawLines(){
|
||||
for(let i in this.value.connections.out){
|
||||
let conn = this.value.connections.out[i];
|
||||
// console.info('Connection out', conn);
|
||||
this.redrawLine(conn);
|
||||
}
|
||||
},
|
||||
|
@ -2405,7 +2527,6 @@ export default {
|
|||
// EVENT LISTENERS
|
||||
onCreatedConnection(conn){
|
||||
if(conn.from_id == this.value.id){
|
||||
// console.info("incomingConnection",conn);
|
||||
this.value.connections.out.push(conn);
|
||||
this.redrawLine(conn);
|
||||
}
|
||||
|
@ -2415,7 +2536,6 @@ export default {
|
|||
for(let i in this.value.connections.in){
|
||||
let c_in = this.value.connections.in[i];
|
||||
if(conn.id == c_in.id){
|
||||
// console.info("Deleting incoming connection",conn);
|
||||
self.value.connections.out.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
@ -2513,7 +2633,6 @@ export default {
|
|||
|
||||
if(!this.dummy)
|
||||
{
|
||||
// console.info('Mounted', this);
|
||||
this.redrawLines();
|
||||
setTimeout(()=>{
|
||||
ItemEventBus.$emit("rePositioned",this.value.id);
|
||||
|
@ -2547,7 +2666,7 @@ export default {
|
|||
template: `
|
||||
<div class="t-item-base" :id="'studyitem-'+value.id">
|
||||
<t-item-course v-model="value" v-if="value.type == 'course'"
|
||||
:plan='plan' ></t-item-course>
|
||||
:plan='plan' :page='page' :period='period' :maxspan='maxspan'></t-item-course>
|
||||
<t-item-junction v-model="value" v-if="value.type == 'junction'" ></t-item-junction>
|
||||
<t-item-start v-model="value" v-if="value.type == 'start'" ></t-item-start>
|
||||
<t-item-finish v-model="value" v-if="value.type == 'finish'" ></t-item-finish>
|
||||
|
@ -2636,14 +2755,26 @@ export default {
|
|||
|
||||
Vue.component('t-item-course', {
|
||||
props: {
|
||||
'value' :{
|
||||
value:{
|
||||
type: Object,
|
||||
default(){ return null;},
|
||||
},
|
||||
'plan' :{
|
||||
plan:{
|
||||
type: Object,
|
||||
default(){ return null;},
|
||||
},
|
||||
page: {
|
||||
type: Object, // PAge data
|
||||
default() { return null;}
|
||||
},
|
||||
period: {
|
||||
type: Object, // Period data
|
||||
default() { return null;}
|
||||
},
|
||||
maxspan: {
|
||||
type: Number,
|
||||
default() { return 0;}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -2788,6 +2919,20 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #modal-footer="{ ok }" class='d-flex'>
|
||||
<div class="flex-fill">
|
||||
<!-- Configure spans and timing if needed -->
|
||||
<t-item-timing-checker
|
||||
:maxspan="maxspan"
|
||||
:page="page"
|
||||
:period="period"
|
||||
v-model="value"
|
||||
></t-item-timing-checker>
|
||||
</div>
|
||||
<b-button class='' variant="primary" @click="ok()">
|
||||
{{ text.ok }}
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<t-item-course-grades
|
||||
v-if='!!value.course.grades && value.course.grades.length > 0'
|
||||
|
@ -3181,7 +3326,6 @@ export default {
|
|||
methodname: 'local_treestudyplan_get_category',
|
||||
args: { "id": this.value.id}
|
||||
}])[0].done(function(response){
|
||||
debug.info("Course info:",response);
|
||||
self.$emit('input', response);
|
||||
}).fail(notification.exception);
|
||||
}
|
||||
|
|
|
@ -124,7 +124,6 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
onHeaderHeightChange(newheight){
|
||||
//console.info("Height change event to",newheight);
|
||||
this.$refs.main.style.height = `${newheight}px`;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -214,7 +214,7 @@ class studyitem {
|
|||
|
||||
$info = ['id' => $this->id,];
|
||||
foreach($editable as $f){
|
||||
if(array_key_exists($f,$fields)){
|
||||
if(array_key_exists($f,$fields) && isset($fields[$f])){
|
||||
$info[$f] = $fields[$f];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1180,16 +1180,15 @@ class studyplanservice extends \external_api
|
|||
}
|
||||
|
||||
/************************
|
||||
* *
|
||||
* edit_period *
|
||||
* *
|
||||
************************/
|
||||
* *
|
||||
* Change course timing *
|
||||
* *
|
||||
************************/
|
||||
|
||||
public static function course_period_timing_parameters()
|
||||
{
|
||||
return new \external_function_parameters( [
|
||||
"page_id" => new \external_value(PARAM_INT, 'Studyplan page id'),
|
||||
"period" => new \external_value(PARAM_INT, 'Period number within page'),
|
||||
"period_id" => new \external_value(PARAM_INT, 'Period number within page'),
|
||||
"course_id"=> new \external_value(PARAM_INT, 'Id of course to adjust dates for'),
|
||||
"span"=> new \external_value(PARAM_INT, 'Period span (default 1)',VALUE_DEFAULT),
|
||||
]);
|
||||
|
@ -1197,43 +1196,75 @@ class studyplanservice extends \external_api
|
|||
|
||||
public static function course_period_timing_returns()
|
||||
{
|
||||
return success::structure();
|
||||
return courseinfo::editor_structure();
|
||||
}
|
||||
|
||||
public static function course_period_timing($page_id, $period, $course_id, $span=1){
|
||||
$course = \get_course($course_id);
|
||||
$coursecontext = \context_course::instance($course_id);
|
||||
$page = studyplanpage::findById($page_id);
|
||||
public static function course_period_timing($period_id, $course_id, $span=1){
|
||||
$period = period::findById($period_id);
|
||||
$periodnr = $period->period();
|
||||
$page = $period->page();
|
||||
// Check for studyplan edit permissions
|
||||
webservicehelper::require_capabilities(self::CAP_EDIT,$page->studyplan()->context());
|
||||
$course = \get_course($course_id);
|
||||
$coursecontext = \context_course::instance($course_id);
|
||||
|
||||
// Determine end period number
|
||||
$endperiod = $period + ($span -1);
|
||||
// Get the proper list of periods for this page
|
||||
$periods = period::findForPage($page);
|
||||
if(array_key_exists($period,$periods)){
|
||||
$pstart = $periods[$period];
|
||||
$pend = $periods[$endperiod];
|
||||
if(webservicehelper::has_capabilities("moodle/course:update",$coursecontext)){
|
||||
|
||||
if(webservicehelper::has_capabilities("moodle/course:update",$coursecontext)){
|
||||
// Actually perform the timing changes, while also updating the module times
|
||||
// Like what happens on a course "reset"
|
||||
$status = reset_course_userdata((object)[
|
||||
'id' => $course->id,
|
||||
'reset_start_date' => $pstart->startdate()->getTimestamp(),
|
||||
'reset_end_date' => $pend->enddate()->getTimestamp(),
|
||||
'reset_start_date_old' => $course->startdate,
|
||||
'reset_end_date_old' => $course->enddate,
|
||||
// Get the proper list of all the periods for this page
|
||||
$periods = period::findForPage($page);
|
||||
|
||||
$pstart = $periods[$periodnr];
|
||||
|
||||
]);
|
||||
|
||||
return success::success()->model();
|
||||
} else {
|
||||
// probably should return a nice message
|
||||
return success::fail("You do not have date change permissions on this course")->model();
|
||||
// Determine end period number - Clip span between 1 and last period
|
||||
if($span <= 1){
|
||||
$pend = $pstart;
|
||||
}
|
||||
else if ($periodnr + ($span - 1) > $page->periods()){
|
||||
$pend = $periods[$page->periods()];
|
||||
}
|
||||
else {
|
||||
$pend = $periods[$periodnr + ($span - 1)];
|
||||
}
|
||||
|
||||
// Actually perform the timing changes, while also updating the module times
|
||||
// Like what happens on a course "reset"
|
||||
reset_course_userdata((object)[
|
||||
'id' => $course->id,
|
||||
'reset_start_date' => $pstart->startdate()->getTimestamp(),
|
||||
'reset_end_date' => $pend->enddate()->getTimestamp(),
|
||||
'reset_start_date_old' => $course->startdate,
|
||||
'reset_end_date_old' => $course->enddate,
|
||||
]);
|
||||
|
||||
return (new courseinfo($course->id))->editor_model();
|
||||
} else {
|
||||
// probably should return a nice message
|
||||
throw new \webservice_access_exception("You do not have date change permissions on this course");
|
||||
}
|
||||
}
|
||||
|
||||
public static function set_studyitem_span_parameters()
|
||||
{
|
||||
return new \external_function_parameters( [
|
||||
"id" => new \external_value(PARAM_INT, 'id of study item'),
|
||||
"span" => new \external_value(PARAM_INT, 'span of item'),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function set_studyitem_span_returns()
|
||||
{
|
||||
return studyitem::editor_structure();
|
||||
}
|
||||
|
||||
public static function set_studyitem_span($id,$span=null)
|
||||
{
|
||||
$o = studyitem::findById($id);
|
||||
webservicehelper::require_capabilities(self::CAP_EDIT,$o->context());
|
||||
|
||||
$config = [ 'span' => $span];
|
||||
$o->edit($config);
|
||||
return $o->editor_model();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -70,7 +70,7 @@ class teachingfinder {
|
|||
foreach($list as $page_id){
|
||||
// Retrieve the studyplan id from the page
|
||||
//TODO: Change this when page management is implemented to return the page instead of the plan
|
||||
$planid = $DB->get_field("local_treestudyplan_page","studyplan_id",["page_id" => $page_id]);
|
||||
$planid = $DB->get_field("local_treestudyplan_page","studyplan_id",["id" => $page_id]);
|
||||
$DB->insert_record(self::TABLE,["teacher_id"=>$userid,"studyplan_id"=>$planid,"update_time"=>$now]);
|
||||
}
|
||||
|
||||
|
|
|
@ -542,4 +542,22 @@ $functions = [
|
|||
'ajax' => true,
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'local_treestudyplan_course_period_timing' => [ //web service function name
|
||||
'classname' => '\local_treestudyplan\studyplanservice', //class containing the external function
|
||||
'methodname' => 'course_period_timing', //external function name
|
||||
'description' => 'Chenge course start and end times to match period', //human readable description of the web service function
|
||||
'type' => 'read', //database rights of the web service function (read, write)
|
||||
'capabilities' => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
|
||||
'ajax' => true,
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'local_treestudyplan_set_studyitem_span' => [ //web service function name
|
||||
'classname' => '\local_treestudyplan\studyplanservice', //class containing the external function
|
||||
'methodname' => 'set_studyitem_span', //external function name
|
||||
'description' => 'Change the span of a course item', //human readable description of the web service function
|
||||
'type' => 'read', //database rights of the web service function (read, write)
|
||||
'capabilities' => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
|
||||
'ajax' => true,
|
||||
'loginrequired' => true,
|
||||
],
|
||||
];
|
||||
|
|
|
@ -292,10 +292,13 @@ $string["badgeissuedstats"] = "Issuing progress";
|
|||
$string["period_default_fullname"] = 'Period {$a}';
|
||||
$string["period_default_shortname"] = 'P{$a}';
|
||||
$string["course_timing_title"] = 'Course timing does not match period timing';
|
||||
$string["course_timing_desc"] = 'The start and end date of the course you are dropping into this period does not match the start and end date of the period.';
|
||||
$string["course_timing_desc"] = 'The start and end date of the course do not match the start and end date of it\'s period(s) in the studyplan.';
|
||||
$string["course_timing_question"] = 'Do you want to update the course\'s start and end time to match that of the period?';
|
||||
$string["course_timing_warning"] = 'You do not have permission to automatically update this course start and end date. Automatic timing update not available';
|
||||
$string["period"] = 'Period';
|
||||
$string["duration"] = 'Duration';
|
||||
$string["course_timing_rememberchoice"] = 'Remember my choice for future date mismatches';
|
||||
$string["course_timing_hidewarning"] = 'Hide this warning next time';
|
||||
$string["course_timing_hidewarning"] = 'Hide this warning next time';
|
||||
$string["course_timing_ok"] = 'Course timing does matches period timing';
|
||||
$string["course_timing_off"] = 'Course timing does not match period timing';
|
||||
$string["course_period_span"] = 'Number of periods';
|
|
@ -301,4 +301,7 @@ $string["course_timing_warning"] = 'Je hebt geen rechten om de start- en eindtij
|
|||
$string["period"] = 'Periode';
|
||||
$string["duration"] = 'Duur';
|
||||
$string["course_timing_rememberchoice'"] = 'Onthoud mijn keuze voor toekomstige mismatches tussen cursus en periode';
|
||||
$string["course_timing_hidewarning"] = 'Hide this warning next time';
|
||||
$string["course_timing_hidewarning"] = 'Hide this warning next time';
|
||||
$string["course_timing_ok"] = 'Cursustiming en periodetiming komen overeen';
|
||||
$string["course_timing_off"] = 'Cursustiming en periodetiming komen niet overeen';
|
||||
$string["course_period_span"] = 'Aantal perioden';
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494)
|
||||
$plugin->version = 2023080300; // YYYYMMDDHH (year, month, day, iteration)
|
||||
$plugin->version = 2023080500; // YYYYMMDDHH (year, month, day, iteration)
|
||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11)
|
||||
|
||||
$plugin->dependencies = [
|
||||
|
|
Loading…
Reference in New Issue
Block a user