Finished custom fields in course popup. Added optional progress bar in course popup bar. Added progress circles to manual aggregation.
This commit is contained in:
parent
5544d57f6b
commit
bfcd41dd81
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/simpleline/simpleline.min.js
vendored
2
amd/build/simpleline/simpleline.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
2
amd/build/treestudyplan-components.min.js
vendored
2
amd/build/treestudyplan-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/util/date-helper.min.js
vendored
2
amd/build/util/date-helper.min.js
vendored
|
@ -1,3 +1,3 @@
|
|||
define("local_treestudyplan/util/date-helper",["exports"],(function(_exports){function format_date(d,short){d instanceof Date||(d=new Date(d));let monthformat="short";return short&&(monthformat="numeric"),d.toLocaleDateString(document.documentElement.lang,{year:"numeric",month:monthformat,day:"numeric"})}function studyplanDates(plan){let earliestStart=null,latestEnd=null,openEnded=!1;for(const ix in plan.pages){const page=plan.pages[ix],s=new Date(page.startdate);if(page.enddate||(openEnded=!0),(!earliestStart||s<earliestStart)&&(earliestStart=s),page.enddate){const e=new Date(page.enddate);(!latestEnd||e>latestEnd)&&(latestEnd=e)}}return{start:earliestStart,end:openEnded?null:latestEnd}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.add_days=function(datestr,days){const date=new Date(datestr);return function(date){const d=new Date(date);let month=""+(d.getMonth()+1),day=""+d.getDate();const year=d.getFullYear();month.length<2&&(month="0"+month);day.length<2&&(day="0"+day);return[year,month,day].join("-")}(new Date(date.getTime()+864e5*days))},_exports.datespaninfo=function(first,last){first instanceof Date||(first=new Date(first));last instanceof Date||(last=new Date(last));first.setHours(0),first.setMinutes(0),first.setSeconds(0),first.setMilliseconds(0),last.setHours(23),last.setMinutes(59),last.setSeconds(59),last.setMilliseconds(999);const dayspan=Math.round((last-first+1)/864e5),years=Math.floor(dayspan/365),ydaysleft=dayspan%365,weeks=Math.floor(ydaysleft/7);return{first:first,last:last,totaldays:dayspan,years:years,weeks:weeks,days:ydaysleft%7,formatted:{first:format_date(first),last:format_date(last)}}},_exports.format_date=format_date,_exports.studyplanDates=studyplanDates,_exports.studyplanPageTiming=function(page){const now=(new Date).getTime(),start=new Date(page.startdate),end=page.enddate?new Date(page.enddate):null;return start<now?end&&now>end?"past":"present":"future"},_exports.studyplanTiming=function(plan){const now=(new Date).getTime(),dates=studyplanDates(plan);return dates.start<now?dates.end&&now>dates.end?"past":"present":"future"}}));
|
||||
define("local_treestudyplan/util/date-helper",["exports"],(function(_exports){function format_date(d,short){d instanceof Date||(d=new Date(d));let monthformat="short";return!0===short?monthformat="numeric":!1===short&&(monthformat="long"),d.toLocaleDateString(document.documentElement.lang,{year:"numeric",month:monthformat,day:"numeric"})}function studyplanDates(plan){let earliestStart=null,latestEnd=null,openEnded=!1;for(const ix in plan.pages){const page=plan.pages[ix],s=new Date(page.startdate);if(page.enddate||(openEnded=!0),(!earliestStart||s<earliestStart)&&(earliestStart=s),page.enddate){const e=new Date(page.enddate);(!latestEnd||e>latestEnd)&&(latestEnd=e)}}return{start:earliestStart,end:openEnded?null:latestEnd}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.add_days=function(datestr,days){const date=new Date(datestr);return function(date){const d=new Date(date);let month=""+(d.getMonth()+1),day=""+d.getDate();const year=d.getFullYear();month.length<2&&(month="0"+month);day.length<2&&(day="0"+day);return[year,month,day].join("-")}(new Date(date.getTime()+864e5*days))},_exports.datespaninfo=function(first,last){first instanceof Date||(first=new Date(first));last instanceof Date||(last=new Date(last));first.setHours(0),first.setMinutes(0),first.setSeconds(0),first.setMilliseconds(0),last.setHours(23),last.setMinutes(59),last.setSeconds(59),last.setMilliseconds(999);const dayspan=Math.round((last-first+1)/864e5),years=Math.floor(dayspan/365),ydaysleft=dayspan%365,weeks=Math.floor(ydaysleft/7);return{first:first,last:last,totaldays:dayspan,years:years,weeks:weeks,days:ydaysleft%7,formatted:{first:format_date(first),last:format_date(last)}}},_exports.format_date=format_date,_exports.studyplanDates=studyplanDates,_exports.studyplanPageTiming=function(page){const now=(new Date).getTime(),start=new Date(page.startdate),end=page.enddate?new Date(page.enddate):null;return start<now?end&&now>end?"past":"present":"future"},_exports.studyplanTiming=function(plan){const now=(new Date).getTime(),dates=studyplanDates(plan);return dates.start<now?dates.end&&now>dates.end?"past":"present":"future"}}));
|
||||
|
||||
//# sourceMappingURL=date-helper.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -1099,6 +1099,26 @@ export default {
|
|||
else {
|
||||
return this.text.noenddate;
|
||||
}
|
||||
},
|
||||
courseprogress() {
|
||||
if (!this.value.course.enrolled) {
|
||||
return 0;
|
||||
} else if(this.value.course.completion) {
|
||||
return (this.value.course.completion.progress / this.value.course.completion.count);
|
||||
} else if(this.value.course.competency) {
|
||||
return (this.value.course.competency.progress / this.value.course.competency.count);
|
||||
} else if(this.value.course.grades) {
|
||||
return (this.gradeprogress(this.value.course.grades) / this.value.course.grades.length);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
hasprogressinfo() {
|
||||
if (!this.value.course.enrolled) {
|
||||
return false;
|
||||
} else {
|
||||
return (this.value.course.completion || this.value.course.competency || this.value.course.grades);
|
||||
}
|
||||
}
|
||||
},
|
||||
created(){
|
||||
|
@ -1138,6 +1158,16 @@ export default {
|
|||
return "check";
|
||||
}
|
||||
},
|
||||
gradeprogress(grades) {
|
||||
let progress = 0;
|
||||
for (const ix in grades) {
|
||||
const g = grades[ix];
|
||||
if (["completed","excellent","good"].includes(g.completion)) {
|
||||
progress++;
|
||||
}
|
||||
}
|
||||
return progress;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div :class="'r-item-competency completion-'+value.completion">
|
||||
|
@ -1156,24 +1186,10 @@ export default {
|
|||
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
|
||||
:title="text.student_not_tracked"></i>
|
||||
</template>
|
||||
<template v-else-if='value.course.completion'>
|
||||
<template v-else-if='hasprogressinfo'>
|
||||
<r-progress-circle v-if='["failed", "progress","incomplete"].includes(value.completion)'
|
||||
:value='value.course.completion.progress'
|
||||
:max='value.course.completion.count'
|
||||
:min='0'
|
||||
:class="'r-course-result r-completion-'+value.completion"
|
||||
:icon='circle_icon(value.completion)'
|
||||
:title="text['completion_'+value.completion]"
|
||||
></r-progress-circle>
|
||||
<i v-else v-b-popover.top
|
||||
:class="'r-course-result fa fa-'+completion_icon(value.completion)+
|
||||
' r-completion-'+value.completion"
|
||||
:title="text['completion_'+value.completion]"></i>
|
||||
</template>
|
||||
<template v-else-if='value.course.competency'>
|
||||
<r-progress-circle v-if='["failed", "progress","incomplete"].includes(value.completion)'
|
||||
:value='value.course.competency.progress'
|
||||
:max='value.course.competency.count'
|
||||
:value='courseprogress'
|
||||
:max='1'
|
||||
:min='0'
|
||||
:class="'r-course-result r-completion-'+value.completion"
|
||||
:icon='circle_icon(value.completion)'
|
||||
|
@ -1205,48 +1221,59 @@ export default {
|
|||
ok-only
|
||||
centered
|
||||
scrollable
|
||||
header-class="r-item-course-header"
|
||||
>
|
||||
<template #modal-header>
|
||||
<div>
|
||||
<h1><a :href="(!guestmode)?('/course/view.php?id='+value.course.id):undefined" target="_blank"
|
||||
><i class="fa fa-graduation-cap"></i> {{ value.course.fullname }}</a></h1>
|
||||
{{ value.course.context.path.join(" / ")}}
|
||||
</div>
|
||||
<div class="r-course-detail-header-right">
|
||||
<div class="r-completion-detail-header">
|
||||
<template v-if='!value.course.enrolled'>
|
||||
{{text.not_enrolled}}
|
||||
<i v-b-popover.top
|
||||
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
|
||||
:title="text.student_not_tracked"></i>
|
||||
</template>
|
||||
<template v-else-if='value.course.completion'>
|
||||
{{text['completion_'+value.completion]}}
|
||||
<r-progress-circle v-if='["failed","progress","incomplete"].includes(value.completion)'
|
||||
:value='value.course.completion.progress'
|
||||
:max='value.course.completion.count'
|
||||
:min='0'
|
||||
:title="text['completion_'+value.completion]"
|
||||
:class="'r-progress-circle-popup r-completion-'+value.completion"
|
||||
:icon='circle_icon(value.completion)'
|
||||
></r-progress-circle
|
||||
><i v-else v-b-popover.top
|
||||
:class="'fa fa-'+completion_icon(value.completion)+
|
||||
' r-progress-icon-popup r-completion-'+value.completion"
|
||||
:title="text['completion_'+value.completion]"></i>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{text['completion_'+value.completion]}}
|
||||
<i :class="'fa fa-'+completion_icon(value.completion)+' r-completion-'+value.completion"
|
||||
:title="text['completion_'+value.completion]"></i>
|
||||
</template>
|
||||
<template #modal-header >
|
||||
<div class="r-item-course-header-details">
|
||||
<div>
|
||||
<h1><a :href="(!guestmode)?('/course/view.php?id='+value.course.id):undefined" target="_blank"
|
||||
><i class="fa fa-graduation-cap"></i> {{ value.course.fullname }}</a></h1>
|
||||
{{ value.course.context.path.join(" / ")}}
|
||||
</div>
|
||||
<div :class="'r-timing-'+value.course.timing">
|
||||
{{text['coursetiming_'+value.course.timing]}}<br>
|
||||
{{ startdate }} - {{ enddate }}
|
||||
<div class="r-course-detail-header-right">
|
||||
<div class="r-completion-detail-header">
|
||||
<template v-if='!value.course.enrolled'>
|
||||
{{text.not_enrolled}}
|
||||
<i v-b-popover.top
|
||||
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
|
||||
:title="text.student_not_tracked"></i>
|
||||
</template>
|
||||
<template v-else-if='hasprogressinfo'>
|
||||
<r-progress-circle v-if='["failed", "progress","incomplete"].includes(value.completion)'
|
||||
:value='courseprogress'
|
||||
:max='1'
|
||||
:min='0'
|
||||
:class="'r-course-result r-completion-'+value.completion"
|
||||
:icon='circle_icon(value.completion)'
|
||||
:title="text['completion_'+value.completion]"
|
||||
></r-progress-circle>
|
||||
<i v-else v-b-popover.top
|
||||
:class="'r-course-result fa fa-'+completion_icon(value.completion)+
|
||||
' r-completion-'+value.completion"
|
||||
:title="text['completion_'+value.completion]"></i>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{text['completion_'+value.completion]}}
|
||||
<i :class="'fa fa-'+completion_icon(value.completion)+' r-completion-'+value.completion"
|
||||
:title="text['completion_'+value.completion]"></i>
|
||||
</template>
|
||||
</div>
|
||||
<div :class="'r-timing-'+value.course.timing">
|
||||
{{text['coursetiming_'+value.course.timing]}}<br>
|
||||
{{ startdate }} - {{ enddate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<s-progress-bar
|
||||
v-if='value.course.showprogressbar && hasprogressinfo'
|
||||
v-model="courseprogress"
|
||||
></s-progress-bar>
|
||||
</template>
|
||||
<s-course-extrafields
|
||||
v-if="value.course.extrafields"
|
||||
v-model="value.course.extrafields"
|
||||
position="above"
|
||||
></s-course-extrafields>
|
||||
<r-item-studentgrades
|
||||
v-if='!!value.course.grades && value.course.grades.length > 0'
|
||||
v-model='value'
|
||||
|
@ -1261,6 +1288,11 @@ export default {
|
|||
v-model='value.course.competency'
|
||||
:item='value'
|
||||
></r-item-student-course-competency>
|
||||
<s-course-extrafields
|
||||
v-if="value.course.extrafields"
|
||||
v-model="value.course.extrafields"
|
||||
position="below"
|
||||
></s-course-extrafields>
|
||||
</b-modal>
|
||||
</b-card></div>
|
||||
`,
|
||||
|
@ -1958,6 +1990,11 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<s-course-extrafields
|
||||
v-if="value.course.extrafields"
|
||||
v-model="value.course.extrafields"
|
||||
position="above"
|
||||
></s-course-extrafields>
|
||||
<r-item-teachergrades
|
||||
v-if='!!value.course.grades && value.course.grades.length > 0'
|
||||
v-model='value.course'
|
||||
|
@ -1973,6 +2010,11 @@ export default {
|
|||
v-model='value.course.competency'
|
||||
:item='value'
|
||||
></r-item-teacher-course-competency>
|
||||
<s-course-extrafields
|
||||
v-if="value.course.extrafields"
|
||||
v-model="value.course.extrafields"
|
||||
position="below"
|
||||
></s-course-extrafields>
|
||||
</b-modal>
|
||||
|
||||
</b-card>
|
||||
|
@ -2045,6 +2087,7 @@ export default {
|
|||
{{ value.course.startdate }} - {{ value.course.enddate }}
|
||||
</div>
|
||||
</div>
|
||||
<s-
|
||||
</template>
|
||||
<b-form-group
|
||||
:label="text.select_grades"
|
||||
|
|
|
@ -152,9 +152,11 @@ export class SimpleLine {
|
|||
this.mutationObserver = new MutationObserver(function(mutations_list) {
|
||||
mutations_list.forEach(function(mutation) {
|
||||
mutation.removedNodes.forEach(function(removed_node) {
|
||||
if(removed_node == this.start || removed_node == this.end) {
|
||||
console.warning("Element removed",removed_node);
|
||||
this.remove();
|
||||
if (this){
|
||||
if(removed_node == this.start || removed_node == this.end) {
|
||||
console.warning("Element removed",removed_node);
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3194,6 +3194,11 @@ export default {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<s-course-extrafields
|
||||
v-if="value.course.extrafields"
|
||||
v-model="value.course.extrafields"
|
||||
position="above"
|
||||
></s-course-extrafields>
|
||||
<t-item-course-grades
|
||||
v-if='!!value.course.grades && value.course.grades.length > 0'
|
||||
v-model='value' :plan="plan"
|
||||
|
@ -3208,7 +3213,11 @@ export default {
|
|||
v-model='value.course.competency'
|
||||
:item='value'
|
||||
></t-item-course-competency>
|
||||
|
||||
<s-course-extrafields
|
||||
v-if="value.course.extrafields"
|
||||
v-model="value.course.extrafields"
|
||||
position="below"
|
||||
></s-course-extrafields>
|
||||
<template #modal-footer="{ ok, cancel, hide }" >
|
||||
<a href='#' @click='$emit("deleterq")' class="text-danger"
|
||||
><i class="fa fa-trash"></i>
|
||||
|
|
|
@ -21,6 +21,9 @@ export default {
|
|||
},
|
||||
details: {
|
||||
details: "studyplan_details",
|
||||
},
|
||||
extrafields: {
|
||||
show: "show@core"
|
||||
}
|
||||
});
|
||||
// Create new eventbus for interaction between item components
|
||||
|
@ -51,27 +54,7 @@ export default {
|
|||
end: (dates.end)?format_date(dates.end):this.text.noenddate,
|
||||
};
|
||||
},
|
||||
width_completed() {
|
||||
if(this.value.progress) {
|
||||
return this.value.progress * 100;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
width_incomplete() {
|
||||
if(this.value.progress) {
|
||||
return (1-this.value.progress) * 100;
|
||||
} else {
|
||||
return 100;
|
||||
}
|
||||
},
|
||||
percentage_complete() {
|
||||
if(this.value.progress) {
|
||||
return Math.round(this.value.progress * 100) + "%";
|
||||
} else {
|
||||
return "0%";
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
onOpenClick(e) {
|
||||
|
@ -97,20 +80,10 @@ export default {
|
|||
<div class='s-studyplan-card-idnumber' v-if='value.idnumber'>
|
||||
{{ text.idnumber }}: {{ value.idnumber }}
|
||||
</div>
|
||||
<div class='s-studyplan-card-progress' v-if='value.progress !== undefined && value.progress !== null'>
|
||||
<div class="s-studyplan-card-progressbar"
|
||||
><span v-if="width_completed > 0"
|
||||
:style="{width: width_completed+'%'}"
|
||||
class='s-studyplan-card-progress-segment s-studyplan-card-progress-completed'
|
||||
></span
|
||||
><span :style="{width: width_incomplete+'%'}"
|
||||
class='s-studyplan-card-progress-segment s-studyplan-card-progress-incomplete'
|
||||
></span
|
||||
></div>
|
||||
<div class="s-studyplan-card-progresstext">
|
||||
{{ percentage_complete}} {{ text.completed.toLowerCase() }}
|
||||
</div>
|
||||
</div>
|
||||
<s-progress-bar
|
||||
v-if='value.progress !== undefined && value.progress !== null'
|
||||
v-model="value.progress"
|
||||
></s-progress-bar>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -131,6 +104,71 @@ export default {
|
|||
`,
|
||||
});
|
||||
|
||||
Vue.component('s-progress-bar', {
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default() { return 0;}
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default() { return 1;}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: strings.studyplancard
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
width_completed() {
|
||||
if(this.value) {
|
||||
const v = ( (this.value - this.min) / (this.max - this.min));
|
||||
return v * 100;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
width_incomplete() {
|
||||
if(this.value) {
|
||||
const v = ( (this.value - this.min) / (this.max - this.min));
|
||||
return (1-v) * 100;
|
||||
} else {
|
||||
return 100;
|
||||
}
|
||||
},
|
||||
percentage_complete() {
|
||||
if(this.value) {
|
||||
const v = ( (this.value - this.min) / (this.max - this.min));
|
||||
return Math.round(v * 100) + "%";
|
||||
} else {
|
||||
return "0%";
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class='s-studyplan-card-progress' >
|
||||
<div class="s-studyplan-card-progressbar"
|
||||
><span v-if="width_completed > 0"
|
||||
:style="{width: width_completed+'%'}"
|
||||
class='s-studyplan-card-progress-segment s-studyplan-card-progress-completed'
|
||||
></span
|
||||
><span :style="{width: width_incomplete+'%'}"
|
||||
class='s-studyplan-card-progress-segment s-studyplan-card-progress-incomplete'
|
||||
></span
|
||||
></div>
|
||||
<div class="s-studyplan-card-progresstext">
|
||||
{{ percentage_complete}} {{ text.completed.toLowerCase() }}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* S-STUDYLINE-HEADER-HEADING
|
||||
* The only reasing this is not a simple empty div, is the fact that the header height
|
||||
|
@ -293,5 +331,73 @@ export default {
|
|||
`,
|
||||
});
|
||||
|
||||
Vue.component('s-course-extrafields', {
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
default() { return "info"; }
|
||||
},
|
||||
position: {
|
||||
type: String,
|
||||
default() { return "below"; }
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default() { return "";}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: strings.extrafields,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
const fields = [];
|
||||
for (const ix in this.value) {
|
||||
const field = this.value[ix];
|
||||
if (field.position == this.position && field.value && field.value.length > 0) {
|
||||
fields.push(field);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
displaydate(field) {
|
||||
return format_date(field.value,false);
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div :class="'s-course-extrafields ' + ((fields.length>0)?position:'empty')">
|
||||
<table v-if="fields.length > 0">
|
||||
<tr v-for="field in fields">
|
||||
<td><span class='title' v-if='field.title'>{{ field.title}}</span></td>
|
||||
<td>
|
||||
<span v-if='field.type == "date"' class="value date">{{ displaydate(field) }}</span>
|
||||
<span v-else-if='field.type == "checkbox"'
|
||||
:class="'value ' + (field.checked?'true':'false')">{{ field.value }}</span>
|
||||
<span v-else-if='field.type == "textarea"'>
|
||||
<a class='text-info' href='#' v-b-modal="field.courseid+'_'+field.fieldname">{{text.show}}...</a>
|
||||
<b-modal
|
||||
:id="field.courseid+'_'+field.fieldname"
|
||||
:title="field.title""
|
||||
scrollable
|
||||
centered
|
||||
ok-only
|
||||
><span v-html="field.value"></span
|
||||
></b-modal>
|
||||
</span>
|
||||
<span v-else class="value"><span v-html="field.value"></span></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
}
|
||||
};
|
|
@ -10,8 +10,10 @@ export function format_date(d,short){
|
|||
}
|
||||
|
||||
let monthformat = "short";
|
||||
if(short){
|
||||
if(short === true){
|
||||
monthformat = "numeric";
|
||||
} else if (short === false) {
|
||||
monthformat = "long";
|
||||
}
|
||||
return d.toLocaleDateString(document.documentElement.lang,{
|
||||
year: 'numeric', month: monthformat, day: 'numeric'
|
||||
|
|
|
@ -768,9 +768,6 @@ class badgeinfo {
|
|||
INNER JOIN {badge_criteria_param} p on p.critid = crit.id
|
||||
WHERE p.value = :courseid AND crit.criteriatype $ctypesql $conditions";
|
||||
|
||||
debug::write("Sql query courses: ");
|
||||
debug::write($sql);
|
||||
debug::print_r($sqlparams);
|
||||
|
||||
$courserelids = $DB->get_fieldset_sql($sql, $sqlparams);
|
||||
|
||||
|
@ -782,9 +779,6 @@ class badgeinfo {
|
|||
INNER JOIN {competency_coursecomp} cc on cc.competencyid = p.value
|
||||
WHERE cc.courseid = :courseid AND crit.criteriatype $ctypesql $conditions";
|
||||
|
||||
debug::write("Sql query through competencies: ");
|
||||
debug::write($sql);
|
||||
debug::print_r($sqlparams);
|
||||
|
||||
$competencyrelids = $DB->get_fieldset_sql($sql,$sqlparams);
|
||||
|
||||
|
|
|
@ -219,6 +219,11 @@ class courseinfo {
|
|||
if (strlen($idnumber) > 0) {
|
||||
return $this->course->idnumber;
|
||||
}
|
||||
} else if ($displayfield == "fullname") {
|
||||
$fullname = trim(preg_replace("/\s+/u", " ", $this->course->fullname));
|
||||
if (strlen($fullname) > 0) {
|
||||
return $fullname;
|
||||
}
|
||||
} else if (strpos( $displayfield , "customfield_") === 0) {
|
||||
$fieldname = substr($displayfield, strlen("customfield_"));
|
||||
|
||||
|
@ -292,7 +297,7 @@ class courseinfo {
|
|||
"canselectgradables" => new \external_value(PARAM_BOOL, 'Requesting user can change selected gradables'),
|
||||
"numenrolled" => new \external_value(PARAM_INT, 'number of students from this studyplan enrolled in the course'),
|
||||
"tag" => new \external_value(PARAM_TEXT, 'Tag'),
|
||||
"extrafields" => self::extrafields_structure();
|
||||
"extrafields" => self::extrafields_structure(),
|
||||
], 'referenced course information', $value);
|
||||
}
|
||||
|
||||
|
@ -319,16 +324,15 @@ class courseinfo {
|
|||
'context' => $contextinfo->model(),
|
||||
'ctxid' => $this->coursecontext->id,
|
||||
'timing' => $timing,
|
||||
'startdate' => date("Y-m-d", $this->course->startdate, ),
|
||||
'startdate' => date("Y-m-d", $this->course->startdate),
|
||||
'enddate' => date("Y-m-d", $this->course->enddate),
|
||||
'amteacher' => $this->am_teacher(),
|
||||
'canupdatecourse' => \has_capability("moodle/course:update", $this->coursecontext),
|
||||
'canselectgradables' => $this->i_can_select_gradables(),
|
||||
'tag' => "Editormodel",
|
||||
'extrafields' => $this->extrafields_model(),
|
||||
'extrafields' => $this->extrafields_model(true),
|
||||
'grades' => [],
|
||||
'numenrolled' => $numenrolled,
|
||||
|
||||
];
|
||||
|
||||
if (isset($this->studyitem)) {
|
||||
|
@ -373,7 +377,8 @@ class courseinfo {
|
|||
"startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'Course end date'),
|
||||
"enrolled" => new \external_value(PARAM_BOOL, 'True if student is enrolled as student in this course'),
|
||||
"extrafields" => self::extrafields_structure();
|
||||
"extrafields" => self::extrafields_structure(),
|
||||
"showprogressbar" => new \external_value(PARAM_BOOL, "Whether to show the progress bar in the header"),
|
||||
], 'course information', $value);
|
||||
}
|
||||
|
||||
|
@ -450,6 +455,7 @@ class courseinfo {
|
|||
'grades' => [],
|
||||
'enrolled' => $this->is_enrolled_student($userid),
|
||||
'extrafields' => $this->extrafields_model(),
|
||||
'showprogressbar' => get_config("local_treestudyplan","courseprogressbar"),
|
||||
];
|
||||
|
||||
|
||||
|
@ -485,27 +491,37 @@ class courseinfo {
|
|||
"value" => new \external_value(PARAM_RAW, 'value'),
|
||||
"position" => new \external_value(PARAM_TEXT, 'position'),
|
||||
"type" => new \external_value(PARAM_TEXT, 'value type'),
|
||||
"fieldname" => new \external_value(PARAM_TEXT, 'field name'),
|
||||
"checked" => new \external_value(PARAM_BOOL, 'checkbox value',VALUE_OPTIONAL),
|
||||
"courseid" => new \external_value(PARAM_TEXT, 'course id number'),
|
||||
], 'referenced course information'), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Webservice model for basic info
|
||||
* @param $includeteachervisible Include custom fiel
|
||||
* @return array Webservice data model
|
||||
*/
|
||||
public function extrafields_model() {
|
||||
public function extrafields_model($includeteachervisible=false) {
|
||||
$list = [];
|
||||
for ($i=1; $i <= 5; $i++) {
|
||||
$field = get_config('local_treestudyplan','courseinfo'.$i.'_field');
|
||||
$title = self::extrafields_localize_title(get_config('local_treestudyplan','courseinfo'.$i.'_title'));
|
||||
$pos = get_config('local_treestudyplan','courseinfo'.$i.'_position');
|
||||
[$value,$type] = $this->extrafields_value($field);
|
||||
$list[] = [
|
||||
"title" => $title,
|
||||
"value" => $value,
|
||||
"position" => $pos,
|
||||
"type" => $type,
|
||||
];
|
||||
|
||||
if ($field) {
|
||||
$title = self::extrafields_localize_title(get_config('local_treestudyplan','courseinfo'.$i.'_title'));
|
||||
$pos = get_config('local_treestudyplan','courseinfo'.$i.'_position');
|
||||
[$value,$type, $raw] = $this->extrafields_value($field,$includeteachervisible);
|
||||
if ($type) {
|
||||
$list[] = [
|
||||
"title" => $title,
|
||||
"value" => $value,
|
||||
"position" => $pos,
|
||||
"type" => $type,
|
||||
"fieldname" => $field,
|
||||
"courseid" => $this->course->id,
|
||||
"checked" => ($type=="checkbox")?($raw?true:false):null,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
@ -514,33 +530,51 @@ class courseinfo {
|
|||
$lang = trim(current_language());
|
||||
$lines = explode("\n",$field);
|
||||
$title = "";
|
||||
$fallback = ""; // Fallback to first title
|
||||
foreach ($lines as $l) {
|
||||
$parts = explode("|",$l,2);
|
||||
if (count($parts) > 0) {
|
||||
if (count($parts) == 1 && empty($title)) {
|
||||
$title = trim($parts[0]);
|
||||
// Set the first line as fallback.
|
||||
if (empty($firsttitle) && !empty($parts[0])) {
|
||||
$fallback = $parts[0];
|
||||
}
|
||||
else if (trim($parts[1]) == $lang) {
|
||||
if (count($parts) == 1 && empty($title)) {
|
||||
// Set line without language as default if no localized line found
|
||||
$title = trim($parts[0]);
|
||||
} else if (trim($parts[1]) == $lang) {
|
||||
return trim($parts[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $title;
|
||||
// Return default title or fall back to first localizef title.
|
||||
return (strlen($title) > 0)?$title:$fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine value and type of an extra field for this course
|
||||
* @return array [value,type] of the field for this
|
||||
*/
|
||||
protected function extrafields_value($fieldname) {
|
||||
$fieldname = get_config("local_treestudyplan", "display_field");
|
||||
protected function extrafields_value($fieldname,$includeteachervisible=false) {
|
||||
|
||||
if ($fieldname == "description") {
|
||||
return [$this->course()->description, "textarea"];
|
||||
// Process embedded files.
|
||||
$value = \file_rewrite_pluginfile_urls(
|
||||
// The description content
|
||||
$this->course()->summary,
|
||||
// The pluginfile URL which will serve the request.
|
||||
'pluginfile.php',
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that file are stored in.
|
||||
$this->coursecontext->id, // System instance is always used for this
|
||||
'course',
|
||||
'summary',
|
||||
''
|
||||
);
|
||||
return [$value, "textarea", $this->course()->summary];
|
||||
} else if ($fieldname == "idnumber") {
|
||||
$idnumber = trim(preg_replace("/\s+/u", " ", $this->course->idnumber));
|
||||
return [$idnumber, "text"];
|
||||
return [$idnumber, "text", $this->course->idnumber];
|
||||
} else if ($fieldname == "contacts") {
|
||||
$cle = new \core_course_list_element($this->course());
|
||||
$contacts = $cle->get_course_contacts();
|
||||
|
@ -552,24 +586,41 @@ class courseinfo {
|
|||
$value .= $contact["username"] . "(".$contact["role"]["name"].")";
|
||||
}
|
||||
|
||||
return [$value, "text"];
|
||||
return [$value, "text", $value];
|
||||
} else if (strpos( $fieldname , "customfield_") === 0) {
|
||||
$fieldname = substr($fieldname, strlen("customfield_"));
|
||||
$fieldshortname = substr($fieldname, strlen("customfield_"));
|
||||
|
||||
$handler = \core_customfield\handler::get_handler('core_course', 'course');
|
||||
$datas = $handler->get_instance_data($this->course->id);
|
||||
foreach ($datas as $data) {
|
||||
$field = $data->get_field();
|
||||
if ($field->get('shortname') == $fieldname) {
|
||||
$value = trim(preg_replace("/\s+/u", " ", $data->get_value()));
|
||||
$fshortname = $field->get('shortname');
|
||||
if ($fshortname == $fieldshortname) {
|
||||
$visibility = $field->get_configdata_property("visibility");
|
||||
$raw = $data->get_value();
|
||||
$type = $field->get('type');
|
||||
if (strlen($value) > 0) {
|
||||
return [$value,$type];
|
||||
|
||||
// Only show if visibility is "Everyone" or ("Teachers" and in teacher view )
|
||||
if ($visibility > 0 && ($visibility == 2 || $includeteachervisible)) {
|
||||
if ($type == "date") {
|
||||
// Date should be converted to YYYY-MM-DD so the javascript can properly format it.
|
||||
if ($raw == 0) {
|
||||
$value = "";
|
||||
} else {
|
||||
// Convert to YYYY-MM-DD format.
|
||||
$value = date("Y-m-d", $raw);
|
||||
}
|
||||
} else {
|
||||
// Everything else can just use the export value.
|
||||
$value = $data->export_value();
|
||||
}
|
||||
|
||||
return [$value,$type,$raw];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback to shortname when the specified display field fails, since shortname is never empty.
|
||||
return ["",""];
|
||||
// Fallback to empty if finding a match fails.
|
||||
return [null,null,null];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -407,7 +407,6 @@ class studentstudyplanservice extends \external_api {
|
|||
|
||||
if ($studyplan->exist_for_user($userid)) {
|
||||
$model = $studyplan->user_model($userid);
|
||||
debug::dump($model);
|
||||
return $model;
|
||||
} else {
|
||||
throw new \moodle_exception("You do not have access to this studyplan");
|
||||
|
|
|
@ -1336,6 +1336,25 @@
|
|||
.features-treestudyplan table.r-item-course-competency-list td {
|
||||
padding-right: 1em;
|
||||
}
|
||||
.path-local-treestudyplan header.modal-header.r-item-course-header,
|
||||
.features-treestudyplan header.modal-header.r-item-course-header {
|
||||
display: block;
|
||||
padding-bottom: 0.2rem;
|
||||
}
|
||||
.path-local-treestudyplan header.modal-header.r-item-course-header .s-studyplan-card-progressbar,
|
||||
.features-treestudyplan header.modal-header.r-item-course-header .s-studyplan-card-progressbar {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.path-local-treestudyplan div.r-item-course-header-details,
|
||||
.features-treestudyplan div.r-item-course-header-details {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.path-local-treestudyplan div.r-item-course-header-details:last-child,
|
||||
.features-treestudyplan div.r-item-course-header-details:last-child {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.path-local-treestudyplan .card.s-studyplan-card,
|
||||
.features-treestudyplan .card.s-studyplan-card {
|
||||
|
@ -1467,6 +1486,31 @@
|
|||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields.above,
|
||||
.features-treestudyplan .s-course-extrafields.above {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields.below,
|
||||
.features-treestudyplan .s-course-extrafields.below {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields .title,
|
||||
.features-treestudyplan .s-course-extrafields .title {
|
||||
font-weight: bold;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields .value.true,
|
||||
.features-treestudyplan .s-course-extrafields .value.true {
|
||||
color: var(--success);
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields .value.false,
|
||||
.features-treestudyplan .s-course-extrafields .value.false {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.path-local-treestudyplan .b-modal-justify-footer-between .modal-footer,
|
||||
.features-treestudyplan .b-modal-justify-footer-between .modal-footer {
|
||||
|
|
|
@ -1150,4 +1150,21 @@
|
|||
|
||||
}
|
||||
|
||||
header.modal-header.r-item-course-header {
|
||||
display: block;
|
||||
.s-studyplan-card-progressbar {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
padding-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
div.r-item-course-header-details {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
&:last-child {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -129,4 +129,35 @@
|
|||
|
||||
}
|
||||
|
||||
.s-course-extrafields {
|
||||
&.above {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
&.below {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.value {
|
||||
&.true {
|
||||
color: var(--success);
|
||||
}
|
||||
&.false {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
16
settings.php
16
settings.php
|
@ -88,17 +88,21 @@ if ($hassiteconfig) {
|
|||
get_string('settingdesc_display_heading', 'local_treestudyplan')
|
||||
));
|
||||
|
||||
$displayfields = ["shortname" => get_string("shortname"), "idnumber" => get_string("idnumber")];
|
||||
$displayfields = ["shortname" => get_string("shortname"), "idnumber" => get_string("idnumber"), "fullname" => get_string("fullname"), ];
|
||||
$infofields = ["" => get_string('none'), "description" => get_string("description"), "contacts" => get_string("teachers"), "idnumber" => get_string("idnumber")];
|
||||
$handler = \core_customfield\handler::get_handler('core_course', 'course');
|
||||
|
||||
foreach ($handler->get_categories_with_fields() as $cat) {
|
||||
$catname = $cat->get_formatted_name();
|
||||
foreach ($cat->get_fields() as $field) {
|
||||
$fieldname = $field->get_formatted_name();
|
||||
$fieldid = $field->get("shortname");
|
||||
$displayfields["customfield_".$fieldid] = $catname.": ".$fieldname;
|
||||
$infofields["customfield_".$fieldid] = $catname.": ".$fieldname;
|
||||
$visibility = $field->get_configdata_property("visibility");
|
||||
if ($visibility > 0) {
|
||||
// Only include fields that are visible to Teachers, or Everyone.
|
||||
$fieldname = $field->get_formatted_name();
|
||||
$fieldid = $field->get("shortname");
|
||||
$displayfields["customfield_".$fieldid] = $catname.": ".$fieldname;
|
||||
$infofields["customfield_".$fieldid] = $catname.": ".$fieldname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,8 +126,6 @@ if ($hassiteconfig) {
|
|||
|
||||
$positions = [ "above" => get_string('infofield_position_above', 'local_treestudyplan'),
|
||||
"below" => get_string("infofield_position_below", 'local_treestudyplan'),
|
||||
"header" => get_string("infofield_position_header", 'local_treestudyplan'),
|
||||
"footer" => get_string("infofield_position_footer", 'local_treestudyplan')
|
||||
];
|
||||
|
||||
for ($i=1;$i<=5;$i++) {
|
||||
|
|
44
styles.css
44
styles.css
|
@ -1336,6 +1336,25 @@
|
|||
.features-treestudyplan table.r-item-course-competency-list td {
|
||||
padding-right: 1em;
|
||||
}
|
||||
.path-local-treestudyplan header.modal-header.r-item-course-header,
|
||||
.features-treestudyplan header.modal-header.r-item-course-header {
|
||||
display: block;
|
||||
padding-bottom: 0.2rem;
|
||||
}
|
||||
.path-local-treestudyplan header.modal-header.r-item-course-header .s-studyplan-card-progressbar,
|
||||
.features-treestudyplan header.modal-header.r-item-course-header .s-studyplan-card-progressbar {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.path-local-treestudyplan div.r-item-course-header-details,
|
||||
.features-treestudyplan div.r-item-course-header-details {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.path-local-treestudyplan div.r-item-course-header-details:last-child,
|
||||
.features-treestudyplan div.r-item-course-header-details:last-child {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.path-local-treestudyplan .card.s-studyplan-card,
|
||||
.features-treestudyplan .card.s-studyplan-card {
|
||||
|
@ -1467,6 +1486,31 @@
|
|||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields.above,
|
||||
.features-treestudyplan .s-course-extrafields.above {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields.below,
|
||||
.features-treestudyplan .s-course-extrafields.below {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields .title,
|
||||
.features-treestudyplan .s-course-extrafields .title {
|
||||
font-weight: bold;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields .value.true,
|
||||
.features-treestudyplan .s-course-extrafields .value.true {
|
||||
color: var(--success);
|
||||
}
|
||||
.path-local-treestudyplan .s-course-extrafields .value.false,
|
||||
.features-treestudyplan .s-course-extrafields .value.false {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.path-local-treestudyplan .b-modal-justify-footer-between .modal-footer,
|
||||
.features-treestudyplan .b-modal-justify-footer-between .modal-footer {
|
||||
|
|
Loading…
Reference in New Issue
Block a user