Added icons, finished description editor
This commit is contained in:
parent
0dda0c6a45
commit
c6882b916a
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
3
amd/build/util/formfields.min.js
vendored
Normal file
3
amd/build/util/formfields.min.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
define("local_treestudyplan/util/formfields",["exports","./debugger"],(function(_exports,_debugger){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.text_integer=function(id){var element=document.getElementById(id);debug.warn("Initializing form element text_integer on ",id,element)};var debug=new(_debugger=(obj=_debugger)&&obj.__esModule?obj:{default:obj}).default("formfields")}));
|
||||
|
||||
//# sourceMappingURL=formfields.min.js.map
|
1
amd/build/util/formfields.min.js.map
Normal file
1
amd/build/util/formfields.min.js.map
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"formfields.min.js","sources":["../../src/util/formfields.js"],"sourcesContent":["import Debugger from './debugger';\nconst debug = new Debugger(\"formfields\");\n/**\n * convert a text field into an integer only text field\n * @param {Date|string} id The Id of the form field\n */\nexport function text_integer(id){\n const element = document.getElementById(id);\n debug.warn(\"Initializing form element text_integer on \",id,element);\n}"],"names":["id","element","document","getElementById","debug","warn"],"mappings":"mMAM6BA,QACnBC,QAAUC,SAASC,eAAeH,IACxCI,MAAMC,KAAK,6CAA6CL,GAAGC,cAPzDG,MAAQ,yEAAa"}
|
3
amd/build/util/int-textfield.min.js
vendored
Normal file
3
amd/build/util/int-textfield.min.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
|
||||
//# sourceMappingURL=int-textfield.min.js.map
|
1
amd/build/util/int-textfield.min.js.map
Normal file
1
amd/build/util/int-textfield.min.js.map
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"int-textfield.min.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
2
amd/build/util/mform-helper.min.js
vendored
2
amd/build/util/mform-helper.min.js
vendored
|
@ -1,3 +1,3 @@
|
|||
define("local_treestudyplan/util/mform-helper",["exports","core/ajax","core/fragment","core/templates","core/notification","./string-helper","./debugger"],(function(_exports,_ajax,_fragment,_templates,_notification,_stringHelper,_debugger){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger);var _default={install:function(Vue){var debug=new _debugger.default("treestudyplan-mform-helper"),strings=(0,_stringHelper.load_strings)({editmod:{save$core:"save$core",cancel$core:"cancel$core"}});Vue.component("mform",{props:{name:{type:String},params:{type:Object},title:{type:String,default:""}},data:function(){return{content:"",loading:!0,uuid:void 0!==crypto.randomUUID?crypto.randomUUID():"10000000-1000-4000-8000-100000000000".replace(/[018]/g,(function(c){return(c^crypto.getRandomValues(new Uint8Array(1))[0]&15>>c/4).toString(16)})),text:strings}},computed:{},methods:{openForm:function(){this.$refs.editormodal.show()},onShown:function(){var self=this;debug.info('Loading form "'.concat(self.name,'" with params'),self.params),self.loading=!1,(0,_ajax.call)([{methodname:"local_treestudyplan_get_mform",args:{formname:self.name,params:JSON.stringify(self.params)}}])[0].then((function(data){var html=data.html;self.loading=!1;var js=(0,_fragment.processCollectedJavascript)(data.javascript);debug.info("Replacing content / el",self.$refs.content),debug.info("Replacing content / html",html),debug.info("Replacing content / js",js);var r=(0,_templates.replaceNodeContents)(self.$refs.content,html,js);debug.info("R:",r)})).catch(_notification.default.exception)},onSave:function(){var self=this,form=this.$refs.content.getElementsByTagName("form")[0];form.dispatchEvent(new Event("save-form-state"));var formdata=new FormData(form),data=new URLSearchParams(formdata).toString();(0,_ajax.call)([{methodname:"local_treestudyplan_submit_mform",args:{formname:self.name,params:JSON.stringify(self.params),formdata:data}}])[0].then((function(){self.$emit("saved",formdata)})).catch(_notification.default.exception)}},template:'\n <span class=\'mform-container\'><a href=\'#\' @click.prevent="openForm"><slot><i class="fa fa-cog"></i></slot></a>\n <b-modal\n ref="editormodal"\n scrollable\n centered\n size="xl"\n id="\'modal-\'+uuid"\n @shown="onShown"\n @ok="onSave"\n :title="title"\n :ok-title="text.save$core"\n ><div :class="\'s-mform-content\'" ref="content"\n ><div class="d-flex justify-content-center mb-3"\n ><b-spinner variant="primary"></b-spinner\n ></div\n ></div\n ></b-modal>\n </span>\n '})}};return _exports.default=_default,_exports.default}));
|
||||
define("local_treestudyplan/util/mform-helper",["exports","core/ajax","core/fragment","core/templates","core/notification","./string-helper","./debugger"],(function(_exports,_ajax,_fragment,_templates,_notification,_stringHelper,_debugger){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger);var _default={install:function(Vue){var debug=new _debugger.default("treestudyplan-mform-helper"),strings=(0,_stringHelper.load_strings)({editmod:{save$core:"save$core",cancel$core:"cancel$core"}});Vue.component("mform",{props:{name:{type:String},params:{type:Object},title:{type:String,default:""},variant:{type:String,default:"primary"},type:{type:String,default:"link"}},data:function(){return{content:"",loading:!0,uuid:void 0!==crypto.randomUUID?crypto.randomUUID():"10000000-1000-4000-8000-100000000000".replace(/[018]/g,(function(c){return(c^crypto.getRandomValues(new Uint8Array(1))[0]&15>>c/4).toString(16)})),text:strings}},computed:{},methods:{openForm:function(){this.$refs.editormodal.show()},onShown:function(){var self=this;debug.info('Loading form "'.concat(self.name,'" with params'),self.params),self.loading=!1,(0,_ajax.call)([{methodname:"local_treestudyplan_get_mform",args:{formname:self.name,params:JSON.stringify(self.params)}}])[0].then((function(data){var html=data.html;self.loading=!1;var js=(0,_fragment.processCollectedJavascript)(data.javascript);(0,_templates.replaceNodeContents)(self.$refs.content,html,js)})).catch(_notification.default.exception)},onSave:function(){var self=this,form=this.$refs.content.getElementsByTagName("form")[0];form.dispatchEvent(new Event("save-form-state"));var formdata=new FormData(form),data=new URLSearchParams(formdata).toString();(0,_ajax.call)([{methodname:"local_treestudyplan_submit_mform",args:{formname:self.name,params:JSON.stringify(self.params),formdata:data}}])[0].then((function(response){var updatedplan=JSON.parse(response.data);self.$emit("saved",updatedplan,formdata)})).catch(_notification.default.exception)}},template:'\n <span class=\'mform-container\'>\n <b-button :variant="variant" v-if=\'type == "button"\' @click.prevent=\'openForm\'\n ><slot><i class=\'fa fa-gear\'></i></slot></b-button>\n <a variant="variant" v-else href=\'#\' @click.prevent=\'openForm\'\n ><slot><i class=\'fa fa-gear\'></i></slot></a>\n <b-modal\n ref="editormodal"\n scrollable\n centered\n size="xl"\n id="\'modal-\'+uuid"\n @shown="onShown"\n @ok="onSave"\n :title="title"\n :ok-title="text.save$core"\n ><div :class="\'s-mform-content\'" ref="content"\n ><div class="d-flex justify-content-center mb-3"\n ><b-spinner variant="primary"></b-spinner\n ></div\n ></div\n ></b-modal>\n </span>\n '})}};return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=mform-helper.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -135,31 +135,7 @@ export default {
|
|||
},
|
||||
studyplan_edit: {
|
||||
studyplan_edit: 'studyplan_edit',
|
||||
studyplan_name: 'studyplan_name',
|
||||
studyplan_name_ph: 'studyplan_name_ph',
|
||||
studyplan_shortname: 'studyplan_shortname',
|
||||
studyplan_shortname_ph: 'studyplan_shortname_ph',
|
||||
studyplan_description: 'studyplan_description',
|
||||
studyplan_description_ph: 'studyplan_description_ph',
|
||||
studyplan_idnumber: 'studyplan_idnumber',
|
||||
studyplan_idnumber_ph: 'studyplan_idnumber_ph',
|
||||
studyplan_context: 'studyplan_context',
|
||||
studyplan_slots: 'studyplan_slots',
|
||||
studyplan_startdate: 'studyplan_startdate',
|
||||
studyplan_enddate: 'studyplan_enddate',
|
||||
choose_aggregation_style: 'choose_aggregation_style',
|
||||
setting_bistate_thresh_excellent: 'setting_bistate_thresh_excellent',
|
||||
settingdesc_bistate_thresh_excellent: 'settingdesc_bistate_thresh_excellent',
|
||||
setting_bistate_thresh_good: 'setting_bistate_thresh_good',
|
||||
settingdesc_bistate_thresh_good: 'settingdesc_bistate_thresh_good',
|
||||
setting_bistate_thresh_completed: 'setting_bistate_thresh_completed',
|
||||
settingdesc_bistate_thresh_completed: 'settingdesc_bistate_thresh_completed',
|
||||
setting_bistate_support_failed: 'setting_bistate_support_failed',
|
||||
settingdesc_bistate_support_failed: 'settingdesc_bistate_support_failed',
|
||||
setting_bistate_thresh_progress: 'setting_bistate_thresh_progress',
|
||||
settingdesc_bistate_thresh_progress: 'settingdesc_bistate_thresh_progress',
|
||||
setting_bistate_accept_pending_submitted: 'setting_bistate_accept_pending_submitted',
|
||||
settingdesc_bistate_accept_pending_submitted: 'settingdesc_bistate_accept_pending_submitted',
|
||||
studyplan_add: 'studyplan_add',
|
||||
},
|
||||
period_edit: {
|
||||
edit: 'period_edit',
|
||||
|
@ -491,14 +467,15 @@ export default {
|
|||
variant="primary"
|
||||
@click="export_plan('csv')"
|
||||
>{{ text.advanced_export_csv}}</b-button>
|
||||
</b-tab>
|
||||
</b-tab>
|
||||
<b-tab :title='text.advanced_purge'>
|
||||
<p>{{text.advanced_purge_expl}}</p>
|
||||
<p><b-button
|
||||
variant="danger"
|
||||
@click="purge_studyline"
|
||||
>{{ text.advanced_purge}}</b-button></p>
|
||||
</b-tab> </b-tabs>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</b-card>
|
||||
</b-modal>
|
||||
</span>
|
||||
|
@ -531,299 +508,64 @@ export default {
|
|||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
'defaultAggregation': {
|
||||
type: String,
|
||||
default: "core",
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
config: {
|
||||
userfields: [
|
||||
{ key: "selected",},
|
||||
{ key: "firstname", "sortable": true,},
|
||||
{ key: "lastname", "sortable": true,},
|
||||
],
|
||||
cohortfields:[
|
||||
{ key: "selected",},
|
||||
{ key: "name", "sortable": true,},
|
||||
{ key: "context", "sortable": true,},
|
||||
]
|
||||
},
|
||||
editdata: {
|
||||
name: '',
|
||||
shortname: '',
|
||||
description: '',
|
||||
idnumber: '',
|
||||
context_id: this.contextid,
|
||||
periods : 4,
|
||||
startdate: (new Date()).getFullYear() + '-08-01',
|
||||
enddate: ((new Date()).getFullYear()+1) + '-08-01',
|
||||
aggregation: this.defaultAggregation,
|
||||
aggregation_config: '',
|
||||
},
|
||||
aggregation_parsed: {
|
||||
|
||||
},
|
||||
aggregators: [],
|
||||
categories: [ { context_id: 1, category: { path: "System"}}], // overwritten during load...
|
||||
text: strings.studyplan_edit,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// retrieve aggregator info
|
||||
const self = this;
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_list_aggregators',
|
||||
args: [],
|
||||
}])[0].done(function(response){
|
||||
self.aggregators = response;
|
||||
for(const ix in self.aggregators){
|
||||
const ag = self.aggregators[ix];
|
||||
|
||||
try{
|
||||
if(ag.defaultconfig && ag.defaultconfig.length > 0){
|
||||
self.aggregation_parsed[ag.id] = JSON.parse(ag.defaultconfig);
|
||||
}
|
||||
}
|
||||
catch(e){
|
||||
debug.warn(e);
|
||||
}
|
||||
}
|
||||
}).fail(notification.exception);
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_list_accessible_categories',
|
||||
args: {operation: "edit",}
|
||||
}])[0].done(function(response){
|
||||
for(const ix in response){
|
||||
const cat = response[ix];
|
||||
cat.category.pathname = cat.category.path.join(" / ");
|
||||
}
|
||||
self.categories = response;
|
||||
}).fail(notification.exception);
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
updated() {
|
||||
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
editPlanStart(){
|
||||
if(this.mode != 'create'){
|
||||
objCopy(this.editdata,this.value.pages[0],STUDYPLAN_EDITOR_PAGE_FIELDS);
|
||||
objCopy(this.editdata,this.value,STUDYPLAN_EDITOR_FIELDS);
|
||||
|
||||
}
|
||||
// decode the aggregation config data that is stored
|
||||
if(this.editdata.aggregation_config && this.editdata.aggregation_config.length > 0){
|
||||
try{
|
||||
this.aggregation_parsed[this.editdata.aggregation] = JSON.parse(this.editdata.aggregation_config);
|
||||
}
|
||||
catch(e){
|
||||
debug.warn(e);
|
||||
}
|
||||
}
|
||||
this.show = true;
|
||||
},
|
||||
editPlanFinish(){
|
||||
planSaved(updatedplan){
|
||||
const self = this;
|
||||
let args = { };
|
||||
let method = 'local_treestudyplan_edit_studyplan';
|
||||
if(this.mode == 'create'){
|
||||
method = 'local_treestudyplan_add_studyplan';
|
||||
} else {
|
||||
args['id'] = this.value.id;
|
||||
debug.info("Got new plan data",updatedplan);
|
||||
|
||||
if(self.mode == 'create'){
|
||||
// Inform parent of the details of the newly created plan
|
||||
self.$emit("created",updatedplan);
|
||||
}
|
||||
else {
|
||||
// determine if the plan moved context...
|
||||
const moved_from = self.value.context_id;
|
||||
const moved_to = updatedplan.context_id;
|
||||
const moved = (moved_from != moved_to);
|
||||
|
||||
// store the configuration for this aggregation type if it is relevant
|
||||
if(this.aggregation_parsed[this.editdata.aggregation]){
|
||||
this.editdata.aggregation_config = JSON.stringify(this.aggregation_parsed[this.editdata.aggregation]);
|
||||
}
|
||||
objCopy(args,this.editdata,STUDYPLAN_EDITOR_FIELDS);
|
||||
objCopy(args,this.editdata,STUDYPLAN_EDITOR_PAGE_FIELDS);
|
||||
|
||||
call([{
|
||||
methodname: method,
|
||||
args: args
|
||||
}])[0].done(function(response){
|
||||
if(self.mode == 'create'){
|
||||
self.$emit("created", response);
|
||||
// And reset the edit fields to default
|
||||
self.editdata = {
|
||||
name: '',
|
||||
shortname: '',
|
||||
description: '',
|
||||
context_id: 1,
|
||||
periods : 4,
|
||||
startdate: (new Date()).getFullYear() + '-08-01',
|
||||
enddate: ((new Date()).getFullYear()+1) + '-08-01',
|
||||
aggregation: 'bistate',
|
||||
aggregation_config: '',
|
||||
};
|
||||
}
|
||||
else {
|
||||
// determine if the plan moved context...
|
||||
const moved_from = self.value.context_id;
|
||||
const moved_to = response.context_id;
|
||||
const moved = (moved_from != moved_to);
|
||||
|
||||
if(response.pages[0].periods != self.value.pages[0].periods){
|
||||
// reload the entire model
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_get_studyplan_map',
|
||||
args: { id: self.value.id}
|
||||
}])[0].done(function(response){
|
||||
self.value = ProcessStudyplan(response,true);
|
||||
debug.info('studyplan processed');
|
||||
self.$emit('input',self.value);
|
||||
}).fail(function(error){
|
||||
notification.exception(error);
|
||||
});
|
||||
} else {
|
||||
objCopy(self.value,response,STUDYPLAN_EDITOR_FIELDS);
|
||||
if(updatedplan.pages[0].periods != self.value.pages[0].periods){
|
||||
// If the pages changed, just reload the entire model for the plan
|
||||
call([{
|
||||
methodname: 'local_treestudyplan_get_studyplan_map',
|
||||
args: { id: self.value.id}
|
||||
}])[0].done(function(response){
|
||||
self.value = ProcessStudyplan(response,true);
|
||||
debug.info('studyplan processed');
|
||||
self.$emit('input',self.value);
|
||||
if(moved){
|
||||
self.$emit('moved',self.value,moved_from, moved_to);
|
||||
}
|
||||
}).fail(function(error){
|
||||
notification.exception(error);
|
||||
});
|
||||
} else {
|
||||
// Copy updated fields and trigger update
|
||||
objCopy(self.value,updatedplan,STUDYPLAN_EDITOR_FIELDS);
|
||||
self.$emit('input',self.value);
|
||||
if(moved){
|
||||
self.$emit('moved',self.value, moved_from, moved_to);
|
||||
}
|
||||
}
|
||||
}).fail(notification.exception);
|
||||
},
|
||||
numberFilter(value){
|
||||
return value;
|
||||
}
|
||||
},
|
||||
}
|
||||
,
|
||||
template:
|
||||
`
|
||||
<span class='s-studyplan-edit'>
|
||||
<b-button :variant="variant" v-if='type == "button"' @click.prevent='editPlanStart()'
|
||||
><slot><i class='fa fa-gear'></i></slot></b-button>
|
||||
<a variant="variant" v-else href='#' @click.prevent='editPlanStart()'
|
||||
><slot><i class='fa fa-gear'></i></slot></a>
|
||||
<b-modal
|
||||
v-model="show"
|
||||
size="lg"
|
||||
ok-variant="primary"
|
||||
:title="text.studyplan_edit"
|
||||
@ok="editPlanFinish()"
|
||||
:ok-disabled="Math.min(editdata.name.length,editdata.shortname.length) == 0"
|
||||
>
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_name}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input v-model="editdata.name"
|
||||
:state='editdata.name.length>0'
|
||||
:placeholder="text.studyplan_name_ph"></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_shortname}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input v-model="editdata.shortname"
|
||||
:state='editdata.shortname.length>0'
|
||||
:placeholder="text.studyplan_shortname_ph"></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_idnumber}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input v-model="editdata.idnumber"
|
||||
:placeholder="text.studyplan_idnumber_ph"></b-form-input>
|
||||
</b-col>
|
||||
</b-row> <b-row>
|
||||
<b-col cols="4">{{ text.studyplan_description}}</b-col>
|
||||
<b-col cols="8">
|
||||
<mform name="studyplan_editform" :params="{studyplan_id: value.id}"></mform>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_context}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-select v-model="editdata.context_id"
|
||||
:options="categories" text-field="category.pathname" value-field="context_id"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_slots}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input
|
||||
min="1"
|
||||
type="number"
|
||||
no-wheel
|
||||
v-model="editdata.periods"
|
||||
></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_startdate}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-datepicker start-weekday="1" v-model="editdata.startdate"></b-form-datepicker>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.studyplan_enddate}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-datepicker start-weekday="1" v-model="editdata.enddate" ></b-form-datepicker>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="4">{{ text.choose_aggregation_style}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-select v-model="editdata.aggregation"
|
||||
:options="aggregators" text-field="name" value-field="id"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<template v-if="aggregation_parsed.bistate && editdata.aggregation == 'bistate'">
|
||||
<b-row >
|
||||
<b-col cols="4">{{ text.setting_bistate_thresh_excellent}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input v-model="aggregation_parsed.bistate.thresh_excellent"
|
||||
type="number" number :formatter="numberFilter"
|
||||
></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row >
|
||||
<b-col cols="4">{{ text.setting_bistate_thresh_good}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input v-model="aggregation_parsed.bistate.thresh_good"
|
||||
type="number" number :formatter="numberFilter"
|
||||
></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row >
|
||||
<b-col cols="4">{{ text.setting_bistate_thresh_completed}}</b-col>
|
||||
<b-col cols="8">
|
||||
<b-form-input v-model="aggregation_parsed.bistate.thresh_completed"
|
||||
type="number" number :formatter="numberFilter"
|
||||
></b-form-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row><b-col cols="*"> </b-col></b-row>
|
||||
<b-row>
|
||||
<b-col cols="7">{{ text.setting_bistate_support_failed}}</b-col>
|
||||
<b-col cols="3">
|
||||
<b-form-checkbox v-model="aggregation_parsed.bistate.use_failed"
|
||||
></b-form-checkbox>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row><b-col cols="*"> </b-col></b-row>
|
||||
<b-row >
|
||||
<b-col cols="7">{{ text.setting_bistate_accept_pending_submitted}}</b-col>
|
||||
<b-col cols="3">
|
||||
<b-form-checkbox v-model="aggregation_parsed.bistate.accept_pending_as_submitted"
|
||||
></b-form-checkbox>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
</b-container>
|
||||
</b-modal>
|
||||
<mform
|
||||
name="studyplan_editform"
|
||||
:params="{studyplan_id: value.id, mode: mode, contextid: contextid }"
|
||||
@saved="planSaved"
|
||||
:variant="variant"
|
||||
:type="type"
|
||||
:title="(mode == 'create')?text.studyplan_add:text.studyplan_edit"
|
||||
><slot><i class='fa fa-gear'></i></slot></mform>
|
||||
</span>
|
||||
`
|
||||
});
|
||||
|
|
|
@ -26,7 +26,8 @@ export default {
|
|||
studyplancard: {
|
||||
open: "open",
|
||||
noenddate: "noenddate",
|
||||
idnumber: "studyplan_idnumber"
|
||||
idnumber: "studyplan_idnumber",
|
||||
description: "studyplan_description"
|
||||
}
|
||||
});
|
||||
// Create new eventbus for interaction between item components
|
||||
|
@ -88,13 +89,27 @@ export default {
|
|||
<template v-else>{{value.name}}</template>
|
||||
<slot name='title'></slot>
|
||||
</b-card-title>
|
||||
<div class='s-studyplan-card-icon'><img :src='value.icon' style="width: 64px; height: 64px;"></div>
|
||||
<div class='s-studyplan-card-idnumber' v-if='value.idnumber'><i>{{ text.idnumber}}:</i> {{ value.idnumber }}</div>
|
||||
<div class='s-studyplan-card-description' v-if='value.description'>{{ value.description }}</div>
|
||||
|
||||
<slot></slot>
|
||||
<template #footer>
|
||||
<span :class="'t-timing-'+timing" v-html="startdate + ' - '+ enddate"></span>
|
||||
<span class="s-studyplan-card-buttons">
|
||||
<slot name='footer'></slot>
|
||||
<template v-if='value.description'>
|
||||
<b-button variant="primary" v-b-modal="'modal-description-'+value.id"
|
||||
><i class='fa fa-info-circle'></i> {{ text.description }}</b-button>
|
||||
<b-modal
|
||||
:title="value.name"
|
||||
scrollable
|
||||
centered
|
||||
ok-only
|
||||
size="xl"
|
||||
:id="'modal-description-'+value.id"
|
||||
><span v-html="value.description"></span>
|
||||
</b-modal>
|
||||
</template>
|
||||
<b-button style="float:right;" v-if='open' variant='primary'
|
||||
@click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>
|
||||
</span>
|
||||
|
|
10
amd/src/util/formfields.js
Normal file
10
amd/src/util/formfields.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import Debugger from './debugger';
|
||||
const debug = new Debugger("formfields");
|
||||
/**
|
||||
* convert a text field into an integer only text field
|
||||
* @param {Date|string} id The Id of the form field
|
||||
*/
|
||||
export function text_integer(id){
|
||||
const element = document.getElementById(id);
|
||||
debug.warn("Initializing form element text_integer on ",id,element);
|
||||
}
|
|
@ -50,6 +50,14 @@ export default {
|
|||
type: String,
|
||||
default: "",
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
default: "primary",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "link",
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -78,12 +86,7 @@ export default {
|
|||
self.loading = false;
|
||||
// Process the collected javascript;
|
||||
const js = processCollectedJavascript(data.javascript);
|
||||
debug.info("Replacing content / el",self.$refs["content"]);
|
||||
debug.info("Replacing content / html", html);
|
||||
debug.info("Replacing content / js",js);
|
||||
|
||||
const r = replaceNodeContents(self.$refs["content"], html, js);
|
||||
debug.info("R:",r);
|
||||
replaceNodeContents(self.$refs["content"], html, js);
|
||||
|
||||
}).catch(notification.exception);
|
||||
|
||||
|
@ -104,14 +107,18 @@ export default {
|
|||
call([{
|
||||
methodname: 'local_treestudyplan_submit_mform',
|
||||
args: {formname: self.name, params: JSON.stringify(self.params), formdata: data}
|
||||
}])[0].then(()=>{
|
||||
self.$emit("saved",formdata);
|
||||
}])[0].then((response)=>{
|
||||
const updatedplan = JSON.parse(response.data);
|
||||
self.$emit("saved",updatedplan,formdata);
|
||||
}).catch(notification.exception);
|
||||
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<span class='mform-container'><a href='#' @click.prevent="openForm"><slot><i class="fa fa-cog"></i></slot></a>
|
||||
<span class='mform-container'>
|
||||
<b-button :variant="variant" v-if='type == "button"' @click.prevent='openForm'
|
||||
><slot><i class='fa fa-gear'></i></slot></b-button>
|
||||
<a variant="variant" v-else href='#' @click.prevent='openForm'
|
||||
><slot><i class='fa fa-gear'></i></slot></a>
|
||||
<b-modal
|
||||
ref="editormodal"
|
||||
scrollable
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
*/
|
||||
|
||||
namespace local_treestudyplan;
|
||||
|
||||
use moodle_exception;
|
||||
use \ValueError;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
@ -71,7 +73,6 @@ abstract class aggregator {
|
|||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new aggregatior object based on the specified method
|
||||
* @param mixed $method Aggregation method
|
||||
|
@ -83,7 +84,7 @@ abstract class aggregator {
|
|||
$agclass = self::aggregator_name($method);
|
||||
return new $agclass($configstr);
|
||||
} else {
|
||||
throw new \ValueError("Cannot find aggregator '{$method}'");
|
||||
throw new moodle_exception("Cannot find aggregator '{$method}'");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
40
classes/local/form_elements/text_integer.php
Normal file
40
classes/local/form_elements/text_integer.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace local_treestudyplan\local\form_elements;
|
||||
use MoodleQuickForm_text;
|
||||
use MoodleQuickForm;
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . "/form/text.php");
|
||||
|
||||
|
||||
class text_integer extends MoodleQuickForm_text {
|
||||
public function toHtml()
|
||||
{
|
||||
global $PAGE;
|
||||
// Add number type attribute
|
||||
$this->_attributes['type'] = 'number';
|
||||
|
||||
$html = parent::toHtml();
|
||||
// Add javascript call to handle stuff
|
||||
$PAGE->requires->js_call_amd('local_treestudyplan/util/formfields', 'text_integer', ['id' => $this->getAttribute('id')]);
|
||||
return $html;
|
||||
}
|
||||
|
||||
public static function Register() {
|
||||
global $CFG;
|
||||
MoodleQuickForm::registerElementType(
|
||||
// The custom element is named `course_competency_rule`.
|
||||
// This is the element name used in the `addElement()` function.
|
||||
'text_integer',
|
||||
|
||||
// This is where it's definition is defined.
|
||||
// This does not currently support class auto-loading.
|
||||
"$CFG->dirroot/local/treestudyplan/classes/local/form_elements/text_integer.php",
|
||||
|
||||
// The class name of the element.
|
||||
'local_treestudyplan\local\form_elements\text_integer'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -53,14 +53,14 @@ abstract class formbase extends \moodleform {
|
|||
/**
|
||||
* Process the submission and perform necessary actions
|
||||
* @param object $entry The processed form data
|
||||
* @return bool True if submission successful
|
||||
* @return object|array Data to pass to receiver if submission successful
|
||||
* @throws \moodle_exception if an error must be given for a specific reason.
|
||||
*/
|
||||
abstract protected function process_submitted_data(object $entry);
|
||||
|
||||
/**
|
||||
* Process the submission and perform necessary actions
|
||||
* @return bool True if submission successful
|
||||
* @return object|array Data to pass to receiver if submission successful
|
||||
* @throws \moodle_exception if an error must be given for a specific reason.
|
||||
*/
|
||||
public function process_submission() {
|
||||
|
@ -69,7 +69,7 @@ abstract class formbase extends \moodleform {
|
|||
if($data) {
|
||||
return $this->process_submitted_data($data);
|
||||
} else {
|
||||
return false;
|
||||
throw new \moodle_exception('no_form_data','local_treestudyplan');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace local_treestudyplan\local\forms;
|
||||
|
||||
use local_treestudyplan\aggregator;
|
||||
use local_treestudyplan\studyplan;
|
||||
use local_treestudyplan\courseservice;
|
||||
use local_treestudyplan\local\form_elements\text_integer;
|
||||
use local_treestudyplan\local\helpers\webservicehelper;
|
||||
use moodle_exception;
|
||||
use stdClass;
|
||||
|
@ -25,14 +29,30 @@ class studyplan_editform extends formbase {
|
|||
*/
|
||||
public static function init_customdata(object $params) {
|
||||
$customdata = new stdClass;
|
||||
$customdata->plan = studyplan::find_by_id($params->studyplan_id);
|
||||
$customdata->context = $customdata->plan->context();
|
||||
$customdata->create = $params->mode=='create'?true:false;
|
||||
if($customdata->create){
|
||||
$customdata->context = \context::instance_by_id($params->contextid);
|
||||
} else {
|
||||
|
||||
$customdata->plan = studyplan::find_by_id($params->studyplan_id);
|
||||
$customdata->context = $customdata->plan->context();
|
||||
$customdata->simplemodel = $customdata->plan->simple_model();
|
||||
}
|
||||
|
||||
$customdata->editoroptions = [
|
||||
'trusttext' => true,
|
||||
'subdirs' => true,
|
||||
'maxfiles' => 20,
|
||||
'maxbytes' => 20*1024*1024,
|
||||
'context' => \context_system::instance(),
|
||||
'context' => \context_system::instance(), // Keep the files in system context
|
||||
];
|
||||
$customdata->fileoptions = [
|
||||
'subdirs' => 0,
|
||||
'maxbytes' => 10*1024*1024, // Max 10MiB should be sufficient for a picture.
|
||||
'areamaxbytes' => 10485760,
|
||||
'maxfiles' => 1, // Just one file
|
||||
'accepted_types' => ['.jpg', '.png'],
|
||||
'return_types' => FILE_INTERNAL | FILE_EXTERNAL,
|
||||
];
|
||||
return $customdata;
|
||||
}
|
||||
|
@ -62,16 +82,65 @@ class studyplan_editform extends formbase {
|
|||
with existing moodle code assumptions.
|
||||
The form API does seem needlessly convoluted in it's use, but we need the editor...
|
||||
*/
|
||||
$entry = $DB->get_record(studyplan::TABLE, ['id' => $customdata->plan->id()]);
|
||||
if($customdata->create) {
|
||||
$entry = new stdClass;
|
||||
$entry->context_id = $customdata->context->id;
|
||||
$entry->aggregation = get_config("local_treestudyplan","aggregation_mode");
|
||||
$ag_cfg = json_decode(aggregator::create($entry->aggregation, "")->config_string(),true);
|
||||
|
||||
// Determine the next august 1st for default value purposes.
|
||||
$august = strtotime("first day of august this year");
|
||||
if($august < time()) {
|
||||
$august = strtotime("first day of august next year");
|
||||
}
|
||||
$entry->startdate = $august;
|
||||
$entry->enddate = $august + (364*24*60*60); // Not bothering about leap years here.
|
||||
$entry->periods = 4;
|
||||
} else {
|
||||
$entry = $DB->get_record(studyplan::TABLE, ['id' => $customdata->plan->id()]);
|
||||
|
||||
$entry->startdate = strtotime($customdata->simplemodel['pages'][0]['startdate']);
|
||||
$entry->enddate = strtotime($customdata->simplemodel['pages'][0]['enddate']);
|
||||
$entry->periods = $customdata->simplemodel['pages'][0]['periods'];
|
||||
$ag_cfg = json_decode($customdata->plan->aggregator()->config_string(),true);
|
||||
}
|
||||
|
||||
// Prepare the editor
|
||||
$entry = file_prepare_standard_editor( $entry,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
\context_system::instance(),
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$customdata->plan->id());
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
\context_system::instance(),
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
($customdata->create)?null:$customdata->plan->id()
|
||||
);
|
||||
|
||||
// Prepare file area for the icon
|
||||
// Get an unused draft itemid which will be used for this form.
|
||||
$draftitemid = file_get_submitted_draft_itemid('icon');
|
||||
file_prepare_draft_area(
|
||||
// The $draftitemid is the target location.
|
||||
$draftitemid,
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that files are currently stored in
|
||||
// and will be copied from.
|
||||
\context_system::instance()->id,
|
||||
'local_treestudyplan',
|
||||
'icon',
|
||||
($customdata->create)?null:$customdata->plan->id(),
|
||||
$customdata->fileoptions
|
||||
);
|
||||
$entry->icon = $draftitemid;
|
||||
|
||||
// Add aggregation configs to entry.
|
||||
|
||||
foreach ($ag_cfg as $key => $val) {
|
||||
$entrykey = $entry->aggregation."_".$key;
|
||||
$entry->$entrykey = $val;
|
||||
}
|
||||
|
||||
return $entry;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +149,117 @@ class studyplan_editform extends formbase {
|
|||
public function definition() {
|
||||
$mform = $this->_form;
|
||||
$customdata = (object)$this->_customdata;
|
||||
|
||||
// Register integer type
|
||||
text_integer::Register();
|
||||
|
||||
// Define the form
|
||||
$field = 'name';
|
||||
$mform->addElement('text',$field,
|
||||
get_string('studyplan_name','local_treestudyplan'),
|
||||
[]);
|
||||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$field = 'shortname';
|
||||
$mform->addElement('text',$field,
|
||||
get_string('studyplan_shortname','local_treestudyplan'),
|
||||
[]);
|
||||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$field = 'idnumber';
|
||||
$mform->addElement('text',$field,
|
||||
get_string('studyplan_idnumber','local_treestudyplan'),
|
||||
[]);
|
||||
|
||||
$contextlist = [];
|
||||
foreach(courseservice::list_accessible_categories() as $c){
|
||||
$contextlist[$c['context_id']] = implode(" / ",$c['category']['path']);
|
||||
}
|
||||
$mform->addElement('autocomplete', 'context_id',
|
||||
get_string('studyplan_context','local_treestudyplan'),
|
||||
$contextlist);
|
||||
|
||||
$mform->addRule('context_id', null, 'required', null, 'client');
|
||||
|
||||
$mform->addElement(
|
||||
'filemanager',
|
||||
'icon',
|
||||
get_string('studyplan_icon', 'local_treestudyplan'),
|
||||
null,
|
||||
$customdata->fileoptions
|
||||
);
|
||||
|
||||
$field = 'startdate';
|
||||
$mform->addElement('date_selector',$field,
|
||||
get_string('studyplan_startdate','local_treestudyplan'),
|
||||
[]);
|
||||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$field = 'enddate';
|
||||
$mform->addElement('date_selector',$field,
|
||||
get_string('studyplan_startdate','local_treestudyplan'),
|
||||
[]);
|
||||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$field = 'periods';
|
||||
$mform->addElement('text_integer',$field,
|
||||
get_string('studyplan_slots','local_treestudyplan'),
|
||||
[]);
|
||||
$mform->setType($field, PARAM_INT);
|
||||
$mform->addRule($field, null, 'required', null, 'client');
|
||||
|
||||
$aggregators = [];
|
||||
foreach(aggregator::list_model() as $a){
|
||||
// Add method only if not deprecated or currently used
|
||||
if ( $customdata->simplemodel['aggregation'] == $a['id'] || !($a['deprecated']) ) {
|
||||
$aggregators[$a['id']] = $a['name'];
|
||||
}
|
||||
}
|
||||
$mform->addElement('select','aggregation',
|
||||
get_string('choose_aggregation_style','local_treestudyplan'),
|
||||
$aggregators);
|
||||
|
||||
/* Start Bistate aggregation specific items */
|
||||
$field = 'bistate_thresh_excellent';
|
||||
$mform->addElement('text_integer',$field,
|
||||
get_string('setting_bistate_thresh_excellent','local_treestudyplan'),
|
||||
[],
|
||||
);
|
||||
$mform->setType($field, PARAM_INT);
|
||||
$mform->hideIf($field, "aggregation", "neq", "bistate");
|
||||
|
||||
$field = 'bistate_thresh_good';
|
||||
$mform->addElement('text_integer',$field,
|
||||
get_string('setting_bistate_thresh_good','local_treestudyplan'),
|
||||
[],
|
||||
);
|
||||
$mform->setType($field, PARAM_INT);
|
||||
$mform->hideIf($field, "aggregation", "neq", "bistate");
|
||||
|
||||
$field = 'bistate_thresh_completed';
|
||||
$mform->addElement('text_integer',$field,
|
||||
get_string('setting_bistate_thresh_completed','local_treestudyplan'),
|
||||
[],
|
||||
);
|
||||
$mform->setType($field, PARAM_INT);
|
||||
$mform->hideIf($field, "aggregation", "neq", "bistate");
|
||||
|
||||
$field = 'bistate_use_failed';
|
||||
$mform->addElement('checkbox',$field,
|
||||
get_string('setting_bistate_support_failed','local_treestudyplan'),
|
||||
[],
|
||||
);
|
||||
$mform->hideIf($field, "aggregation", "neq", "bistate");
|
||||
|
||||
$field = 'bistate_accept_pending_as_submitted';
|
||||
$mform->addElement('checkbox',$field,
|
||||
get_string('setting_bistate_accept_pending_submitted','local_treestudyplan'),
|
||||
[],
|
||||
);
|
||||
$mform->hideIf($field, "aggregation", "neq", "bistate");
|
||||
|
||||
/* End Bistate aggregation specific items */
|
||||
|
||||
$mform->addElement('editor', 'description_editor',
|
||||
get_string('studyplan_description', 'local_treestudyplan'),
|
||||
null,
|
||||
|
@ -96,25 +275,81 @@ class studyplan_editform extends formbase {
|
|||
*/
|
||||
protected function process_submitted_data($entry) {
|
||||
$customdata = (object)$this->_customdata;
|
||||
// Process the provided files in the description editor
|
||||
$entry = file_postupdate_standard_editor($entry,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
\context_system::instance(),
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$customdata->plan->id());
|
||||
|
||||
$f = fopen("/tmp/debug","a+");
|
||||
fputs($f,print_r($entry,true));
|
||||
fclose($f);
|
||||
// Add aggregation configs to entry.
|
||||
$ag_cfg = json_decode(aggregator::create($entry->aggregation, "")->config_string(),true); // Retrieve default config string from selected aggregation method
|
||||
foreach ($ag_cfg as $key => $val) {
|
||||
$entrykey = $entry->aggregation."_".$key;
|
||||
$ag_cfg[$key] = $entry->$entrykey;
|
||||
}
|
||||
$aggregation_config = json_encode($ag_cfg);
|
||||
|
||||
// Use our own abstraction to update the record, so caches are maintained
|
||||
$customdata->plan->edit(['description' => $entry->description,
|
||||
'descriptionformat' => $entry->descriptionformat]);
|
||||
if($customdata->create) {
|
||||
|
||||
// Use our own abstraction to update the record, so caches are maintained
|
||||
$plan = studyplan::add(['name' => $entry->name,
|
||||
'shortname' => $entry->shortname,
|
||||
'idnumber' => $entry->idnumber,
|
||||
'context_id' => $customdata->contextid,
|
||||
'aggregation' => $entry->aggregation,
|
||||
'aggregation_config' => $aggregation_config,
|
||||
'startdate' => date("Y-m-d",$entry->startdate),
|
||||
'enddate' => date("Y-m-d",$entry->enddate),
|
||||
'periods' => $entry->periods,
|
||||
]);
|
||||
// Process the provided files in the description editor
|
||||
$entry = file_postupdate_standard_editor($entry,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
\context_system::instance(),
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$plan->id());
|
||||
// Update the description
|
||||
$plan->edit([
|
||||
'description' => $entry->description,
|
||||
'descriptionformat' => $entry->descriptionformat,
|
||||
]);
|
||||
} else {
|
||||
$plan = $customdata->plan;
|
||||
|
||||
return true;
|
||||
// Process the provided files in the description editor
|
||||
$entry = file_postupdate_standard_editor($entry,
|
||||
'description',
|
||||
$customdata->editoroptions,
|
||||
\context_system::instance(),
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$plan->id());
|
||||
|
||||
// Use our own abstraction to update the record, so caches are maintained
|
||||
$plan->edit(['name' => $entry->name,
|
||||
'shortname' => $entry->shortname,
|
||||
'idnumber' => $entry->idnumber,
|
||||
'description' => $entry->description,
|
||||
'descriptionformat' => $entry->descriptionformat,
|
||||
'aggregation' => $entry->aggregation,
|
||||
'aggregation_config' => $aggregation_config,
|
||||
'startdate' => date("Y-m-d",$entry->startdate),
|
||||
'enddate' => date("Y-m-d",$entry->enddate),
|
||||
'periods' => $entry->periods,
|
||||
]);
|
||||
}
|
||||
|
||||
// Now save the icon file in correct part of the File API.
|
||||
file_save_draft_area_files(
|
||||
// The $entry->icon property contains the itemid of the draft file area.
|
||||
$entry->icon,
|
||||
// The combination of contextid / component / filearea / itemid
|
||||
// form the virtual bucket that file are stored in.
|
||||
\context_system::instance()->id,
|
||||
'local_treestudyplan',
|
||||
'icon',
|
||||
$plan->id(),
|
||||
$customdata->fileoptions
|
||||
);
|
||||
|
||||
return $plan->simple_model(); // Return the simple model of the plan to make sure we can update stuff
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -168,9 +168,9 @@ class studyitemconnection {
|
|||
'to_id' => $toid,
|
||||
]);
|
||||
|
||||
return success::success('Items Disconnected');
|
||||
return success::success(['msg'=>'Items Disconnected']);
|
||||
} else {
|
||||
return success::success('Connection does not exist');
|
||||
return success::success(['msg'=>'Connection does not exist']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,62 @@ class studyplan {
|
|||
return $this->r->name;
|
||||
}
|
||||
|
||||
private function icon() {
|
||||
global $CFG;
|
||||
$fs = \get_file_storage();
|
||||
// Returns an array of `stored_file` instances.
|
||||
$files = $fs->get_area_files(\context_system::instance()->id, 'local_treestudyplan', 'icon', $this->id);
|
||||
|
||||
if (count($files) > 0 ){
|
||||
$file = array_shift($files);
|
||||
if ($file->get_filename() == ".") {
|
||||
// Get next file if the first is the directory itself.
|
||||
$file = array_shift($files);
|
||||
}
|
||||
|
||||
$url = \moodle_url::make_pluginfile_url(
|
||||
$file->get_contextid(),
|
||||
$file->get_component(),
|
||||
$file->get_filearea(),
|
||||
$file->get_itemid(),
|
||||
$file->get_filepath(),
|
||||
$file->get_filename(),
|
||||
false // Do not force download of the file.
|
||||
);
|
||||
} else {
|
||||
// Try the configured default in settings.
|
||||
$defaulticon = get_config('local_treestudyplan', 'defaulticon');
|
||||
if (empty($defaulticon)) {
|
||||
// Fall back to the standard (ugly) default image.
|
||||
$url = new \moodle_url($CFG->wwwroot . "/local/treestudyplan/pix/default_icon.png");
|
||||
} else {
|
||||
$url = \moodle_url::make_pluginfile_url(\context_system::instance()->id, 'local_treestudyplan', 'defaulticon', 0, "/",
|
||||
$defaulticon);
|
||||
}
|
||||
|
||||
}
|
||||
return $url->out();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return description with all file references resolved
|
||||
*/
|
||||
public function description() {
|
||||
$text = file_rewrite_pluginfile_urls(
|
||||
// The content of the text stored in the database.
|
||||
$this->r->description,
|
||||
// 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.
|
||||
\context_system::instance()->id, // System instance is always used for this
|
||||
'local_treestudyplan',
|
||||
'studyplan',
|
||||
$this->id
|
||||
);
|
||||
return $text;
|
||||
}
|
||||
/**
|
||||
* Return the studyplan pages associated with this plan
|
||||
* @return studyplanpage[]
|
||||
|
@ -160,6 +216,7 @@ class studyplan {
|
|||
"context_id" => new \external_value(PARAM_INT, 'context_id of studyplan'),
|
||||
"description" => new \external_value(PARAM_RAW, 'description of studyplan'),
|
||||
"descriptionformat" => new \external_value(PARAM_INT, 'description format'),
|
||||
"icon" => new \external_value(PARAM_RAW,'icon for this plan'),
|
||||
"aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator'),
|
||||
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
||||
"aggregation_info" => aggregator::basic_structure(),
|
||||
|
@ -183,8 +240,9 @@ class studyplan {
|
|||
'shortname' => $this->r->shortname,
|
||||
'idnumber' => $this->r->idnumber,
|
||||
'context_id' => $this->context()->id,
|
||||
'description' => $this->r->description,
|
||||
'description' => $this->description(),
|
||||
'descriptionformat' => $this->r->descriptionformat,
|
||||
'icon' => $this->icon(),
|
||||
'aggregation' => $this->r->aggregation,
|
||||
'aggregation_config' => $this->aggregator->config_string(),
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
|
@ -204,6 +262,7 @@ class studyplan {
|
|||
"idnumber" => new \external_value(PARAM_TEXT, 'idnumber of curriculum'),
|
||||
"description" => new \external_value(PARAM_RAW, 'description of studyplan'),
|
||||
"descriptionformat" => new \external_value(PARAM_INT, 'description format'),
|
||||
"icon" => new \external_value(PARAM_RAW,'icon for this plan'),
|
||||
"context_id" => new \external_value(PARAM_INT, 'context_id of studyplan'),
|
||||
"aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator'),
|
||||
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
||||
|
@ -232,8 +291,9 @@ class studyplan {
|
|||
'name' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'idnumber' => $this->r->idnumber,
|
||||
'description' => $this->r->description,
|
||||
'description' => $this->description(),
|
||||
'descriptionformat' => $this->r->descriptionformat,
|
||||
'icon' => $this->icon(),
|
||||
'context_id' => $this->context()->id,
|
||||
"aggregation" => $this->r->aggregation,
|
||||
"aggregation_config" => $this->aggregator->config_string(),
|
||||
|
@ -562,6 +622,7 @@ class studyplan {
|
|||
"shortname" => new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
||||
"description" => new \external_value(PARAM_RAW, 'description of studyplan'),
|
||||
"descriptionformat" => new \external_value(PARAM_INT, 'description format'),
|
||||
"icon" => new \external_value(PARAM_RAW,'icon for this plan'),
|
||||
"idnumber" => new \external_value(PARAM_TEXT, 'idnumber of curriculum'),
|
||||
"pages" => new \external_multiple_structure(studyplanpage::user_structure()),
|
||||
"aggregation_info" => aggregator::basic_structure(),
|
||||
|
@ -580,8 +641,9 @@ class studyplan {
|
|||
'id' => $this->r->id,
|
||||
'name' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'description' => $this->r->description,
|
||||
'description' => $this->description(),
|
||||
'descriptionformat' => $this->r->descriptionformat,
|
||||
'icon' => $this->icon(),
|
||||
'idnumber' => $this->r->idnumber,
|
||||
'pages' => [],
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
|
@ -618,8 +680,13 @@ class studyplan {
|
|||
'name' => $name,
|
||||
'shortname' => $shortname,
|
||||
'description' => $this->r->description,
|
||||
'descriptionformat' => $this->r->descriptionformat,
|
||||
'aggregation' => $this->r->aggregation,
|
||||
'aggregation_config' => $this->r->aggregation_config
|
||||
]);
|
||||
|
||||
//TODO: Copy any files related to this userid....
|
||||
|
||||
// Next, copy the studylines.
|
||||
|
||||
foreach ($this->pages() as $p) {
|
||||
|
|
|
@ -41,27 +41,31 @@ class success {
|
|||
/**
|
||||
* Create new successful result with optional message
|
||||
* @param string $msg Message to add to result
|
||||
* @param array|object $data Custom data to pass to receiver
|
||||
*/
|
||||
public static function success($msg = "") : self {
|
||||
return new self(true, $msg);
|
||||
public static function success($data=[]) : self {
|
||||
return new self(true, 'success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new failed result with optional message
|
||||
* @param string $msg Message to add to result
|
||||
* @param array|object $data Custom data to pass to receiver
|
||||
*/
|
||||
public static function fail($msg = "") : self {
|
||||
return new self(false, $msg);
|
||||
public static function fail($msg = "", $data=[]) : self {
|
||||
return new self(false, $msg, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new succes result
|
||||
* @param bool $success Whether result is succesful or not
|
||||
* @param string $msg Message to add to result
|
||||
* @param array|object $data Custom data to pass to receiver
|
||||
*/
|
||||
public function __construct($success, $msg) {
|
||||
public function __construct($success, $msg, $data=[]) {
|
||||
$this->success = ($success) ? true : false;
|
||||
$this->msg = $msg;
|
||||
$this->data = json_encode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,6 +75,7 @@ class success {
|
|||
return new \external_single_structure([
|
||||
"success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
|
||||
"msg" => new \external_value(PARAM_TEXT, 'message'),
|
||||
"data" => new \external_value(PARAM_RAW, 'message'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -80,7 +85,7 @@ class success {
|
|||
* @return array Webservice value
|
||||
*/
|
||||
public function model() {
|
||||
return ["success" => $this->success, "msg" => $this->msg];
|
||||
return ["success" => $this->success, "msg" => $this->msg, "data" => $this->data];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,4 +106,13 @@ class success {
|
|||
return $this->msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data
|
||||
*
|
||||
* @return string Message
|
||||
*/
|
||||
public function data() {
|
||||
return json_decode($this->data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -157,11 +157,8 @@ class utilityservice extends \external_api {
|
|||
// Load the form, provide submitted form data and perform security checks
|
||||
$mform = self::load_mform($formname, $params, $ajaxformdata);
|
||||
|
||||
if ($mform->process_submission() !== false) {
|
||||
return success::success()->model();
|
||||
} else {
|
||||
return success::fail("Error in submission data")->model();
|
||||
}
|
||||
$return = $mform->process_submission();
|
||||
return success::success($return)->model();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<FIELD NAME="name" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="shortname" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="descriptionformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="descriptionformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
|
||||
<FIELD NAME="idnumber" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="aggregation" TYPE="char" LENGTH="30" NOTNULL="true" DEFAULT="bistate" SEQUENCE="false"/>
|
||||
<FIELD NAME="aggregation_config" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
|
@ -170,6 +170,7 @@
|
|||
<FIELD NAME="fullname" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="shortname" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="descriptionformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
|
||||
<FIELD NAME="startdate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="enddate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
|
|
|
@ -497,7 +497,7 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
|
||||
// Define field descriptionformat to be added to local_treestudyplan.
|
||||
$table = new xmldb_table('local_treestudyplan');
|
||||
$field = new xmldb_field('descriptionformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'description');
|
||||
$field = new xmldb_field('descriptionformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '1', 'description');
|
||||
|
||||
// Conditionally launch add field descriptionformat.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
|
@ -507,6 +507,19 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2023101900, 'local', 'treestudyplan');
|
||||
}
|
||||
if ($oldversion < 2023102200) {
|
||||
|
||||
// Define field descriptionformat to be added to local_treestudyplan.
|
||||
$table = new xmldb_table('local_treestudyplan_page');
|
||||
$field = new xmldb_field('descriptionformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '1', 'description');
|
||||
|
||||
// Conditionally launch add field descriptionformat.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2023102200, 'local', 'treestudyplan');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -128,7 +128,6 @@ print $OUTPUT->header();
|
|||
@click='selectStudyplan(studyplan)'>{{ studyplan.name }}</b-form-select-option>
|
||||
</b-form-select>
|
||||
<t-studyplan-edit
|
||||
@creating=""
|
||||
@created="onStudyPlanCreated"
|
||||
v-if='!activestudyplan && !loadingstudyplan'
|
||||
mode="create"
|
||||
|
|
47
lib.php
47
lib.php
|
@ -404,23 +404,37 @@ function local_treestudyplan_pluginfile(
|
|||
): bool {
|
||||
global $DB,$USER;
|
||||
|
||||
$fp = fopen("/tmp/debug","a+");
|
||||
fputs($fp, print_r([
|
||||
'context' => $context,
|
||||
'filearea' => $filearea,
|
||||
'args' => $args,
|
||||
'forcedownload' => $forcedownload,
|
||||
'options' => $options,
|
||||
],true)."\n\n");
|
||||
fclose($fp);
|
||||
|
||||
$studyplan_filecaps = ["local/treestudyplan:editstudyplan","local/treestudyplan:viewuserreports"];
|
||||
|
||||
// Check the contextlevel is as expected - the studyplan plugin only uses system context for storing files.
|
||||
// This avoids headaches when moving studyplans, while the security impact is minimal...
|
||||
// This avoids headaches when moving studyplans between contexts, while the security impact is minimal...
|
||||
if ($context->contextlevel != CONTEXT_SYSTEM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Make sure the filearea is one of those used by the plugin.
|
||||
if ($filearea == "studyplan") {
|
||||
if (in_array($filearea,["studyplan","icon"])) {
|
||||
// The args is an array containing [itemid, path].
|
||||
// Fetch the itemid from the path.
|
||||
$itemid = array_shift($args);
|
||||
|
||||
$plan = studyplan::find_by_id($itemid);
|
||||
$planctx = $plan->context();
|
||||
|
||||
// Check if the current user has access to this studyplan
|
||||
if (webservicehelper::has_capabilities(['',''],$planctx) || $plan->has_linked_user($USER)) {
|
||||
// Extract the filename / filepath from the $args array.
|
||||
if ( webservicehelper::has_capabilities($studyplan_filecaps,$planctx) || $plan->has_linked_user($USER)) {
|
||||
// Extract the filename / filepath from the $args array
|
||||
$filename = array_pop($args); // The last item in the $args array.
|
||||
if (empty($args)) {
|
||||
// $args is empty => the path is '/'.
|
||||
|
@ -432,20 +446,41 @@ function local_treestudyplan_pluginfile(
|
|||
|
||||
// Retrieve the file from the Files API.
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->get_file(\context_system::instance(), 'local_treestudyplan', $filearea, $itemid, $filepath, $filename);
|
||||
$file = $fs->get_file(\context_system::instance()->id, 'local_treestudyplan', $filearea, $itemid, $filepath, $filename);
|
||||
if (!$file) {
|
||||
// The file does not exist.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
|
||||
send_stored_file($file, 24*60*60, 0, $forcedownload, $options);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (in_array($filearea,['defaulticon'])) {
|
||||
// The args is an array containing [itemid, path].
|
||||
// Fetch the itemid from the path.
|
||||
$itemid = array_shift($args);
|
||||
|
||||
// Extract the filename / filepath from the $args array
|
||||
$filename = array_pop($args); // The last item in the $args array.
|
||||
if (empty($args)) {
|
||||
// $args is empty => the path is '/'.
|
||||
$filepath = '/';
|
||||
} else {
|
||||
// $args contains the remaining elements of the filepath.
|
||||
$filepath = '/' . implode('/', $args) . '/';
|
||||
}
|
||||
|
||||
// Retrieve the file from the Files API.
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->get_file(\context_system::instance()->id, 'local_treestudyplan', $filearea, $itemid, $filepath, $filename);
|
||||
if (!$file) {
|
||||
// The file does not exist.
|
||||
return false;
|
||||
}
|
||||
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
|
||||
send_stored_file($file, 24*60*60, 0, $forcedownload, $options);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
BIN
pix/default_icon.png
Normal file
BIN
pix/default_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
10
settings.php
10
settings.php
|
@ -54,6 +54,16 @@ if ($hassiteconfig) {
|
|||
true,
|
||||
));
|
||||
|
||||
// Default image for study plans
|
||||
$page->add(new admin_setting_configstoredfile('local_treestudyplan/defaulticon',
|
||||
get_string('setting_defaulticon', 'local_treestudyplan'),
|
||||
get_string('setting_defaulticon_desc', 'local_treestudyplan'),
|
||||
'defaulticon', 0,
|
||||
[
|
||||
'maxfiles' => 1,
|
||||
'accepted_types' => ['.jpg', '.png']]
|
||||
));
|
||||
|
||||
// OUTCOME AGGREGATION SETTINGS.
|
||||
$page->add(new admin_setting_heading('local_treestudyplan/aggregation_heading',
|
||||
get_string('setting_aggregation_heading', 'local_treestudyplan'),
|
||||
|
|
|
@ -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 = 2023102100; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->version = 2023102300; // YYYYMMDDHH (year, month, day, iteration).
|
||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
||||
|
||||
$plugin->release = "1.1.0-b";
|
||||
|
|
Loading…
Reference in New Issue
Block a user