Code style fixes (mostly white-space)

This commit is contained in:
PMKuipers 2023-09-08 12:47:29 +02:00
parent 5eacc6f68c
commit 9f8a87ac83
68 changed files with 720 additions and 709 deletions

View File

@ -34,11 +34,11 @@ module.exports = function(grunt) {
const sass = require('sass'); const sass = require('sass');
// Import grunt configuration for moodle base // Import grunt configuration for moodle base
process.chdir("../.."); // change dir to moodle base process.chdir("../.."); // change dir to moodle base
require(path.resolve(`./Gruntfile.js`))(grunt); // Run Gruntfile module from moodle base require(path.resolve(`./Gruntfile.js`))(grunt); // Run Gruntfile module from moodle base
grunt.registerTask('scssplugin','Compile scss/*.sccs into styles.css and css/devstyles.css', () => { grunt.registerTask('scssplugin','Compile scss/*.sccs into styles.css and css/devstyles.css', () => {
const devoutput = 'css/devstyles.css'; const devoutput = 'css/devstyles.css';
const prodoutput = 'styles.css'; const prodoutput = 'styles.css';
@ -93,7 +93,7 @@ module.exports = function(grunt) {
// Remove gherkinlint from the startup list, since it exits with an error because of errors in moodle's own code // Remove gherkinlint from the startup list, since it exits with an error because of errors in moodle's own code
grunt.moodleEnv.startupTasks.splice(grunt.moodleEnv.startupTasks.indexOf("gherkinlint"),1); grunt.moodleEnv.startupTasks.splice(grunt.moodleEnv.startupTasks.indexOf("gherkinlint"),1);
// Add the 'scssplugin' task as a startup task. // Add the 'scssplugin' task as a startup task.
grunt.moodleEnv.startupTasks.push('scssplugin'); grunt.moodleEnv.startupTasks.push('scssplugin');

View File

@ -1,6 +1,6 @@
# Moodle studyplan plugin # Moodle studyplan plugin
Plugin to organize a curriculum into an easy to read graphical respresentation of courses and student progress therein. Plugin to organize a curriculum into an easy to read graphical respresentation of courses and student progress therein.
The studyplan plugin extends Moodle with the ability to show students and teachers an overview of their curriculum and results therein. The studyplan plugin extends Moodle with the ability to show students and teachers an overview of their curriculum and results therein.
By showing students an easy to read graphical overview of their progress over multiple courses, students are more in control of their own By showing students an easy to read graphical overview of their progress over multiple courses, students are more in control of their own
@ -14,7 +14,7 @@ Install the plugin by adding the zip file manually to the installed plugins in y
### Dynamic Navigation links ### Dynamic Navigation links
The studyplan plugin will automatically add links to the flat navigation bat used in Moodle 3.11 and below The studyplan plugin will automatically add links to the flat navigation bat used in Moodle 3.11 and below
However, Moodle 4.0-4.2 use a new "primary navigation" bar, and does not yet support customizing the primary navigation bar (it is a planned feature though) However, Moodle 4.0-4.2 use a new "primary navigation" bar, and does not yet support customizing the primary navigation bar (it is a planned feature though)
Use the following workaround: Use the following workaround:
You can add custom primary menu items in **Site administration** -> **Appearance** -> **Theme settings** You can add custom primary menu items in **Site administration** -> **Appearance** -> **Theme settings**
That page contains an item called Custom menu items". Add the following into that area to custimize it That page contains an item called Custom menu items". Add the following into that area to custimize it
@ -37,13 +37,13 @@ The recommended roles below give an example of that.
#### Recommended roles #### Recommended roles
It is recommended to create the following new roles: It is recommended to create the following new roles:
- **Studyplan Manager** (*studyplanmanager*) - **Studyplan Manager** (*studyplanmanager*)
Context types: *System*, *Category* Context types: *System*, *Category*
*Capabilities* *Capabilities*
- **Manage studyplans** (*local/treestudyplan:editstudyplan*) - **Manage studyplans** (*local/treestudyplan:editstudyplan*)
- **Studyplan Viewer** (*studyplanviewer*) - **Studyplan Viewer** (*studyplanviewer*)
Context types: *System*, *Category* Context types: *System*, *Category*
*Capabilities* *Capabilities*
- **View study plan of others** (*local/treestudyplan:viewuserreports*) - **View study plan of others** (*local/treestudyplan:viewuserreports*)
Then assign the role **studyplanmanager** in a specific category context, or the system context to all users who should be able to create and edit studyplans in that specific context Then assign the role **studyplanmanager** in a specific category context, or the system context to all users who should be able to create and edit studyplans in that specific context
@ -54,7 +54,7 @@ After installing the plugin you can find the main configuration in **Site admini
## Documentation / User manual ## Documentation / User manual
After installing the plugin, the detailed user manual can be found under **Site administration** -> **Courses** -> **Studyplans** -> **Studyplan plugin documentation** After installing the plugin, the detailed user manual can be found under **Site administration** -> **Courses** -> **Studyplans** -> **Studyplan plugin documentation**
Alternatively, you can unpack this ZIP file and open the **doc/index.html** file. Alternatively, you can unpack this ZIP file and open the **doc/index.html** file.
(Note that viewing the html files directly will not five the optimal experience, since fontawesome tags and bootstrap styles will not be properly rendered) (Note that viewing the html files directly will not five the optimal experience, since fontawesome tags and bootstrap styles will not be properly rendered)
## Development ## Development

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"cfg-grades.min.js","sources":["../src/cfg-grades.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n// You can call it anything you like\n\nimport {get_string,get_strings} from 'core/str';\nimport {call} from 'core/ajax';\nimport Debugger from './debugger';\n\nimport {load_strings} from './string-helper';\n\nlet debug = new Debugger(\"treestudyplan-config-grades\");\n\n/*\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n*/\n\n/**\n * Initialize grade cfg page\n */\nexport function init() {\n { const\n intRx = /\\d/,\n integerChange = (event) => {\n if ( (event.key.length > 1) || intRx.test(event.key)\n ) { \n return; \n }\n event.preventDefault();\n };\n \n for (let input of document.querySelectorAll( 'input[type=\"number\"][step=\"1\"][min=\"0\"]' )){\n input.addEventListener(\"keydown\", integerChange);\n } \n \n }\n\n { const\n decimal= /^[0-9]*?\\.[0-9]*?$/,\n intRx = /\\d/,\n floatChange = (event) => {\n if ( (event.key.length > 1) || ( (event.key === \".\") && (!event.currentTarget.value.match(decimal)) )\n || intRx.test(event.key)\n ) { \n return; \n }\n event.preventDefault();\n };\n \n for (let input of document.querySelectorAll( 'input[type=\"number\"][min=\"0\"]:not([step])' )){\n input.addEventListener(\"keydown\", floatChange);\n } \n for (let input of document.querySelectorAll( 'input[type=\"text\"].float' )){\n input.addEventListener(\"keydown\", floatChange);\n } \n \n }\n\n\n}\n\n"],"names":["intRx","integerChange","event","key","length","test","preventDefault","document","querySelectorAll","addEventListener","decimal","floatChange","currentTarget","value","match"],"mappings":"kgDA6BQA,MAAQ,KACRC,cAAgB,SAACC,OACTA,MAAMC,IAAIC,OAAS,GAAMJ,MAAMK,KAAKH,MAAMC,MAIhDD,MAAMI,uDAGQC,SAASC,iBAAkB,+FAA4C,aACjFC,iBAAiB,UAAWR,6EAMlCS,QAAS,qBACTV,OAAQ,KACRW,YAAc,SAACT,OACPA,MAAMC,IAAIC,OAAS,GAAuB,MAAdF,MAAMC,MAAkBD,MAAMU,cAAcC,MAAMC,MAAMJ,UACjFV,OAAMK,KAAKH,MAAMC,MAI1BD,MAAMI,wDAGQC,SAASC,iBAAkB,oGAA8C,cACnFC,iBAAiB,UAAWE,mHAElBJ,SAASC,iBAAkB,mFAA6B,cAClEC,iBAAiB,UAAWE,oEA9C9B,yEAAa"} {"version":3,"file":"cfg-grades.min.js","sources":["../src/cfg-grades.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n// You can call it anything you like\n\nimport {get_string,get_strings} from 'core/str';\nimport {call} from 'core/ajax';\nimport Debugger from './debugger';\n\nimport {load_strings} from './string-helper';\n\nlet debug = new Debugger(\"treestudyplan-config-grades\");\n\n/*\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n*/\n\n/**\n * Initialize grade cfg page\n */\nexport function init() {\n { const\n intRx = /\\d/,\n integerChange = (event) => {\n if ( (event.key.length > 1) || intRx.test(event.key)\n ) {\n return;\n }\n event.preventDefault();\n };\n\n for (let input of document.querySelectorAll( 'input[type=\"number\"][step=\"1\"][min=\"0\"]' )){\n input.addEventListener(\"keydown\", integerChange);\n }\n\n }\n\n { const\n decimal= /^[0-9]*?\\.[0-9]*?$/,\n intRx = /\\d/,\n floatChange = (event) => {\n if ( (event.key.length > 1) || ( (event.key === \".\") && (!event.currentTarget.value.match(decimal)) )\n || intRx.test(event.key)\n ) {\n return;\n }\n event.preventDefault();\n };\n\n for (let input of document.querySelectorAll( 'input[type=\"number\"][min=\"0\"]:not([step])' )){\n input.addEventListener(\"keydown\", floatChange);\n }\n for (let input of document.querySelectorAll( 'input[type=\"text\"].float' )){\n input.addEventListener(\"keydown\", floatChange);\n }\n\n }\n\n\n}\n\n"],"names":["intRx","integerChange","event","key","length","test","preventDefault","document","querySelectorAll","addEventListener","decimal","floatChange","currentTarget","value","match"],"mappings":"kgDA6BQA,MAAQ,KACRC,cAAgB,SAACC,OACTA,MAAMC,IAAIC,OAAS,GAAMJ,MAAMK,KAAKH,MAAMC,MAIhDD,MAAMI,uDAGQC,SAASC,iBAAkB,+FAA4C,aACjFC,iBAAiB,UAAWR,6EAMlCS,QAAS,qBACTV,OAAQ,KACRW,YAAc,SAACT,OACPA,MAAMC,IAAIC,OAAS,GAAuB,MAAdF,MAAMC,MAAkBD,MAAMU,cAAcC,MAAMC,MAAMJ,UACjFV,OAAMK,KAAKH,MAAMC,MAI1BD,MAAMI,wDAGQC,SAASC,iBAAkB,oGAA8C,cACnFC,iBAAiB,UAAWE,mHAElBJ,SAASC,iBAAkB,mFAA6B,cAClEC,iBAAiB,UAAWE,oEA9C9B,yEAAa"}

View File

@ -1,3 +1,3 @@
define("local_treestudyplan/modedit-modal",["exports","core/fragment","./util/string-helper","core/ajax","core/notification","core/templates"],(function(_exports,_fragment,_stringHelper,_ajax,_notification,_templates){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};var _default={install:function(Vue){var strings=(0,_stringHelper.load_strings)({editmod:{save$core:"save$core",cancel$core:"cancel$core"}});Vue.component("s-edit-mod",{props:{cmid:{type:Number},coursectxid:{type:Number},title:{type:String,default:""},genericonly:{type:Boolean,default:!1}},data:function(){return{content:"",text:strings.editmod}},computed:{},methods:{openForm:function(){this.$refs.editormodal.show()},onShown:function(){var self=this,params={cmid:this.cmid};console.info("Loading form"),(0,_fragment.loadFragment)("local_treestudyplan","mod_edit_form",this.coursectxid,params).then((function(html,js){(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_cm_editform",args:{cmid:this.cmid,formdata:data}}])[0].done((function(){self.$emit("saved",formdata)})).fail(_notification.default.exception)}},template:'\n <span class=\'s-edit-mod\'><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-cm-\'+cmid"\n @shown="onShown"\n @ok="onSave"\n :title="title"\n :ok-title="text.save$core"\n ><div :class="\'s-edit-mod-form \'+ (genericonly?\'genericonly\':\'\')" ref="content"\n ><div class="d-flex justify-content-center mb-3"><b-spinner variant="primary"></b-spinner></div\n ></div\n ></b-modal>\n </span>\n '})}};return _exports.default=_default,_exports.default})); define("local_treestudyplan/modedit-modal",["exports","core/fragment","./util/string-helper","core/ajax","core/notification","core/templates"],(function(_exports,_fragment,_stringHelper,_ajax,_notification,_templates){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};var _default={install:function(Vue){var strings=(0,_stringHelper.load_strings)({editmod:{save$core:"save$core",cancel$core:"cancel$core"}});Vue.component("s-edit-mod",{props:{cmid:{type:Number},coursectxid:{type:Number},title:{type:String,default:""},genericonly:{type:Boolean,default:!1}},data:function(){return{content:"",text:strings.editmod}},computed:{},methods:{openForm:function(){this.$refs.editormodal.show()},onShown:function(){var self=this,params={cmid:this.cmid};console.info("Loading form"),(0,_fragment.loadFragment)("local_treestudyplan","mod_edit_form",this.coursectxid,params).then((function(html,js){(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_cm_editform",args:{cmid:this.cmid,formdata:data}}])[0].done((function(){self.$emit("saved",formdata)})).fail(_notification.default.exception)}},template:'\n <span class=\'s-edit-mod\'><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-cm-\'+cmid"\n @shown="onShown"\n @ok="onSave"\n :title="title"\n :ok-title="text.save$core"\n ><div :class="\'s-edit-mod-form \'+ (genericonly?\'genericonly\':\'\')" ref="content"\n ><div class="d-flex justify-content-center mb-3"><b-spinner variant="primary"></b-spinner></div\n ></div\n ></b-modal>\n </span>\n '})}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=modedit-modal.min.js.map //# sourceMappingURL=modedit-modal.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"page-myreport.min.js","sources":["../src/page-myreport.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint no-console: \"off\" */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n// You can call it anything you like\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport RVComponents from './report-viewer-components';\nVue.use(RVComponents);\nimport TSComponents from './treestudyplan-components';\n\nimport Debugger from './util/debugger';\n\nimport {ProcessStudyplans} from './studyplan-processor';\n\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\nlet debug = new Debugger(\"treestudyplan-report\");\n\n/**\n * Initialize the Page\n * @param {string} type Type of page to show\n * @param {Object} arg Arguments passed\n */\n export function init(type=\"myreport\",arg) {\n let app = new Vue({\n el: '#root',\n data: {\n \"studyplans\": [],\n },\n mounted() {\n let call_method;\n let call_args;\n if(type == \"invited\"){\n call_method = 'local_treestudyplan_get_invited_studyplan';\n call_args = {\"invitekey\": arg};\n }\n else if(type == \"other\"){\n call_method = 'local_treestudyplan_get_user_studyplans';\n call_args = {\"userid\": arg};\n }\n else if(type == \"teaching\"){\n call_method = 'local_treestudyplan_get_teaching_studyplans';\n call_args = {};\n }\n else{\n call_method = 'local_treestudyplan_get_own_studyplan';\n call_args = {};\n }\n call([{\n methodname: call_method,\n args: call_args\n }])[0].done(function(response){\n debug.info(\"Studyplans:\",response);\n const timingval = { future: 0, present: 1, past: 2, };\n response.sort((a,b) => {\n const timinga = TSComponents.studyplanTiming(a);\n const timingb = TSComponents.studyplanTiming(b);\n\n let t = timingval[timinga] - timingval[timingb];\n if(t == 0){\n // sort by start date if timing is equal\n t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime();\n \n if (t == 0) {\n // sort by name if timing is equal\n t = a.name.localeCompare(b.name);\n }\n }\n return t;\n });\n app.studyplans = ProcessStudyplans(response);\n }).fail(notification.exception); \n \n },\n\n methods: {\n \n },\n });\n\n}\n\n"],"names":["type","arg","app","Vue","el","data","mounted","call_method","call_args","methodname","args","done","response","debug","info","timingval","future","present","past","sort","a","b","timinga","TSComponents","studyplanTiming","timingb","t","Date","startdate","getTime","name","localeCompare","studyplans","fail","notification","exception","methods","use","RVComponents","PortalVue","BootstrapVue","Debugger"],"mappings":"ikBAmCsBA,4DAAK,WAAWC,2CAC9BC,IAAM,IAAIC,aAAI,CACdC,GAAI,QACJC,KAAM,YACY,IAElBC,uBACQC,YACAC,UACO,WAARR,MACCO,YAAc,4CACdC,UAAY,WAAcP,MAEd,SAARD,MACJO,YAAc,0CACdC,UAAY,QAAWP,MAEX,YAARD,MACJO,YAAc,8CACdC,UAAY,KAGZD,YAAc,wCACdC,UAAY,mBAEX,CAAC,CACFC,WAAYF,YACZG,KAAMF,aACN,GAAGG,MAAK,SAASC,UACjBC,MAAMC,KAAK,cAAcF,cACnBG,UAAY,CAAEC,OAAQ,EAAGC,QAAS,EAAGC,KAAM,GACjDN,SAASO,MAAK,SAACC,EAAEC,OACPC,QAAUC,iCAAaC,gBAAgBJ,GACvCK,QAAUF,iCAAaC,gBAAgBH,GAEzCK,EAAIX,UAAUO,SAAWP,UAAUU,gBAC/B,GAALC,GAIU,IAFTA,EAAI,IAAIC,KAAKN,EAAEO,WAAWC,UAAY,IAAIF,KAAKP,EAAEQ,WAAWC,aAIxDH,EAAIN,EAAEU,KAAKC,cAAcV,EAAES,OAG5BJ,KAEXxB,IAAI8B,YAAa,yCAAkBpB,aACpCqB,KAAKC,sBAAaC,YAIzBC,QAAS,yYAxEbC,IAAIC,8CASJD,IAAIE,iCAEJF,IAAIG,2BAEJ3B,MAAQ,IAAI4B,kBAAS"} {"version":3,"file":"page-myreport.min.js","sources":["../src/page-myreport.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint no-console: \"off\" */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n// You can call it anything you like\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport RVComponents from './report-viewer-components';\nVue.use(RVComponents);\nimport TSComponents from './treestudyplan-components';\n\nimport Debugger from './util/debugger';\n\nimport {ProcessStudyplans} from './studyplan-processor';\n\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\nlet debug = new Debugger(\"treestudyplan-report\");\n\n/**\n * Initialize the Page\n * @param {string} type Type of page to show\n * @param {Object} arg Arguments passed\n */\n export function init(type=\"myreport\",arg) {\n let app = new Vue({\n el: '#root',\n data: {\n \"studyplans\": [],\n },\n mounted() {\n let call_method;\n let call_args;\n if(type == \"invited\"){\n call_method = 'local_treestudyplan_get_invited_studyplan';\n call_args = {\"invitekey\": arg};\n }\n else if(type == \"other\"){\n call_method = 'local_treestudyplan_get_user_studyplans';\n call_args = {\"userid\": arg};\n }\n else if(type == \"teaching\"){\n call_method = 'local_treestudyplan_get_teaching_studyplans';\n call_args = {};\n }\n else{\n call_method = 'local_treestudyplan_get_own_studyplan';\n call_args = {};\n }\n call([{\n methodname: call_method,\n args: call_args\n }])[0].done(function(response){\n debug.info(\"Studyplans:\",response);\n const timingval = { future: 0, present: 1, past: 2, };\n response.sort((a,b) => {\n const timinga = TSComponents.studyplanTiming(a);\n const timingb = TSComponents.studyplanTiming(b);\n\n let t = timingval[timinga] - timingval[timingb];\n if(t == 0){\n // sort by start date if timing is equal\n t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime();\n\n if (t == 0) {\n // sort by name if timing is equal\n t = a.name.localeCompare(b.name);\n }\n }\n return t;\n });\n app.studyplans = ProcessStudyplans(response);\n }).fail(notification.exception);\n\n },\n\n methods: {\n\n },\n });\n\n}\n\n"],"names":["type","arg","app","Vue","el","data","mounted","call_method","call_args","methodname","args","done","response","debug","info","timingval","future","present","past","sort","a","b","timinga","TSComponents","studyplanTiming","timingb","t","Date","startdate","getTime","name","localeCompare","studyplans","fail","notification","exception","methods","use","RVComponents","PortalVue","BootstrapVue","Debugger"],"mappings":"ikBAmCsBA,4DAAK,WAAWC,2CAC9BC,IAAM,IAAIC,aAAI,CACdC,GAAI,QACJC,KAAM,YACY,IAElBC,uBACQC,YACAC,UACO,WAARR,MACCO,YAAc,4CACdC,UAAY,WAAcP,MAEd,SAARD,MACJO,YAAc,0CACdC,UAAY,QAAWP,MAEX,YAARD,MACJO,YAAc,8CACdC,UAAY,KAGZD,YAAc,wCACdC,UAAY,mBAEX,CAAC,CACFC,WAAYF,YACZG,KAAMF,aACN,GAAGG,MAAK,SAASC,UACjBC,MAAMC,KAAK,cAAcF,cACnBG,UAAY,CAAEC,OAAQ,EAAGC,QAAS,EAAGC,KAAM,GACjDN,SAASO,MAAK,SAACC,EAAEC,OACPC,QAAUC,iCAAaC,gBAAgBJ,GACvCK,QAAUF,iCAAaC,gBAAgBH,GAEzCK,EAAIX,UAAUO,SAAWP,UAAUU,gBAC/B,GAALC,GAIU,IAFTA,EAAI,IAAIC,KAAKN,EAAEO,WAAWC,UAAY,IAAIF,KAAKP,EAAEQ,WAAWC,aAIxDH,EAAIN,EAAEU,KAAKC,cAAcV,EAAES,OAG5BJ,KAEXxB,IAAI8B,YAAa,yCAAkBpB,aACpCqB,KAAKC,sBAAaC,YAIzBC,QAAS,yYAxEbC,IAAIC,8CASJD,IAAIE,iCAEJF,IAAIG,2BAEJ3B,MAAQ,IAAI4B,kBAAS"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
define("local_treestudyplan/treestudyplan-components",["exports","./util/string-helper","./util/date-helper"],(function(_exports,_stringHelper,_dateHelper){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={studyplanTiming:function(a){var now=(new Date).getTime(),timing="future";return new Date(a.startdate).getTime()<now&&(timing=a.enddate&&now>new Date(a.enddate).getTime()?"past":"present"),timing},install:function(Vue){var strings=(0,_stringHelper.load_strings)({studyplancard:{open:"open",noenddate:"noenddate",idnumber:"studyplan_idnumber"}}),ItemEventBus=new Vue;Vue.component("s-studyplan-card",{props:{value:{type:Object},open:{type:Boolean}},data:function(){return{text:strings.studyplancard}},computed:{timing:function(){var now=(new Date).getTime(),startdate=new Date(this.value.pages[0].startdate).getTime(),enddate=new Date(this.value.pages[0].enddate).getTime(),timing="future";return startdate<now&&(timing=this.value.pages[0].enddate&&now>enddate?"past":"present"),timing},startdate:function(){return(0,_dateHelper.format_date)(this.value.pages[0].startdate)},enddate:function(){return this.value.pages[0].enddate?(0,_dateHelper.format_date)(this.value.pages[0].enddate):this.text.noenddate}},methods:{onOpenClick:function(e){this.$emit("open",e)}},template:"\n <b-card \n :class=\"'s-studyplan-card timing-' + timing\"\n >\n <template #header></template>\n <b-card-title>\n <a v-if='open' href='#' @click.prevent='onOpenClick($event)'>{{value.name}}</a>\n <template v-else>{{value.name}}</template>\n <slot name='title'></slot>\n </b-card-title>\n <div class='s-studyplan-card-idnumber' v-if='value.idnumber'><i>{{ text.idnumber}}:</i> {{ value.idnumber }}</div>\n <div class='s-studyplan-card-description' v-if='value.description'>{{ value.description }}</div>\n <slot></slot>\n <template #footer>\n <span :class=\"'t-timing-'+timing\" v-html=\"startdate + ' - '+ enddate\"></span>\n <span class=\"s-studyplan-card-buttons\">\n <slot name='footer'></slot>\n <b-button style=\"float:right;\" v-if='open' variant='primary' \n @click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>\n </span>\n </template>\n </b-card>\n "}),Vue.component("s-studyline-header-heading",{props:{},data:function(){return{layerHeights:{}}},created:function(){ItemEventBus.$on("headerHeightChange",this.onHeaderHeightChange)},computed:{},methods:{onHeaderHeightChange:function(newheight){this.$refs.main&&(this.$refs.main.style.height="".concat(newheight,"px"))}},template:'\n <div class="s-studyline-header-heading" ref="main"></div>\n '}),Vue.component("s-studyline-header-period",{props:{value:{type:Object}},mounted:function(){var self=this;1==self.value.period&&(self.resizeListener=new ResizeObserver((function(){if(self.$refs.main){var size=self.$refs.main.getBoundingClientRect();ItemEventBus.$emit("headerHeightChange",size.height)}})).observe(self.$refs.main))},unmounted:function(){this.resizeListener&&this.resizeListener.disconnect()},computed:{startdate:function(){return(0,_dateHelper.format_date)(this.value.startdate)},enddate:function(){return(0,_dateHelper.format_date)(this.value.enddate)},current:function(){if(this.value&&this.value.startdate&&this.value.enddate){var now=new Date,pstart=new Date(this.value.startdate),pend=new Date(this.value.enddate);return now>=pstart&&now<pend}return!1}},data:function(){return{}},template:'\n <div :class="\'s-studyline-header-period \' + (current?\'current \':\' \')" ref="main"\n ><p><abbr :id="\'s-period-\'+value.id" :title="value.fullname">{{ value.shortname }}</abbr>\n <b-tooltip\n :target="\'s-period-\'+value.id" triggers="hover"\n >{{ value.fullname }}<br>\n <span class="s-studyline-header-period-datespan">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </span>\n </b-tooltip>\n <slot></slot\n ><p class="s-studyline-header-period-datespan small">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </p>\n </div>\n '})}};return _exports.default=_default,_exports.default})); define("local_treestudyplan/treestudyplan-components",["exports","./util/string-helper","./util/date-helper"],(function(_exports,_stringHelper,_dateHelper){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={studyplanTiming:function(a){var now=(new Date).getTime(),timing="future";return new Date(a.startdate).getTime()<now&&(timing=a.enddate&&now>new Date(a.enddate).getTime()?"past":"present"),timing},install:function(Vue){var strings=(0,_stringHelper.load_strings)({studyplancard:{open:"open",noenddate:"noenddate",idnumber:"studyplan_idnumber"}}),ItemEventBus=new Vue;Vue.component("s-studyplan-card",{props:{value:{type:Object},open:{type:Boolean}},data:function(){return{text:strings.studyplancard}},computed:{timing:function(){var now=(new Date).getTime(),startdate=new Date(this.value.pages[0].startdate).getTime(),enddate=new Date(this.value.pages[0].enddate).getTime(),timing="future";return startdate<now&&(timing=this.value.pages[0].enddate&&now>enddate?"past":"present"),timing},startdate:function(){return(0,_dateHelper.format_date)(this.value.pages[0].startdate)},enddate:function(){return this.value.pages[0].enddate?(0,_dateHelper.format_date)(this.value.pages[0].enddate):this.text.noenddate}},methods:{onOpenClick:function(e){this.$emit("open",e)}},template:"\n <b-card\n :class=\"'s-studyplan-card timing-' + timing\"\n >\n <template #header></template>\n <b-card-title>\n <a v-if='open' href='#' @click.prevent='onOpenClick($event)'>{{value.name}}</a>\n <template v-else>{{value.name}}</template>\n <slot name='title'></slot>\n </b-card-title>\n <div class='s-studyplan-card-idnumber' v-if='value.idnumber'><i>{{ text.idnumber}}:</i> {{ value.idnumber }}</div>\n <div class='s-studyplan-card-description' v-if='value.description'>{{ value.description }}</div>\n <slot></slot>\n <template #footer>\n <span :class=\"'t-timing-'+timing\" v-html=\"startdate + ' - '+ enddate\"></span>\n <span class=\"s-studyplan-card-buttons\">\n <slot name='footer'></slot>\n <b-button style=\"float:right;\" v-if='open' variant='primary'\n @click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>\n </span>\n </template>\n </b-card>\n "}),Vue.component("s-studyline-header-heading",{props:{},data:function(){return{layerHeights:{}}},created:function(){ItemEventBus.$on("headerHeightChange",this.onHeaderHeightChange)},computed:{},methods:{onHeaderHeightChange:function(newheight){this.$refs.main&&(this.$refs.main.style.height="".concat(newheight,"px"))}},template:'\n <div class="s-studyline-header-heading" ref="main"></div>\n '}),Vue.component("s-studyline-header-period",{props:{value:{type:Object}},mounted:function(){var self=this;1==self.value.period&&(self.resizeListener=new ResizeObserver((function(){if(self.$refs.main){var size=self.$refs.main.getBoundingClientRect();ItemEventBus.$emit("headerHeightChange",size.height)}})).observe(self.$refs.main))},unmounted:function(){this.resizeListener&&this.resizeListener.disconnect()},computed:{startdate:function(){return(0,_dateHelper.format_date)(this.value.startdate)},enddate:function(){return(0,_dateHelper.format_date)(this.value.enddate)},current:function(){if(this.value&&this.value.startdate&&this.value.enddate){var now=new Date,pstart=new Date(this.value.startdate),pend=new Date(this.value.enddate);return now>=pstart&&now<pend}return!1}},data:function(){return{}},template:'\n <div :class="\'s-studyline-header-period \' + (current?\'current \':\' \')" ref="main"\n ><p><abbr :id="\'s-period-\'+value.id" :title="value.fullname">{{ value.shortname }}</abbr>\n <b-tooltip\n :target="\'s-period-\'+value.id" triggers="hover"\n >{{ value.fullname }}<br>\n <span class="s-studyline-header-period-datespan">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </span>\n </b-tooltip>\n <slot></slot\n ><p class="s-studyline-header-period-datespan small">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </p>\n </div>\n '})}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=treestudyplan-components.min.js.map //# sourceMappingURL=treestudyplan-components.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ That's why the plain bootstrap.js file is used
2. Copy node_modules/bootstrap-vue/dist/bootstrap-vue.js to amd/src/bootstrap-vue 2. Copy node_modules/bootstrap-vue/dist/bootstrap-vue.js to amd/src/bootstrap-vue
Copy node_modules/bootstrap-vue/dist/bootstrap-vue.min.css to css/bootstrap-vue Copy node_modules/bootstrap-vue/dist/bootstrap-vue.min.css to css/bootstrap-vue
3. Change require("vue"...) and define("vue"...) in the lines 10 - 16 to 3. Change require("vue"...) and define("vue"...) in the lines 10 - 16 to
require("../vue/vue"...) and define("../vue/vue"...) require("../vue/vue"...) and define("../vue/vue"...)

View File

@ -1,10 +1,10 @@
/* eslint-disable */ /* eslint-disable */
/* /*
Compiled from ts into js by PMKuipers @ 2023) from source Compiled from ts into js by PMKuipers @ 2023) from source
at https://github.com/alexsasharegan/vue-functional-data-merge at https://github.com/alexsasharegan/vue-functional-data-merge
Below is copy of licens in original project Below is copy of licens in original project
*/ */
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2017 Alex Regan Copyright (c) 2017 Alex Regan

View File

@ -30,16 +30,16 @@ export function init() {
intRx = /\d/, intRx = /\d/,
integerChange = (event) => { integerChange = (event) => {
if ( (event.key.length > 1) || intRx.test(event.key) if ( (event.key.length > 1) || intRx.test(event.key)
) { ) {
return; return;
} }
event.preventDefault(); event.preventDefault();
}; };
for (let input of document.querySelectorAll( 'input[type="number"][step="1"][min="0"]' )){ for (let input of document.querySelectorAll( 'input[type="number"][step="1"][min="0"]' )){
input.addEventListener("keydown", integerChange); input.addEventListener("keydown", integerChange);
} }
} }
{ const { const
@ -48,19 +48,19 @@ export function init() {
floatChange = (event) => { floatChange = (event) => {
if ( (event.key.length > 1) || ( (event.key === ".") && (!event.currentTarget.value.match(decimal)) ) if ( (event.key.length > 1) || ( (event.key === ".") && (!event.currentTarget.value.match(decimal)) )
|| intRx.test(event.key) || intRx.test(event.key)
) { ) {
return; return;
} }
event.preventDefault(); event.preventDefault();
}; };
for (let input of document.querySelectorAll( 'input[type="number"][min="0"]:not([step])' )){ for (let input of document.querySelectorAll( 'input[type="number"][min="0"]:not([step])' )){
input.addEventListener("keydown", floatChange); input.addEventListener("keydown", floatChange);
} }
for (let input of document.querySelectorAll( 'input[type="text"].float' )){ for (let input of document.querySelectorAll( 'input[type="text"].float' )){
input.addEventListener("keydown", floatChange); input.addEventListener("keydown", floatChange);
} }
} }

View File

@ -59,12 +59,12 @@ export default {
loadFragment('local_treestudyplan', 'mod_edit_form', this.coursectxid, params).then((html,js) =>{ loadFragment('local_treestudyplan', 'mod_edit_form', this.coursectxid, params).then((html,js) =>{
replaceNodeContents(self.$refs["content"], html, js); replaceNodeContents(self.$refs["content"], html, js);
}).catch(notification.exception); }).catch(notification.exception);
}, },
onSave(){ onSave(){
const self = this; const self = this;
let form = this.$refs["content"].getElementsByTagName("form")[0]; let form = this.$refs["content"].getElementsByTagName("form")[0];
// markFormSubmitted(form); // Moodle 4.00+ only // markFormSubmitted(form); // Moodle 4.00+ only
// We call this, so other modules can update the form with the latest state. // We call this, so other modules can update the form with the latest state.
form.dispatchEvent(new Event("save-form-state")); form.dispatchEvent(new Event("save-form-state"));
@ -88,7 +88,7 @@ export default {
}, },
template: ` template: `
<span class='s-edit-mod'><a href='#' @click.prevent="openForm"><slot><i class="fa fa-cog"></i></slot></a> <span class='s-edit-mod'><a href='#' @click.prevent="openForm"><slot><i class="fa fa-cog"></i></slot></a>
<b-modal <b-modal
ref="editormodal" ref="editormodal"
scrollable scrollable
centered centered

View File

@ -52,7 +52,7 @@ let strings = load_strings({
*/ */
export function init(contextid,categoryid) { export function init(contextid,categoryid) {
// Make sure the id's are numeric and integer // Make sure the id's are numeric and integer
if(undefined === contextid || !Number.isInteger(Number(contextid)) || contextid < 1 ){ contextid = 1;} if(undefined === contextid || !Number.isInteger(Number(contextid)) || contextid < 1 ){ contextid = 1;}
else { contextid = Number(contextid);} // ensure a numeric value instead of string else { contextid = Number(contextid);} // ensure a numeric value instead of string
if(undefined === categoryid || !Number.isInteger(Number(categoryid))){ categoryid = 0;} if(undefined === categoryid || !Number.isInteger(Number(categoryid))){ categoryid = 0;}
else { categoryid = Number(categoryid);} // ensure a numeric value instead of string else { categoryid = Number(categoryid);} // ensure a numeric value instead of string
@ -92,7 +92,7 @@ export function init(contextid,categoryid) {
}, },
created() { created() {
this.$root.$on('studyplanRemoved',(studyplan)=>{ this.$root.$on('studyplanRemoved',(studyplan)=>{
if(app.activestudyplan == studyplan){ if(app.activestudyplan == studyplan){
app.activestudyplan = null; app.activestudyplan = null;
} }
@ -125,14 +125,14 @@ export function init(contextid,categoryid) {
if(t == 0){ if(t == 0){
// sort by start date if timing is equal // sort by start date if timing is equal
t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime(); t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime();
if (t == 0) { if (t == 0) {
// sort by name if timing is equal // sort by name if timing is equal
t = a.name.localeCompare(b.name); t = a.name.localeCompare(b.name);
} }
} }
return t; return t;
}); });
app.studyplans = response; app.studyplans = response;
// load studyplan from hash if applicable // load studyplan from hash if applicable
@ -151,19 +151,19 @@ export function init(contextid,categoryid) {
args: {} args: {}
}])[0].done(function(response){ }])[0].done(function(response){
app.badges = response; app.badges = response;
}).fail(notification.exception); }).fail(notification.exception);
call([{ call([{
methodname: 'local_treestudyplan_map_categories', methodname: 'local_treestudyplan_map_categories',
args: {root_id: categoryid} args: {root_id: categoryid}
}])[0].done(function(response){ }])[0].done(function(response){
app.courses = response; app.courses = response;
}).fail(notification.exception); }).fail(notification.exception);
call([{ call([{
methodname: 'local_treestudyplan_list_used_categories', methodname: 'local_treestudyplan_list_used_categories',
args: { operation: 'edit'} args: { operation: 'edit'}
}])[0].done(function(response){ }])[0].done(function(response){
app.usedcontexts = response; app.usedcontexts = response;
}).fail(notification.exception); }).fail(notification.exception);
}, },
computed: { computed: {
@ -191,7 +191,7 @@ export function init(contextid,categoryid) {
closeStudyplan() { closeStudyplan() {
app.activestudyplan = null; app.activestudyplan = null;
window.location.hash = ''; window.location.hash = '';
}, },
movedStudyplan(plan,from,to) { movedStudyplan(plan,from,to) {
// reload the page in the new context (needed, since a number of links are not reactive in the page) // reload the page in the new context (needed, since a number of links are not reactive in the page)
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
@ -208,7 +208,7 @@ export function init(contextid,categoryid) {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
params.set('categoryid', ctx.id); params.set('categoryid', ctx.id);
window.location.search = params.toString(); window.location.search = params.toString();
}, },
selectStudyplan(studyplan){ selectStudyplan(studyplan){
// fetch studyplan // fetch studyplan
@ -242,8 +242,8 @@ export function init(contextid,categoryid) {
} else { } else {
debug.error("Import failed: ",response.msg); debug.error("Import failed: ",response.msg);
} }
}).fail(notification.exception); }).fail(notification.exception);
}, "application/json"); }, "application/json");
}, },
export_plan(plan,format){ export_plan(plan,format){
@ -258,9 +258,9 @@ export function init(contextid,categoryid) {
format: format format: format
}, },
}])[0].done(function(response){ }])[0].done(function(response){
download(plan.shortname+".json",response.content,response.format); download(plan.shortname+".json",response.content,response.format);
}).fail(notification.exception); }).fail(notification.exception);
}, },
toggletoolbox(event) { toggletoolbox(event) {
debug.info(event); debug.info(event);

View File

@ -72,7 +72,7 @@ let debug = new Debugger("treestudyplan-report");
if(t == 0){ if(t == 0){
// sort by start date if timing is equal // sort by start date if timing is equal
t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime(); t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime();
if (t == 0) { if (t == 0) {
// sort by name if timing is equal // sort by name if timing is equal
t = a.name.localeCompare(b.name); t = a.name.localeCompare(b.name);
@ -81,12 +81,12 @@ let debug = new Debugger("treestudyplan-report");
return t; return t;
}); });
app.studyplans = ProcessStudyplans(response); app.studyplans = ProcessStudyplans(response);
}).fail(notification.exception); }).fail(notification.exception);
}, },
methods: { methods: {
}, },
}); });

View File

@ -42,7 +42,7 @@ let strings = load_strings({
*/ */
export function init(contextid,categoryid) { export function init(contextid,categoryid) {
// Make sure the id's are numeric and integer // Make sure the id's are numeric and integer
if(undefined === contextid || !Number.isInteger(Number(contextid)) || contextid < 1 ){ contextid = 1;} if(undefined === contextid || !Number.isInteger(Number(contextid)) || contextid < 1 ){ contextid = 1;}
else { contextid = Number(contextid);} // ensure a numeric value instead of string else { contextid = Number(contextid);} // ensure a numeric value instead of string
if(undefined === categoryid || !Number.isInteger(Number(categoryid))){ categoryid = 0;} if(undefined === categoryid || !Number.isInteger(Number(categoryid))){ categoryid = 0;}
else { categoryid = Number(categoryid);} // ensure a numeric value instead of string else { categoryid = Number(categoryid);} // ensure a numeric value instead of string

View File

@ -1,13 +1,13 @@
/* eslint-disable */ /* eslint-disable */
/*! /*!
* portal-vue © Thorsten Lünborg, 2019 * portal-vue © Thorsten Lünborg, 2019
* *
* Version: 2.1.7 * Version: 2.1.7
* *
* LICENCE: MIT * LICENCE: MIT
* *
* https://github.com/linusborg/portal-vue * https://github.com/linusborg/portal-vue
* *
*/ */
/* Modification by PMKuipers to handle relative loading of vue */ /* Modification by PMKuipers to handle relative loading of vue */

View File

@ -118,9 +118,9 @@ export default {
select_grades: "select_grades", select_grades: "select_grades",
coursetiming_past: "coursetiming_past", coursetiming_past: "coursetiming_past",
coursetiming_present: "coursetiming_present", coursetiming_present: "coursetiming_present",
coursetiming_future: "coursetiming_future", coursetiming_future: "coursetiming_future",
grade_include: "grade_include", grade_include: "grade_include",
grade_require: "grade_require", grade_require: "grade_require",
required_goal: "required_goal", required_goal: "required_goal",
student_from_plan_enrolled: "student_from_plan_enrolled", student_from_plan_enrolled: "student_from_plan_enrolled",
students_from_plan_enrolled: "students_from_plan_enrolled", students_from_plan_enrolled: "students_from_plan_enrolled",
@ -197,7 +197,7 @@ export default {
return 50 - (50*this.stroke); return 50 - (50*this.stroke);
}, },
arcpath() { arcpath() {
let fraction = 0; let fraction = 0;
const r = 50 - (50*this.stroke); const r = 50 - (50*this.stroke);
if(this.max - this.min != 0){ if(this.max - this.min != 0){
fraction = (this.value - this.min)/(this.max - this.min); fraction = (this.value - this.min)/(this.max - this.min);
@ -214,17 +214,17 @@ export default {
<svg width="1em" height="1em" viewBox="0 0 100 100" <svg width="1em" height="1em" viewBox="0 0 100 100"
style="position: absolute;top: 0;left: 0;"> style="position: absolute;top: 0;left: 0;">
<title>{{title}}</title> <title>{{title}}</title>
<circle v-if="fraction >= 1.0" cx="50" cy="50" :r="radius" <circle v-if="fraction >= 1.0" cx="50" cy="50" :r="radius"
:style="'opacity: 1; stroke-width: '+ (stroke*100)+'; stroke: currentcolor; fill: none;'"/> :style="'opacity: 1; stroke-width: '+ (stroke*100)+'; stroke: currentcolor; fill: none;'"/>
<g v-else> <g v-else>
<circle cx="50" cy="50" :r="radius" <circle cx="50" cy="50" :r="radius"
:style="'opacity: ' + bgopacity + ';stroke-width: '+ (stroke*100)+'; stroke: currentcolor; fill: none;'"/> :style="'opacity: ' + bgopacity + ';stroke-width: '+ (stroke*100)+'; stroke: currentcolor; fill: none;'"/>
<path :d="arcpath" <path :d="arcpath"
:style="'stroke-width: ' + (stroke*100) +'; stroke: currentcolor; fill: none;'"/> :style="'stroke-width: ' + (stroke*100) +'; stroke: currentcolor; fill: none;'"/>
</g> </g>
</svg> </svg>
<i v-if='icon' :class="'fa fa-'+icon" <i v-if='icon' :class="'fa fa-'+icon"
style=" position: absolute; top: 0.42em; left: 0.43em; text-align:center; display: inline-block; style=" position: absolute; top: 0.42em; left: 0.43em; text-align:center; display: inline-block;
width:1em; font-size: 0.55em; " width:1em; font-size: 0.55em; "
></i> ></i>
</div> </div>
@ -268,9 +268,9 @@ export default {
}, },
}, },
template: ` template: `
<div class='t-studyplan-container r-report-tabs'> <div class='t-studyplan-container r-report-tabs'>
<b-list-group horizontal> <b-list-group horizontal>
<b-list-group-item <b-list-group-item
v-for="(studyplan,planindex) in value" v-for="(studyplan,planindex) in value"
:key="studyplan.id" :key="studyplan.id"
:active="displayedstudyplan && studyplan.id == displayedstudyplan.id" :active="displayedstudyplan && studyplan.id == displayedstudyplan.id"
@ -364,7 +364,7 @@ export default {
} }
} }
} }
return show; return show;
} }
}, },
@ -386,7 +386,7 @@ export default {
v-model="page.studylines[lineindex]" v-model="page.studylines[lineindex]"
:layers='countLineLayers(line)+1' :layers='countLineLayers(line)+1'
:class=" 't-studyline' + ((lineindex%2==0)?' odd ' :' even ' ) :class=" 't-studyline' + ((lineindex%2==0)?' odd ' :' even ' )
+ ((lineindex==0)?' first ':' ') + ((lineindex==0)?' first ':' ')
+ ((lineindex==page.studylines.length-1)?' last ':' ')" + ((lineindex==page.studylines.length-1)?' last ':' ')"
></r-studyline-heading ></r-studyline-heading
></div> ></div>
@ -395,24 +395,24 @@ export default {
<div class="r-studyplan-timeline" :style="columns_stylerule"> <div class="r-studyplan-timeline" :style="columns_stylerule">
<!-- add period information --> <!-- add period information -->
<template v-for="(n,index) in (page.periods+1)"> <template v-for="(n,index) in (page.periods+1)">
<s-studyline-header-period <s-studyline-header-period
v-if="index > 0" v-if="index > 0"
v-model="page.perioddesc[index-1]" v-model="page.perioddesc[index-1]"
></s-studyline-header-period> ></s-studyline-header-period>
<div class="s-studyline-header-filter"></div> <div class="s-studyline-header-filter"></div>
</template> </template>
<!-- Line by line add the items --> <!-- Line by line add the items -->
<!-- The grid layout handles putting it in rows and columns --> <!-- The grid layout handles putting it in rows and columns -->
<template v-for="(line,lineindex) in page.studylines" <template v-for="(line,lineindex) in page.studylines"
><template v-for="(layernr,layeridx) in countLineLayers(line)" ><template v-for="(layernr,layeridx) in countLineLayers(line)"
><template v-for="(n,index) in (page.periods+1)" ><template v-for="(n,index) in (page.periods+1)"
><r-studyline-slot ><r-studyline-slot
v-if="index > 0 && showslot(line, index, layeridx, 'gradable')" v-if="index > 0 && showslot(line, index, layeridx, 'gradable')"
type='gradable' type='gradable'
v-model="line.slots[index].courses" v-model="line.slots[index].courses"
:key="'c-'+lineindex+'-'+index+'-'+layernr" :key="'c-'+lineindex+'-'+index+'-'+layernr"
:slotindex="index" :slotindex="index"
:line="line" :line="line"
:plan="value" :plan="value"
:page="page" :page="page"
@ -424,13 +424,13 @@ export default {
+ ((lineindex==0 && layernr==1)?' first ':' ') + ((lineindex==0 && layernr==1)?' first ':' ')
+ ((lineindex==page.studylines.length-1)?' last ':' ')" + ((lineindex==page.studylines.length-1)?' last ':' ')"
></r-studyline-slot ></r-studyline-slot
><r-studyline-slot ><r-studyline-slot
v-if="showslot(line, index, layeridx, 'gradable')" v-if="showslot(line, index, layeridx, 'gradable')"
type='filter' type='filter'
v-model="line.slots[index].filters" v-model="line.slots[index].filters"
:teachermode='teachermode' :teachermode='teachermode'
:key="'f-'+lineindex+'-'+index+'-'+layernr" :key="'f-'+lineindex+'-'+index+'-'+layernr"
:slotindex="index" :slotindex="index"
:line="line" :line="line"
:plan="value" :plan="value"
:page="page" :page="page"
@ -475,12 +475,12 @@ export default {
ItemEventBus.$on('lineHeightChange', this.onLineHeightChange); ItemEventBus.$on('lineHeightChange', this.onLineHeightChange);
}, },
computed: { computed: {
}, },
methods: { methods: {
onLineHeightChange(lineid){ onLineHeightChange(lineid){
// All layers for this line have the first slot send an update message on layer height change. // All layers for this line have the first slot send an update message on layer height change.
// When one of those updates is received, record the height and recalculate the total height of the // When one of those updates is received, record the height and recalculate the total height of the
// header // header
if(this.$refs.mainEl && (lineid == this.value.id || lineid === null)){ if(this.$refs.mainEl && (lineid == this.value.id || lineid === null)){
const items = document.querySelectorAll( const items = document.querySelectorAll(
@ -500,7 +500,7 @@ export default {
} }
}, },
template: ` template: `
<div class="r-studyline r-studyline-heading " <div class="r-studyline r-studyline-heading "
:data-studyline="value.id" ref="mainEl" :data-studyline="value.id" ref="mainEl"
><div class="r-studyline-handle" :style="'background-color: ' + value.color"></div> ><div class="r-studyline-handle" :style="'background-color: ' + value.color"></div>
<div class="r-studyline-title"> <div class="r-studyline-title">
@ -530,7 +530,7 @@ export default {
}, },
layer : { layer : {
type: Number, type: Number,
}, },
plan: { plan: {
type: Object, type: Object,
default(){ return null;}, default(){ return null;},
@ -538,7 +538,7 @@ export default {
page: { page: {
type: Object, type: Object,
default(){ return null;}, default(){ return null;},
}, },
guestmode: { guestmode: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -547,7 +547,7 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
period: { period: {
type: Object, type: Object,
default(){ return null;}, default(){ return null;},
} }
@ -598,17 +598,17 @@ export default {
}; };
}, },
methods: { methods: {
}, },
template: ` template: `
<div :class=" 'r-studyline-slot ' + type + ' ' + (current?'current ':' ') <div :class=" 'r-studyline-slot ' + type + ' ' + (current?'current ':' ')
+ 'r-studyline-slot-' + slotindex + ' ' + 'r-studyline-slot-' + slotindex + ' '
+ ((slotindex==0)?'r-studyline-firstcolumn ':' ')" + ((slotindex==0)?'r-studyline-firstcolumn ':' ')"
:data-studyline="line.id" ref="sizeElement" :data-studyline="line.id" ref="sizeElement"
:style='spanCss' :style='spanCss'
><div class="r-slot-item" v-if="item" ><div class="r-slot-item" v-if="item"
><r-item ><r-item
v-model="item" v-model="item"
:plan="plan" :plan="plan"
:guestmode='guestmode' :guestmode='guestmode'
:teachermode='teachermode'></r-item :teachermode='teachermode'></r-item
@ -624,7 +624,7 @@ export default {
value :{ value :{
type: Object, type: Object,
default: function(){ return null;}, default: function(){ return null;},
}, },
plan: { plan: {
type: Object, type: Object,
default(){ return null;} default(){ return null;}
@ -725,7 +725,7 @@ export default {
} }
}, },
created(){ created(){
}, },
mounted(){ mounted(){
// Initialize connection lines when mounting // Initialize connection lines when mounting
@ -752,7 +752,7 @@ export default {
} }
lineinfo.line = undefined; lineinfo.line = undefined;
} }
} }
} }
// Remove resize event listener // Remove resize event listener
window.removeEventListener('resize',this.onWindowResize); window.removeEventListener('resize',this.onWindowResize);
@ -780,7 +780,7 @@ export default {
v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-finish> v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-finish>
<r-item-badge v-if="value.type == 'badge'" <r-item-badge v-if="value.type == 'badge'"
v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-badge> v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-badge>
<r-item-invalid v-if="value.type == 'invalid' && teachermode" <r-item-invalid v-if="value.type == 'invalid' && teachermode"
v-model="value" ></r-item-invalid> v-model="value" ></r-item-invalid>
</div> </div>
`, `,
@ -832,7 +832,7 @@ export default {
teachermode: { teachermode: {
type: Boolean, type: Boolean,
default(){ return false;} default(){ return false;}
}, },
plan: { plan: {
type: Object, type: Object,
default(){ return null;} default(){ return null;}
@ -850,7 +850,7 @@ export default {
enddate(){ enddate(){
if(this.value.course.enddate){ if(this.value.course.enddate){
return format_date(this.value.course.enddate); return format_date(this.value.course.enddate);
} }
else { else {
return this.text.noenddate; return this.text.noenddate;
} }
@ -866,7 +866,7 @@ export default {
case "pending": case "pending":
return "question-circle"; return "question-circle";
case "failed": case "failed":
return "times-circle"; return "times-circle";
case "progress": case "progress":
return "exclamation-circle"; return "exclamation-circle";
case "completed": case "completed":
@ -882,7 +882,7 @@ export default {
default: // case "incomplete" default: // case "incomplete"
return null; return null;
case "failed": case "failed":
return "times"; return "times";
case "progress": case "progress":
return "exclamation"; return "exclamation";
case "completed": case "completed":
@ -899,15 +899,15 @@ export default {
<b-card no-body > <b-card no-body >
<b-row no-gutters> <b-row no-gutters>
<b-col md="1"> <b-col md="1">
<span <span
:title="text['coursetiming_'+value.course.timing]" :title="text['coursetiming_'+value.course.timing]"
v-b-popover.hover.top="startdate+' - '+enddate" v-b-popover.hover.top="startdate+' - '+enddate"
:class="'r-timing-indicator timing-'+value.course.timing"></span> :class="'r-timing-indicator timing-'+value.course.timing"></span>
</b-col> </b-col>
<b-col md="11" class="align-items-center"> <b-col md="11" class="align-items-center">
<b-card-body > <b-card-body >
<template v-if='!value.course.enrolled'> <template v-if='!value.course.enrolled'>
<i v-b-popover.top <i v-b-popover.top
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert" class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
:title="text.student_not_tracked"></i> :title="text.student_not_tracked"></i>
</template> </template>
@ -920,28 +920,28 @@ export default {
:icon='circle_icon(value.completion)' :icon='circle_icon(value.completion)'
:title="text['completion_'+value.completion]" :title="text['completion_'+value.completion]"
></r-progress-circle> ></r-progress-circle>
<i v-else v-b-popover.top <i v-else v-b-popover.top
:class="'r-course-result fa fa-'+completion_icon(value.completion)+ :class="'r-course-result fa fa-'+completion_icon(value.completion)+
' r-completion-'+value.completion" ' r-completion-'+value.completion"
:title="text['completion_'+value.completion]"></i> :title="text['completion_'+value.completion]"></i>
</template> </template>
<template v-else> <template v-else>
<i v-b-popover.top <i v-b-popover.top
:class="'r-course-result fa fa-'+completion_icon(value.completion)+ :class="'r-course-result fa fa-'+completion_icon(value.completion)+
' r-completion-'+value.completion" ' r-completion-'+value.completion"
:title="text['completion_'+value.completion]"></i> :title="text['completion_'+value.completion]"></i>
</template> </template>
<a v-b-modal="'r-item-course-details-'+value.id" <a v-b-modal="'r-item-course-details-'+value.id"
:href="(!guestmode)?('/course/view.php?id='+value.course.id):'#'" :href="(!guestmode)?('/course/view.php?id='+value.course.id):'#'"
@click.prevent.stop='' @click.prevent.stop=''
>{{ value.course.displayname }}</i></a> >{{ value.course.displayname }}</i></a>
</b-card-body> </b-card-body>
</b-col> </b-col>
</b-row> </b-row>
<b-modal <b-modal
:id="'r-item-course-details-'+value.id" :id="'r-item-course-details-'+value.id"
:title="value.course.displayname + ' - ' + value.course.fullname" :title="value.course.displayname + ' - ' + value.course.fullname"
size="lg" size="lg"
ok-only ok-only
centered centered
@ -957,7 +957,7 @@ export default {
<div class="r-completion-detail-header"> <div class="r-completion-detail-header">
<template v-if='!value.course.enrolled'> <template v-if='!value.course.enrolled'>
{{text.not_enrolled}} {{text.not_enrolled}}
<i v-b-popover.top <i v-b-popover.top
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert" class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
:title="text.student_not_tracked"></i> :title="text.student_not_tracked"></i>
</template> </template>
@ -971,7 +971,7 @@ export default {
:class="'r-progress-circle-popup r-completion-'+value.completion" :class="'r-progress-circle-popup r-completion-'+value.completion"
:icon='circle_icon(value.completion)' :icon='circle_icon(value.completion)'
></r-progress-circle ></r-progress-circle
><i v-else v-b-popover.top ><i v-else v-b-popover.top
:class="'fa fa-'+completion_icon(value.completion)+ :class="'fa fa-'+completion_icon(value.completion)+
' r-progress-icon-popup r-completion-'+value.completion" ' r-progress-icon-popup r-completion-'+value.completion"
:title="text['completion_'+value.completion]"></i> :title="text['completion_'+value.completion]"></i>
@ -988,16 +988,16 @@ export default {
</div> </div>
</div> </div>
</template> </template>
<r-item-studentgrades <r-item-studentgrades
v-if='!!value.course.grades && value.course.grades.length > 0' v-if='!!value.course.grades && value.course.grades.length > 0'
v-model='value' v-model='value'
:guestmode='guestmode'></r-item-studentgrades> :guestmode='guestmode'></r-item-studentgrades>
<r-item-studentcompletion <r-item-studentcompletion
v-if='!!value.course.completion' v-if='!!value.course.completion'
v-model='value.course.completion' v-model='value.course.completion'
:course='value.course' :course='value.course'
:guestmode='guestmode'></r-item-studentcompletion> :guestmode='guestmode'></r-item-studentcompletion>
</b-modal> </b-modal>
</b-card></div> </b-card></div>
`, `,
}); });
@ -1038,7 +1038,7 @@ export default {
else { else {
return false; return false;
} }
}, },
}, },
methods: { methods: {
completion_icon(completion) { completion_icon(completion) {
@ -1048,7 +1048,7 @@ export default {
case "pending": case "pending":
return "question-circle"; return "question-circle";
case "failed": case "failed":
return "times-circle"; return "times-circle";
case "progress": case "progress":
return "exclamation-circle"; return "exclamation-circle";
case "completed": case "completed":
@ -1074,14 +1074,14 @@ export default {
<td><span :class="' r-completion-'+g.completion">{{g.grade}}</span></td> <td><span :class="' r-completion-'+g.completion">{{g.grade}}</span></td>
<td><i :class="'fa fa-'+completion_icon(g.completion)+' r-completion-'+g.completion" <td><i :class="'fa fa-'+completion_icon(g.completion)+' r-completion-'+g.completion"
:title="text['completion_'+g.completion]"></i> :title="text['completion_'+g.completion]"></i>
<i v-if='g.pendingsubmission' :title="text['completion_pending']" <i v-if='g.pendingsubmission' :title="text['completion_pending']"
class="r-pendingsubmission fa fa-clock-o"></i></td> class="r-pendingsubmission fa fa-clock-o"></i></td>
<td v-if="g.feedback"> <td v-if="g.feedback">
<a v-b-modal="'r-grade-feedback-'+g.id" <a v-b-modal="'r-grade-feedback-'+g.id"
href="#" href="#"
>{{ text["view_feedback"]}}</a> >{{ text["view_feedback"]}}</a>
<b-modal <b-modal
:id="'r-grade-feedback-'+g.id" :id="'r-grade-feedback-'+g.id"
size="sm" size="sm"
ok-only ok-only
centered centered
@ -1090,7 +1090,7 @@ export default {
<template #modal-header> <template #modal-header>
<h2><i class="fa fa-graduation-cap"></i>{{ value.course.fullname }}</h2><br> <h2><i class="fa fa-graduation-cap"></i>{{ value.course.fullname }}</h2><br>
<span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span>{{g.name}} <span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span>{{g.name}}
</template> </template>
<span v-html="g.feedback"></span> <span v-html="g.feedback"></span>
</b-modal> </b-modal>
</td> </td>
@ -1205,13 +1205,13 @@ export default {
<span v-else v-html='addTargetBlank(ci.details.criteria)'></span> <span v-else v-html='addTargetBlank(ci.details.criteria)'></span>
<a href="#" v-b-tooltip.click.hover.right.html="{ customClass: 'r-tooltip ' + ci.status}" <a href="#" v-b-tooltip.click.hover.right.html="{ customClass: 'r-tooltip ' + ci.status}"
:title="requirementHTML(ci.details.requirement)" :title="requirementHTML(ci.details.requirement)"
class="text-primary"><i v-if="ci.details.requirement" class="text-primary"><i v-if="ci.details.requirement"
class='fa fa-question-circle' class='fa fa-question-circle'
></i></a> ></i></a>
<td <td
><span :class="' r-completion-'+ci.status">{{ci.grade}}</span> ><span :class="' r-completion-'+ci.status">{{ci.grade}}</span>
<span v-if="ci.warning" <span v-if="ci.warning"
><i class="text-primary fa fa-exclamation-triangle" ><i class="text-primary fa fa-exclamation-triangle"
v-b-tooltip.hover.right.click="{ customClass: 'r-tooltip info' }" v-b-tooltip.hover.right.click="{ customClass: 'r-tooltip info' }"
:title="ci.warning" :title="ci.warning"
></i ></i
@ -1219,15 +1219,15 @@ export default {
></td> ></td>
<td><i :class="'fa fa-'+completion_icon(ci.status)+' r-completion-'+ci.status" <td><i :class="'fa fa-'+completion_icon(ci.status)+' r-completion-'+ci.status"
:title="text['completion_'+ci.status]"></i> :title="text['completion_'+ci.status]"></i>
<i v-if='ci.pending' :title="text['completion_pending']" <i v-if='ci.pending' :title="text['completion_pending']"
class="r-pendingsubmission fa fa-clock-o"></i> class="r-pendingsubmission fa fa-clock-o"></i>
</td> </td>
<td v-if="ci.feedback"> <td v-if="ci.feedback">
<a v-b-modal="'r-grade-feedback-'+ci.id" <a v-b-modal="'r-grade-feedback-'+ci.id"
href="#" href="#"
>{{ text["view_feedback"]}}</a> >{{ text["view_feedback"]}}</a>
<b-modal <b-modal
:id="'r-grade-feedback-'+ci.id" :id="'r-grade-feedback-'+ci.id"
size="sm" size="sm"
ok-only ok-only
centered centered
@ -1356,7 +1356,7 @@ export default {
enddate(){ enddate(){
if(this.value.course.enddate){ if(this.value.course.enddate){
return format_date(this.value.course.enddate); return format_date(this.value.course.enddate);
} }
else { else {
return this.text.noenddate; return this.text.noenddate;
} }
@ -1385,7 +1385,7 @@ export default {
} }
else if(Number(grade.grading.graded) > 0) { else if(Number(grade.grading.graded) > 0) {
if(Number(grade.grading.graded) == Number(grade.grading.students)){ if(Number(grade.grading.graded) == Number(grade.grading.students)){
allgraded++; allgraded++;
} else { } else {
graded++; graded++;
} }
@ -1394,7 +1394,7 @@ export default {
unknown = true; unknown = true;
} }
} }
if(ungraded > 0){ if(ungraded > 0){
return 'ungraded'; return 'ungraded';
} else if(unknown) { } else if(unknown) {
@ -1429,14 +1429,14 @@ export default {
<b-card no-body> <b-card no-body>
<div class='d-flex flex-wrap mr-0 ml-0'> <div class='d-flex flex-wrap mr-0 ml-0'>
<div> <div>
<span <span
:title="text['coursetiming_'+value.course.timing]" :title="text['coursetiming_'+value.course.timing]"
v-b-popover.hover.top="startdate+' - '+enddate" v-b-popover.hover.top="startdate+' - '+enddate"
:class="'r-timing-indicator timing-'+value.course.timing"></span> :class="'r-timing-indicator timing-'+value.course.timing"></span>
</div> </div>
<div class="flex-fill align-items-center"> <div class="flex-fill align-items-center">
<b-card-body> <b-card-body>
<a v-b-modal="'r-item-course-details-'+value.id" <a v-b-modal="'r-item-course-details-'+value.id"
:href="(!guestmode)?('/course/view.php?id='+value.course.id):'#'" :href="(!guestmode)?('/course/view.php?id='+value.course.id):'#'"
@click.prevent.stop='' @click.prevent.stop=''
>{{ value.course.displayname }}</i></a> >{{ value.course.displayname }}</i></a>
@ -1447,8 +1447,8 @@ export default {
</div> </div>
<b-modal <b-modal
v-if="true" v-if="true"
:id="'r-item-course-details-'+value.id" :id="'r-item-course-details-'+value.id"
:title="value.course.displayname + ' - ' + value.course.fullname" :title="value.course.displayname + ' - ' + value.course.fullname"
size="lg" size="lg"
ok-only ok-only
centered centered
@ -1463,7 +1463,7 @@ export default {
:useRequiredGrades="useRequiredGrades" :useRequiredGrades="useRequiredGrades"
:plan="plan" :plan="plan"
></r-item-teacher-gradepicker> ></r-item-teacher-gradepicker>
<a v-if='!!value.course.completion && value.course.amteacher' <a v-if='!!value.course.completion && value.course.amteacher'
:href="'/course/completion.php?id='+value.course.id" target="_blank" :href="'/course/completion.php?id='+value.course.id" target="_blank"
:title="text.configure_completion"><i class="fa fa-gear"></i></a> :title="text.configure_completion"><i class="fa fa-gear"></i></a>
</h1> </h1>
@ -1484,18 +1484,18 @@ export default {
</div> </div>
</div> </div>
</template> </template>
<r-item-teachergrades <r-item-teachergrades
v-if='!!value.course.grades && value.course.grades.length > 0' v-if='!!value.course.grades && value.course.grades.length > 0'
v-model='value.course' v-model='value.course'
:useRequiredGrades="useRequiredGrades" :useRequiredGrades="useRequiredGrades"
></r-item-teachergrades> ></r-item-teachergrades>
<r-item-teachercompletion <r-item-teachercompletion
v-if='!!value.course.completion' v-if='!!value.course.completion'
v-model='value.course.completion' v-model='value.course.completion'
:course='value.course' :course='value.course'
></r-item-teachercompletion> ></r-item-teachercompletion>
</b-modal> </b-modal>
</b-card> </b-card>
</div> </div>
`, `,
@ -1526,7 +1526,7 @@ export default {
call([{ call([{
methodname: 'local_treestudyplan_include_grade', methodname: 'local_treestudyplan_include_grade',
args: { 'grade_id': g.id, args: { 'grade_id': g.id,
'item_id': this.value.id, 'item_id': this.value.id,
'include': newValue, 'include': newValue,
'required': g.required, 'required': g.required,
} }
@ -1536,21 +1536,21 @@ export default {
call([{ call([{
methodname: 'local_treestudyplan_include_grade', methodname: 'local_treestudyplan_include_grade',
args: { 'grade_id': g.id, args: { 'grade_id': g.id,
'item_id': this.value.id, 'item_id': this.value.id,
'include': g.selected, 'include': g.selected,
'required': newValue, 'required': newValue,
} }
}])[0].fail(notification.exception); }])[0].fail(notification.exception);
}, },
}, },
template: ` template: `
<a v-if="value.course.canselectgradables" href='#' <a v-if="value.course.canselectgradables" href='#'
v-b-modal="'r-item-course-config-'+value.id" v-b-modal="'r-item-course-config-'+value.id"
@click.prevent.stop='' @click.prevent.stop=''
><i class='fa fa-cog'></i> ><i class='fa fa-cog'></i>
<b-modal v-if='value.course.canselectgradables' <b-modal v-if='value.course.canselectgradables'
:id="'r-item-course-config-'+value.id" :id="'r-item-course-config-'+value.id"
:title="value.course.displayname + ' - ' + value.course.fullname" :title="value.course.displayname + ' - ' + value.course.fullname"
ok-only ok-only
scrollable scrollable
> >
@ -1581,13 +1581,13 @@ export default {
<b-form-checkbox v-if="useRequiredGrades" inline :disabled="!g.selected" <b-form-checkbox v-if="useRequiredGrades" inline :disabled="!g.selected"
@change="requiredChanged($event,g)" v-model="g.required" @change="requiredChanged($event,g)" v-model="g.required"
></b-form-checkbox> ></b-form-checkbox>
<span :title="g.typename" v-html="g.icon"></span><a <span :title="g.typename" v-html="g.icon"></span><a
:href="g.link" target="_blank">{{g.name}}</a> :href="g.link" target="_blank">{{g.name}}</a>
<s-edit-mod <s-edit-mod
:title="value.course.fullname" :title="value.course.fullname"
@saved="(fd) => g.name = fd.get('name')" @saved="(fd) => g.name = fd.get('name')"
v-if="g.cmid > 0" v-if="g.cmid > 0"
:cmid="g.cmid" :cmid="g.cmid"
:coursectxid="value.course.ctxid" :coursectxid="value.course.ctxid"
genericonly></s-edit-mod> genericonly></s-edit-mod>
</li> </li>
@ -1660,7 +1660,7 @@ export default {
if(grade.grading){ if(grade.grading){
if(grade.grading.ungraded){ if(grade.grading.ungraded){
return 'ungraded'; return 'ungraded';
} }
else if(grade.grading.completed_pass || grade.grading.completed || grade.grading.completed_fail) { else if(grade.grading.completed_pass || grade.grading.completed || grade.grading.completed_fail) {
if(Number(grade.grading.completed) + Number(grade.grading.completed_pass) if(Number(grade.grading.completed) + Number(grade.grading.completed_pass)
+ Number(grade.grading.completed_fail) + Number(grade.grading.completed_fail)
@ -1678,7 +1678,7 @@ export default {
return 'unknown'; return 'unknown';
} }
}, },
}, },
template: ` template: `
<div> <div>
@ -1688,17 +1688,17 @@ export default {
><a ><a
:href="g.gradinglink" :href="g.gradinglink"
target="_blank" :title="g.name">{{g.name}}</a> target="_blank" :title="g.name">{{g.name}}</a>
<s-edit-mod <s-edit-mod
:title="value.fullname" :title="value.fullname"
@saved="(fd) => g.name = fd.get('name')" @saved="(fd) => g.name = fd.get('name')"
v-if="g.cmid > 0" v-if="g.cmid > 0"
:cmid="g.cmid" :cmid="g.cmid"
:coursectxid="value.ctxid" :coursectxid="value.ctxid"
genericonly></s-edit-mod> genericonly></s-edit-mod>
<abbr v-if="useRequiredGrades && g.required" :title="text.required_goal" <abbr v-if="useRequiredGrades && g.required" :title="text.required_goal"
:class="'s-required ' + is_grading_needed(g)" :class="'s-required ' + is_grading_needed(g)"
><i class='fa fa-asterisk' ></i ><i class='fa fa-asterisk' ></i
></abbr> ></abbr>
</td> </td>
<td v-if='g.grading' <td v-if='g.grading'
><i :class="'r-course-grading fa fa-'+grading_icon(g)+' r-graded-'+is_grading_needed(g)" ><i :class="'r-course-grading fa fa-'+grading_icon(g)+' r-graded-'+is_grading_needed(g)"
@ -1795,8 +1795,8 @@ export default {
<td><span v-html='ci.details.criteria'></span> <td><span v-html='ci.details.criteria'></span>
<a href="#" v-b-tooltip.click <a href="#" v-b-tooltip.click
:title="ci.details.requirement" :title="ci.details.requirement"
class='text-info'><i v-if="ci.details.requirement" class='text-info'><i v-if="ci.details.requirement"
class='fa fa-question-circle' class='fa fa-question-circle'
></i></a> ></i></a>
</td> </td>
<td> <td>
@ -1805,7 +1805,7 @@ export default {
</tr> </tr>
</template> </template>
<tr><td colspan='2' class='pt-2'> <tr><td colspan='2' class='pt-2'>
<a target="_blank" :href='completionreport'>{{ text.view_completion_report}} <a target="_blank" :href='completionreport'>{{ text.view_completion_report}}
<i class='fa fa-external-link'></i></a></td></tr> <i class='fa fa-external-link'></i></a></td></tr>
</table> </table>
`, `,
@ -2049,7 +2049,7 @@ export default {
return this.arcpath(begin,this.fraction_completed_fail()); return this.arcpath(begin,this.fraction_completed_fail());
}, },
arcpath_incomplete() { arcpath_incomplete() {
const begin = this.fraction_ungraded() const begin = this.fraction_ungraded()
+ this.fraction_completed() + this.fraction_completed()
+ this.fraction_completed_pass() + this.fraction_completed_pass()
+ this.fraction_completed_fail(); + this.fraction_completed_fail();
@ -2106,20 +2106,20 @@ export default {
template: ` template: `
<svg width="1em" height="1em" viewBox="0 0 100 100"> <svg width="1em" height="1em" viewBox="0 0 100 100">
<title>{{title}}</title> <title>{{title}}</title>
<circle cx="50" cy="50" :r="radius" <circle cx="50" cy="50" :r="radius"
:style="'stroke-width: ' + (stroke*100)+'; stroke: #ccc; fill: none;'"/> :style="'stroke-width: ' + (stroke*100)+'; stroke: #ccc; fill: none;'"/>
<path :d="arcpath_ungraded" <path :d="arcpath_ungraded"
:style="'stroke-width: ' + (stroke*100) +'; stroke: var(--warning); fill: none;'"/> :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--warning); fill: none;'"/>
<path :d="arcpath_completed" <path :d="arcpath_completed"
:style="'stroke-width: ' + (stroke*100) +'; stroke: var(--info); fill: none;'"/> :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--info); fill: none;'"/>
<path :d="arcpath_completed_pass" <path :d="arcpath_completed_pass"
:style="'stroke-width: ' + (stroke*100) +'; stroke: var(--success); fill: none;'"/> :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--success); fill: none;'"/>
<path :d="arcpath_completed_fail" <path :d="arcpath_completed_fail"
:style="'stroke-width: ' + (stroke*100) +'; stroke: var(--danger); fill: none;'"/> :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--danger); fill: none;'"/>
<circle v-if="disabled" cx="50" cy="50" :r="radius/2" <circle v-if="disabled" cx="50" cy="50" :r="radius/2"
:style="'fill: var(--dark);'"/> :style="'fill: var(--dark);'"/>
<circle v-else-if="value.ungraded > 0" cx="50" cy="50" :r="radius/2" <circle v-else-if="value.ungraded > 0" cx="50" cy="50" :r="radius/2"
:style="'fill: var(--warning);'"/> :style="'fill: var(--warning);'"/>
</g> </g>
</svg> </svg>
@ -2158,7 +2158,7 @@ export default {
}, },
template: ` template: `
<div :class="'r-item-junction r-item-filter completion-'+completion"> <div :class="'r-item-junction r-item-filter completion-'+completion">
<i v-if="value.completion=='incomplete'" class="fa fa-circle-o"></i> <i v-if="value.completion=='incomplete'" class="fa fa-circle-o"></i>
<i v-else-if="value.completion=='failed'" class="fa fa-times-circle"></i> <i v-else-if="value.completion=='failed'" class="fa fa-times-circle"></i>
<i v-else-if="value.completion=='progress'" class="fa fa-exclamation-circle"></i> <i v-else-if="value.completion=='progress'" class="fa fa-exclamation-circle"></i>
@ -2198,9 +2198,9 @@ export default {
methods: { methods: {
}, },
template: ` template: `
<div :class="'r-item-finish r-item-filter completion-'+completion"> <div :class="'r-item-finish r-item-filter completion-'+completion">
<i class="fa fa-stop-circle"></i> <i class="fa fa-stop-circle"></i>
</div> </div>
`, `,
}); });
@ -2231,9 +2231,9 @@ export default {
return "incomplete"; return "incomplete";
} }
} }
}, },
created(){ created(){
}, },
methods: { methods: {
}, },
@ -2306,19 +2306,19 @@ export default {
}, },
}, },
template: ` template: `
<div :class="'r-item-badge r-item-filter r-completion-'+completion" v-b-tooltip.hover :title="value.badge.name"> <div :class="'r-item-badge r-item-filter r-completion-'+completion" v-b-tooltip.hover :title="value.badge.name">
<a v-b-modal="'r-item-badge-details-'+value.id" <a v-b-modal="'r-item-badge-details-'+value.id"
><svg class="r-badge-backdrop " width='50px' height='50px' viewBox="0 0 100 100"> ><svg class="r-badge-backdrop " width='50px' height='50px' viewBox="0 0 100 100">
<title>{{value.badge.name}}</title> <title>{{value.badge.name}}</title>
<template v-if="teachermode"> <template v-if="teachermode">
<circle cx="50" cy="50" r="44" <circle cx="50" cy="50" r="44"
style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/> style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/>
<path :d="arcpath_issued" <path :d="arcpath_issued"
:style="'stroke-width: 8; stroke: var(--info); fill: none;'"/> :style="'stroke-width: 8; stroke: var(--info); fill: none;'"/>
</template> </template>
<circle v-else-if="value.badge.issued" cx="50" cy="50" r="46" <circle v-else-if="value.badge.issued" cx="50" cy="50" r="46"
style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/> style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/>
<circle v-else cx="50" cy="50" r="46" <circle v-else cx="50" cy="50" r="46"
stroke-dasharray="6 9" stroke-dasharray="6 9"
style="stroke: #999; stroke-width: 6; fill: #ddd; fill-opacity: 0.8;"/> style="stroke: #999; stroke-width: 6; fill: #ddd; fill-opacity: 0.8;"/>
<image class="badge-image" clip-path="circle() fill-box" <image class="badge-image" clip-path="circle() fill-box"
@ -2327,8 +2327,8 @@ export default {
</svg></a> </svg></a>
<b-modal <b-modal
:id="'r-item-badge-details-'+value.id" :id="'r-item-badge-details-'+value.id"
:title="value.badge.name" :title="value.badge.name"
size="lg" size="lg"
ok-only ok-only
centered centered
@ -2376,7 +2376,7 @@ export default {
</b-col></b-row> </b-col></b-row>
</b-container> </b-container>
</b-modal> </b-modal>
</div> </div>
`, `,
}); });

View File

@ -24,11 +24,11 @@ export const Absolute = {
/** One point. 1pt = 1/72nd of 1in */ /** One point. 1pt = 1/72nd of 1in */
pt: 96 / 72 pt: 96 / 72
}; };
// units ->(calc context)-> pixels // units ->(calc context)-> pixels
export const Relative = { export const Relative = {
/** /**
* Equal to 1% of the height of the viewport * Equal to 1% of the height of the viewport
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -36,7 +36,7 @@ export const Absolute = {
return ((ctx ? ctx.viewportHeight : window.innerHeight) / 100) * count; return ((ctx ? ctx.viewportHeight : window.innerHeight) / 100) * count;
}, },
/** /**
* Equal to 1% of the width of the viewport * Equal to 1% of the width of the viewport
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -44,7 +44,7 @@ export const Absolute = {
return ((ctx ? ctx.viewportWidth : window.innerWidth) / 100) * count; return ((ctx ? ctx.viewportWidth : window.innerWidth) / 100) * count;
}, },
/** /**
* 1/100th of the smallest viewport side * 1/100th of the smallest viewport side
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -58,7 +58,7 @@ export const Absolute = {
); );
}, },
/** /**
* 1/100th of the largest viewport side * 1/100th of the largest viewport side
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -72,7 +72,7 @@ export const Absolute = {
); );
}, },
/** /**
* Represents the font-size of <html> element * Represents the font-size of <html> element
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -86,7 +86,7 @@ export const Absolute = {
); );
}, },
/** /**
* percent of width * percent of width
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -94,7 +94,7 @@ export const Absolute = {
return ((ctx ? ctx.width : document.body.clientWidth) / 100) * count; return ((ctx ? ctx.width : document.body.clientWidth) / 100) * count;
}, },
/** /**
* percent of height * percent of height
* @param {number} count * @param {number} count
* @param {object} ctx * @param {object} ctx
*/ */
@ -102,59 +102,59 @@ export const Absolute = {
return ((ctx ? ctx.height : document.body.clientHeight) / 100) * count; return ((ctx ? ctx.height : document.body.clientHeight) / 100) * count;
} }
}; };
export const Units = { export const Units = {
...Relative, ...Relative,
...Absolute ...Absolute
}; };
export const UnitRegexpStr = `(?:\\s|^)(\\d*(?:\\.\\d+)?)(${Object.keys( export const UnitRegexpStr = `(?:\\s|^)(\\d*(?:\\.\\d+)?)(${Object.keys(
Units Units
).join("|")})(?:\\s|$|\\n)`; ).join("|")})(?:\\s|$|\\n)`;
export const UnitRegexp = new RegExp(UnitRegexpStr); export const UnitRegexp = new RegExp(UnitRegexpStr);
export const UnitRegexpGM = new RegExp(UnitRegexpStr, "gm"); export const UnitRegexpGM = new RegExp(UnitRegexpStr, "gm");
/** /**
* *
* @param {*} count * @param {*} count
* @param {*} fromUnits * @param {*} fromUnits
* @param {*} toUnits * @param {*} toUnits
* @param {*} ctx * @param {*} ctx
* @returns * @returns
*/ */
export function convert(count, fromUnits, toUnits, ctx = calcCtx()) { export function convert(count, fromUnits, toUnits, ctx = calcCtx()) {
const baseUnit = Units[fromUnits]; const baseUnit = Units[fromUnits];
const basePx = const basePx =
typeof baseUnit === "function" ? baseUnit(count, ctx) : baseUnit * count; typeof baseUnit === "function" ? baseUnit(count, ctx) : baseUnit * count;
const dstUnit = Units[toUnits]; const dstUnit = Units[toUnits];
const dstBasePx = typeof dstUnit === "function" ? dstUnit(1, ctx) : dstUnit; const dstBasePx = typeof dstUnit === "function" ? dstUnit(1, ctx) : dstUnit;
return basePx / dstBasePx; return basePx / dstBasePx;
} }
/** /**
* *
* @param {*} expr * @param {*} expr
* @param {*} toUnits * @param {*} toUnits
* @param {*} ctx * @param {*} ctx
* @returns * @returns
*/ */
export function convertAllInStr(expr, toUnits, ctx = calcCtx()) { export function convertAllInStr(expr, toUnits, ctx = calcCtx()) {
return expr.replace(UnitRegexpGM, (substr, count, unit) => { return expr.replace(UnitRegexpGM, (substr, count, unit) => {
return convert(parseFloat(count), unit, toUnits, ctx).toString(); return convert(parseFloat(count), unit, toUnits, ctx).toString();
}); });
} }
/** /**
* *
* @param {*} el * @param {*} el
* @returns * @returns
*/ */
export function calcCtx(el) { export function calcCtx(el) {
if (el) { if (el) {
const rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
return { return {
width: rect.width, width: rect.width,
height: rect.height, height: rect.height,
@ -176,13 +176,13 @@ export const Absolute = {
}; };
} }
} }
/** /**
* *
* @param {*} expression * @param {*} expression
* @param {*} el_ctx * @param {*} el_ctx
* @param {*} ctx * @param {*} ctx
* @returns * @returns
*/ */
export function calc(expression, el_ctx, ctx) { export function calc(expression, el_ctx, ctx) {
if (el_ctx === undefined) {ctx = calcCtx(); } if (el_ctx === undefined) {ctx = calcCtx(); }
@ -193,7 +193,6 @@ export const Absolute = {
ctx = el_ctx; ctx = el_ctx;
} }
} }
return eval(convertAllInStr(expression, "px", ctx)); return eval(convertAllInStr(expression, "px", ctx));
} }

View File

@ -1,7 +1,7 @@
/*eslint no-console: "off"*/ /*eslint no-console: "off"*/
/*eslint no-trailing-spaces: "off"*/ /*eslint no-trailing-spaces: "off"*/
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2023 P.M. Kuipers Copyright (c) 2023 P.M. Kuipers
@ -100,10 +100,10 @@ export class SimpleLine {
/** /**
* Create a new line object * Create a new line object
* *
* @param {HTMLElement|string} start The element the line starts from * @param {HTMLElement|string} start The element the line starts from
* @param {HTMLElement|string} end The element the line ends at * @param {HTMLElement|string} end The element the line ends at
* @param {object} config Configuration for the * @param {object} config Configuration for the
* @returns {SimpleLine} object * @returns {SimpleLine} object
*/ */
constructor(start, end, config){ constructor(start, end, config){
@ -159,10 +159,10 @@ export class SimpleLine {
}); });
}); });
}); });
this.mutationObserver.observe(this.start.parentElement, { subtree: false, childList: true }); this.mutationObserver.observe(this.start.parentElement, { subtree: false, childList: true });
this.mutationObserver.observe(this.end.parentElement, { subtree: false, childList: true }); this.mutationObserver.observe(this.end.parentElement, { subtree: false, childList: true });
// Setup the position checker // Setup the position checker
this.positionCheck(); // Initialize refresh this.positionCheck(); // Initialize refresh
@ -175,7 +175,7 @@ export class SimpleLine {
/** /**
* Change the simpleline config * Change the simpleline config
* @param {object} config The object containing the specs * @param {object} config The object containing the specs
*/ */
setConfig(config){ setConfig(config){
// setup defaults // setup defaults
@ -325,7 +325,7 @@ export class SimpleLine {
/** /**
* (Re)Paint the arrow * (Re)Paint the arrow
* *
*/ */
update(){ update(){
if(!this.active){ return;} // don't do this if we are no longer active if(!this.active){ return;} // don't do this if we are no longer active

File diff suppressed because it is too large Load Diff

View File

@ -67,7 +67,7 @@ export default {
enddate(){ enddate(){
if(this.value.pages[0].enddate){ if(this.value.pages[0].enddate){
return format_date(this.value.pages[0].enddate); return format_date(this.value.pages[0].enddate);
} }
else { else {
return this.text.noenddate; return this.text.noenddate;
} }
@ -79,7 +79,7 @@ export default {
} }
}, },
template: ` template: `
<b-card <b-card
:class="'s-studyplan-card timing-' + timing" :class="'s-studyplan-card timing-' + timing"
> >
<template #header></template> <template #header></template>
@ -95,7 +95,7 @@ export default {
<span :class="'t-timing-'+timing" v-html="startdate + ' - '+ enddate"></span> <span :class="'t-timing-'+timing" v-html="startdate + ' - '+ enddate"></span>
<span class="s-studyplan-card-buttons"> <span class="s-studyplan-card-buttons">
<slot name='footer'></slot> <slot name='footer'></slot>
<b-button style="float:right;" v-if='open' variant='primary' <b-button style="float:right;" v-if='open' variant='primary'
@click.prevent='onOpenClick($event)'>{{ text.open }}</b-button> @click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>
</span> </span>
</template> </template>
@ -123,7 +123,7 @@ export default {
ItemEventBus.$on('headerHeightChange', this.onHeaderHeightChange); ItemEventBus.$on('headerHeightChange', this.onHeaderHeightChange);
}, },
computed: { computed: {
}, },
methods: { methods: {
onHeaderHeightChange(newheight){ onHeaderHeightChange(newheight){

View File

@ -2,7 +2,7 @@ Instructions for downloading and integrating vue-easy-dnd
(Unfortunately we cannot use the latest version, since that requires vue3 support, and bootstrap-vue isn't there yet...) (Unfortunately we cannot use the latest version, since that requires vue3 support, and bootstrap-vue isn't there yet...)
1. download vue-easy-dnd distribusion throuhg npm 1. download vue-easy-dnd distribusion throuhg npm
npm install vue-easy-dnd@1.22.0 npm install vue-easy-dnd@1.22.0
2. Copy node_modules/vue-easy-dnd/dist/vue-easy-dnd.esm.js to amd/src/vue-easy-dnd/ 2. Copy node_modules/vue-easy-dnd/dist/vue-easy-dnd.esm.js to amd/src/vue-easy-dnd/
@ -17,7 +17,7 @@ Instructions for downloading and integrating vue-easy-dnd
This version if vue-easy-dnd requires some additional support libraries in the same folder This version if vue-easy-dnd requires some additional support libraries in the same folder
-- vue-class-component.js -- -- vue-class-component.js --
1. if not already installed during vue-easy-dnd install, download vue-class-component distribusion through npm 1. if not already installed during vue-easy-dnd install, download vue-class-component distribusion through npm
npm install vue-class-component@7.2.6 npm install vue-class-component@7.2.6
2. copy node_modules/vue-class-component/dist/vue-class-component.esm-browser.js to amd/src/vue-easy-dnd/vue-class-component.js 2. copy node_modules/vue-class-component/dist/vue-class-component.esm-browser.js to amd/src/vue-easy-dnd/vue-class-component.js
@ -31,9 +31,9 @@ This version if vue-easy-dnd requires some additional support libraries in the s
5. Replace both export statements at the bottom of the file by the following: 5. Replace both export statements at the bottom of the file by the following:
export { Component, createDecorator, mixins }; export { Component, createDecorator, mixins };
-- vue-property-decorator -- -- vue-property-decorator --
1. if not already installed during vue-easy-dnd install, download vue-property-decorator distribusion through npm 1. if not already installed during vue-easy-dnd install, download vue-property-decorator distribusion through npm
npm install vue-property-decorator@8.5.1 npm install vue-property-decorator@8.5.1
2. copy node_modules/vue-property-decorator/lib/vue-property-decorator.js to amd/src/vue-easy-dnd/ 2. copy node_modules/vue-property-decorator/lib/vue-property-decorator.js to amd/src/vue-easy-dnd/
@ -47,7 +47,7 @@ This version if vue-easy-dnd requires some additional support libraries in the s
/*eslint no-unused-vars: "off" */ /*eslint no-unused-vars: "off" */
-- reflect-metadata.js -- -- reflect-metadata.js --
1. if not already installed during vue-easy-dnd install, download reflect-metadata distribusion through npm 1. if not already installed during vue-easy-dnd install, download reflect-metadata distribusion through npm
npm install reflect-metadata@0.1.13 npm install reflect-metadata@0.1.13
2. copy node_modules/reflect-metadata/Reflect.js to amd/src/vue-easy-dnd/reflect-metadata.js 2. copy node_modules/reflect-metadata/Reflect.js to amd/src/vue-easy-dnd/reflect-metadata.js
@ -56,5 +56,5 @@ This version if vue-easy-dnd requires some additional support libraries in the s
/* eslint-disable */ /* eslint-disable */
/*eslint no-unused-vars: "off" */ /*eslint no-unused-vars: "off" */
4. add the following after the copyright notice: 4. add the following after the copyright notice:
export {Reflect}; export {Reflect};

View File

@ -334,7 +334,7 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
cancelScrollAction(); cancelScrollAction();
return false; return false;
} }
// NOTE: Much of the information here, with regard to document dimensions, // NOTE: Much of the information here, with regard to document dimensions,
// viewport dimensions, and window scrolling is derived from JavaScript.info. // viewport dimensions, and window scrolling is derived from JavaScript.info.
// I am consuming it here primarily as NOTE TO SELF. // I am consuming it here primarily as NOTE TO SELF.
@ -345,18 +345,18 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
// recalculated on window-resize events (for the most part). I am keeping it // recalculated on window-resize events (for the most part). I am keeping it
// all here in the mousemove event handler to remove as many of the moving // all here in the mousemove event handler to remove as many of the moving
// parts as possible and keep the demo as simple as possible. // parts as possible and keep the demo as simple as possible.
// Get the viewport-relative coordinates of the mousemove event. // Get the viewport-relative coordinates of the mousemove event.
var rect = container.getBoundingClientRect(); var rect = container.getBoundingClientRect();
var isBody = container === document.body; var isBody = container === document.body;
var viewportX = clientX - rect.left; var viewportX = clientX - rect.left;
var viewportY = clientY - rect.top; var viewportY = clientY - rect.top;
if (isBody) { if (isBody) {
viewportX = clientX; viewportX = clientX;
viewportY = clientY; viewportY = clientY;
} }
// Get the viewport dimensions. // Get the viewport dimensions.
var viewportWidth = rect.width; var viewportWidth = rect.width;
var viewportHeight = rect.height; var viewportHeight = rect.height;
@ -364,7 +364,7 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
viewportWidth = document.documentElement.clientWidth; viewportWidth = document.documentElement.clientWidth;
viewportHeight = document.documentElement.clientHeight; viewportHeight = document.documentElement.clientHeight;
} }
// Next, we need to determine if the mouse is within the "edge" of the // Next, we need to determine if the mouse is within the "edge" of the
// viewport, which may require scrolling the window. To do this, we need to // viewport, which may require scrolling the window. To do this, we need to
// calculate the boundaries of the edge in the viewport (these coordinates // calculate the boundaries of the edge in the viewport (these coordinates
@ -373,22 +373,22 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
var edgeLeft = edgeSize; var edgeLeft = edgeSize;
var edgeBottom = ( viewportHeight - edgeSize ); var edgeBottom = ( viewportHeight - edgeSize );
var edgeRight = ( viewportWidth - edgeSize ); var edgeRight = ( viewportWidth - edgeSize );
var isInLeftEdge = ( viewportX < edgeLeft ); var isInLeftEdge = ( viewportX < edgeLeft );
var isInRightEdge = ( viewportX > edgeRight ); var isInRightEdge = ( viewportX > edgeRight );
var isInTopEdge = ( viewportY < edgeTop ); var isInTopEdge = ( viewportY < edgeTop );
var isInBottomEdge = ( viewportY > edgeBottom ); var isInBottomEdge = ( viewportY > edgeBottom );
// If the mouse is not in the viewport edge, there's no need to calculate // If the mouse is not in the viewport edge, there's no need to calculate
// anything else. // anything else.
if (!(isInLeftEdge || isInRightEdge || isInTopEdge || isInBottomEdge)) { if (!(isInLeftEdge || isInRightEdge || isInTopEdge || isInBottomEdge)) {
cancelScrollAction(); cancelScrollAction();
return false; return false;
} }
// If we made it this far, the user's mouse is located within the edge of the // If we made it this far, the user's mouse is located within the edge of the
// viewport. As such, we need to check to see if scrolling needs to be done. // viewport. As such, we need to check to see if scrolling needs to be done.
// Get the document dimensions. // Get the document dimensions.
var documentWidth = Math.max( var documentWidth = Math.max(
container.scrollWidth, container.scrollWidth,
@ -400,13 +400,13 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
container.offsetHeight, container.offsetHeight,
container.clientHeight container.clientHeight
); );
// Calculate the maximum scroll offset in each direction. Since you can only // Calculate the maximum scroll offset in each direction. Since you can only
// scroll the overflow portion of the document, the maximum represents the // scroll the overflow portion of the document, the maximum represents the
// length of the document that is NOT in the viewport. // length of the document that is NOT in the viewport.
var maxScrollX = (documentWidth - viewportWidth); var maxScrollX = (documentWidth - viewportWidth);
var maxScrollY = (documentHeight - viewportHeight); var maxScrollY = (documentHeight - viewportHeight);
// As we examine the mousemove event, we want to adjust the window scroll in // As we examine the mousemove event, we want to adjust the window scroll in
// immediate response to the event; but, we also want to continue adjusting // immediate response to the event; but, we also want to continue adjusting
// the window scroll if the user rests their mouse in the edge boundary. To // the window scroll if the user rests their mouse in the edge boundary. To
@ -415,12 +415,12 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
// still be scrolled in a particular direction. // still be scrolled in a particular direction.
(function checkForWindowScroll() { (function checkForWindowScroll() {
cancelScrollAction(); cancelScrollAction();
if (adjustWindowScroll()) { if (adjustWindowScroll()) {
timer = setTimeout( checkForWindowScroll, 30 ); timer = setTimeout( checkForWindowScroll, 30 );
} }
})(); })();
// Adjust the window scroll based on the user's mouse position. Returns True // Adjust the window scroll based on the user's mouse position. Returns True
// or False depending on whether or not the window scroll was changed. // or False depending on whether or not the window scroll was changed.
function adjustWindowScroll() { function adjustWindowScroll() {
@ -431,27 +431,27 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
currentScrollX = window.pageXOffset; currentScrollX = window.pageXOffset;
currentScrollY = window.pageYOffset; currentScrollY = window.pageYOffset;
} }
// Determine if the window can be scrolled in any particular direction. // Determine if the window can be scrolled in any particular direction.
var canScrollUp = (currentScrollY > 0); var canScrollUp = (currentScrollY > 0);
var canScrollDown = (currentScrollY < maxScrollY); var canScrollDown = (currentScrollY < maxScrollY);
var canScrollLeft = (currentScrollX > 0); var canScrollLeft = (currentScrollX > 0);
var canScrollRight = (currentScrollX < maxScrollX); var canScrollRight = (currentScrollX < maxScrollX);
// Since we can potentially scroll in two directions at the same time, // Since we can potentially scroll in two directions at the same time,
// let's keep track of the next scroll, starting with the current scroll. // let's keep track of the next scroll, starting with the current scroll.
// Each of these values can then be adjusted independently in the logic // Each of these values can then be adjusted independently in the logic
// below. // below.
var nextScrollX = currentScrollX; var nextScrollX = currentScrollX;
var nextScrollY = currentScrollY; var nextScrollY = currentScrollY;
// As we examine the mouse position within the edge, we want to make the // As we examine the mouse position within the edge, we want to make the
// incremental scroll changes more "intense" the closer that the user // incremental scroll changes more "intense" the closer that the user
// gets the viewport edge. As such, we'll calculate the percentage that // gets the viewport edge. As such, we'll calculate the percentage that
// the user has made it "through the edge" when calculating the delta. // the user has made it "through the edge" when calculating the delta.
// Then, that use that percentage to back-off from the "max" step value. // Then, that use that percentage to back-off from the "max" step value.
var maxStep = 50; var maxStep = 50;
// Should we scroll left? // Should we scroll left?
if (isInLeftEdge && canScrollLeft) { if (isInLeftEdge && canScrollLeft) {
var intensity = ((edgeLeft - viewportX) / edgeSize); var intensity = ((edgeLeft - viewportX) / edgeSize);
@ -462,7 +462,7 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
var intensity = ((viewportX - edgeRight) / edgeSize); var intensity = ((viewportX - edgeRight) / edgeSize);
nextScrollX = (nextScrollX + (maxStep * intensity)); nextScrollX = (nextScrollX + (maxStep * intensity));
} }
// Should we scroll up? // Should we scroll up?
if (isInTopEdge && canScrollUp) { if (isInTopEdge && canScrollUp) {
var intensity = ((edgeTop - viewportY) / edgeSize); var intensity = ((edgeTop - viewportY) / edgeSize);
@ -473,14 +473,14 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
var intensity = ((viewportY - edgeBottom) / edgeSize); var intensity = ((viewportY - edgeBottom) / edgeSize);
nextScrollY = (nextScrollY + (maxStep * intensity)); nextScrollY = (nextScrollY + (maxStep * intensity));
} }
// Sanitize invalid maximums. An invalid scroll offset won't break the // Sanitize invalid maximums. An invalid scroll offset won't break the
// subsequent .scrollTo() call; however, it will make it harder to // subsequent .scrollTo() call; however, it will make it harder to
// determine if the .scrollTo() method should have been called in the // determine if the .scrollTo() method should have been called in the
// first place. // first place.
nextScrollX = Math.max(0, Math.min(maxScrollX, nextScrollX)); nextScrollX = Math.max(0, Math.min(maxScrollX, nextScrollX));
nextScrollY = Math.max(0, Math.min(maxScrollY, nextScrollY)); nextScrollY = Math.max(0, Math.min(maxScrollY, nextScrollY));
if ( if (
(nextScrollX !== currentScrollX) || (nextScrollX !== currentScrollX) ||
(nextScrollY !== currentScrollY) (nextScrollY !== currentScrollY)
@ -492,7 +492,7 @@ function performEdgeScroll(event, container, clientX, clientY, edgeSize) {
return false; return false;
} }
} }
return true return true
} }
@ -1036,9 +1036,9 @@ var __vue_staticRenderFns__ = [];
/* functional template */ /* functional template */
var __vue_is_functional_template__ = false; var __vue_is_functional_template__ = false;
/* style inject SSR */ /* style inject SSR */
var Drag$1 = normalizeComponent_1( var Drag$1 = normalizeComponent_1(
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
__vue_inject_styles__, __vue_inject_styles__,
@ -1291,9 +1291,9 @@ var __vue_staticRenderFns__$1 = [];
/* functional template */ /* functional template */
var __vue_is_functional_template__$1 = false; var __vue_is_functional_template__$1 = false;
/* style inject SSR */ /* style inject SSR */
var Drop$1 = normalizeComponent_1( var Drop$1 = normalizeComponent_1(
{ render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 }, { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
__vue_inject_styles__$1, __vue_inject_styles__$1,
@ -1349,11 +1349,11 @@ var __vue_staticRenderFns__$2 = [];
/* functional template */ /* functional template */
var __vue_is_functional_template__$2 = false; var __vue_is_functional_template__$2 = false;
/* style inject */ /* style inject */
/* style inject SSR */
/* style inject SSR */
var DropMask$1 = normalizeComponent_1( var DropMask$1 = normalizeComponent_1(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 }, { render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
__vue_inject_styles__$2, __vue_inject_styles__$2,
@ -1531,11 +1531,11 @@ var __vue_staticRenderFns__$3 = [];
/* functional template */ /* functional template */
var __vue_is_functional_template__$3 = false; var __vue_is_functional_template__$3 = false;
/* style inject */ /* style inject */
/* style inject SSR */
/* style inject SSR */
var DragFeedback$1 = normalizeComponent_1( var DragFeedback$1 = normalizeComponent_1(
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 }, { render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
__vue_inject_styles__$3, __vue_inject_styles__$3,
@ -2055,9 +2055,9 @@ var __vue_staticRenderFns__$4 = [];
/* functional template */ /* functional template */
var __vue_is_functional_template__$4 = false; var __vue_is_functional_template__$4 = false;
/* style inject SSR */ /* style inject SSR */
var DropList$1 = normalizeComponent_1( var DropList$1 = normalizeComponent_1(
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 }, { render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
__vue_inject_styles__$4, __vue_inject_styles__$4,

View File

@ -633,7 +633,7 @@
// multiple renders, cloning them avoids errors when DOM manipulations rely // multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference. // on their elm reference.
function cloneVNode(vnode) { function cloneVNode(vnode) {
var cloned = new VNode(vnode.tag, vnode.data, var cloned = new VNode(vnode.tag, vnode.data,
// #7975 // #7975
// clone children array to avoid mutating original in case of cloning // clone children array to avoid mutating original in case of cloning
// a child. // a child.
@ -2066,7 +2066,7 @@
return data; return data;
} }
function resolveScopedSlots(fns, res, function resolveScopedSlots(fns, res,
// the following are added in 2.6 // the following are added in 2.6
hasDynamicKeys, contentHashKey) { hasDynamicKeys, contentHashKey) {
res = res || { $stable: !hasDynamicKeys }; res = res || { $stable: !hasDynamicKeys };
@ -4118,7 +4118,7 @@
*/ */
var Watcher = /** @class */ (function () { var Watcher = /** @class */ (function () {
function Watcher(vm, expOrFn, cb, options, isRenderWatcher) { function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
recordEffectScope(this, recordEffectScope(this,
// if the active effect scope is manually created (not a component scope), // if the active effect scope is manually created (not a component scope),
// prioritize it // prioritize it
activeEffectScope && !activeEffectScope._vm activeEffectScope && !activeEffectScope._vm
@ -5025,14 +5025,14 @@
var name = getComponentName(Ctor.options) || tag; var name = getComponentName(Ctor.options) || tag;
var vnode = new VNode( var vnode = new VNode(
// @ts-expect-error // @ts-expect-error
"vue-component-".concat(Ctor.cid).concat(name ? "-".concat(name) : ''), data, undefined, undefined, undefined, context, "vue-component-".concat(Ctor.cid).concat(name ? "-".concat(name) : ''), data, undefined, undefined, undefined, context,
// @ts-expect-error // @ts-expect-error
{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, asyncFactory); { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, asyncFactory);
return vnode; return vnode;
} }
function createComponentInstanceForVnode( function createComponentInstanceForVnode(
// we know it's MountedComponentVNode but flow doesn't // we know it's MountedComponentVNode but flow doesn't
vnode, vnode,
// activeInstance in lifecycle state // activeInstance in lifecycle state
parent) { parent) {
var options = { var options = {
@ -7119,7 +7119,7 @@
var oldElm = oldVnode.elm; var oldElm = oldVnode.elm;
var parentElm = nodeOps.parentNode(oldElm); var parentElm = nodeOps.parentNode(oldElm);
// create new node // create new node
createElm(vnode, insertedVnodeQueue, createElm(vnode, insertedVnodeQueue,
// extremely rare edge case: do not insert if old element is in a // extremely rare edge case: do not insert if old element is in a
// leaving transition. Only happens when combining transition + // leaving transition. Only happens when combining transition +
// keep-alive + HOCs. (#4590) // keep-alive + HOCs. (#4590)
@ -7495,7 +7495,7 @@
if (c === 0x2f) { if (c === 0x2f) {
// / // /
var j = i - 1; var j = i - 1;
var p var p
// find first non-whitespace prev char // find first non-whitespace prev char
= void 0; = void 0;
// find first non-whitespace prev char // find first non-whitespace prev char
@ -8019,7 +8019,7 @@
target.addEventListener(name, handler, supportsPassive ? { capture: capture, passive: passive } : capture); target.addEventListener(name, handler, supportsPassive ? { capture: capture, passive: passive } : capture);
} }
function remove(name, handler, capture, _target) { function remove(name, handler, capture, _target) {
(_target || target).removeEventListener(name, (_target || target).removeEventListener(name,
//@ts-expect-error //@ts-expect-error
handler._wrapper || handler, capture); handler._wrapper || handler, capture);
} }
@ -11018,7 +11018,7 @@
if (!el.plain || (el.pre && maybeComponent)) { if (!el.plain || (el.pre && maybeComponent)) {
data = genData(el, state); data = genData(el, state);
} }
var tag var tag
// check if this is a component in <script setup> // check if this is a component in <script setup>
= void 0; = void 0;
// check if this is a component in <script setup> // check if this is a component in <script setup>

View File

@ -633,7 +633,7 @@
// multiple renders, cloning them avoids errors when DOM manipulations rely // multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference. // on their elm reference.
function cloneVNode(vnode) { function cloneVNode(vnode) {
var cloned = new VNode(vnode.tag, vnode.data, var cloned = new VNode(vnode.tag, vnode.data,
// #7975 // #7975
// clone children array to avoid mutating original in case of cloning // clone children array to avoid mutating original in case of cloning
// a child. // a child.
@ -2066,7 +2066,7 @@
return data; return data;
} }
function resolveScopedSlots(fns, res, function resolveScopedSlots(fns, res,
// the following are added in 2.6 // the following are added in 2.6
hasDynamicKeys, contentHashKey) { hasDynamicKeys, contentHashKey) {
res = res || { $stable: !hasDynamicKeys }; res = res || { $stable: !hasDynamicKeys };
@ -4118,7 +4118,7 @@
*/ */
var Watcher = /** @class */ (function () { var Watcher = /** @class */ (function () {
function Watcher(vm, expOrFn, cb, options, isRenderWatcher) { function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
recordEffectScope(this, recordEffectScope(this,
// if the active effect scope is manually created (not a component scope), // if the active effect scope is manually created (not a component scope),
// prioritize it // prioritize it
activeEffectScope && !activeEffectScope._vm activeEffectScope && !activeEffectScope._vm
@ -5025,14 +5025,14 @@
var name = getComponentName(Ctor.options) || tag; var name = getComponentName(Ctor.options) || tag;
var vnode = new VNode( var vnode = new VNode(
// @ts-expect-error // @ts-expect-error
"vue-component-".concat(Ctor.cid).concat(name ? "-".concat(name) : ''), data, undefined, undefined, undefined, context, "vue-component-".concat(Ctor.cid).concat(name ? "-".concat(name) : ''), data, undefined, undefined, undefined, context,
// @ts-expect-error // @ts-expect-error
{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, asyncFactory); { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }, asyncFactory);
return vnode; return vnode;
} }
function createComponentInstanceForVnode( function createComponentInstanceForVnode(
// we know it's MountedComponentVNode but flow doesn't // we know it's MountedComponentVNode but flow doesn't
vnode, vnode,
// activeInstance in lifecycle state // activeInstance in lifecycle state
parent) { parent) {
var options = { var options = {
@ -7119,7 +7119,7 @@
var oldElm = oldVnode.elm; var oldElm = oldVnode.elm;
var parentElm = nodeOps.parentNode(oldElm); var parentElm = nodeOps.parentNode(oldElm);
// create new node // create new node
createElm(vnode, insertedVnodeQueue, createElm(vnode, insertedVnodeQueue,
// extremely rare edge case: do not insert if old element is in a // extremely rare edge case: do not insert if old element is in a
// leaving transition. Only happens when combining transition + // leaving transition. Only happens when combining transition +
// keep-alive + HOCs. (#4590) // keep-alive + HOCs. (#4590)
@ -7495,7 +7495,7 @@
if (c === 0x2f) { if (c === 0x2f) {
// / // /
var j = i - 1; var j = i - 1;
var p var p
// find first non-whitespace prev char // find first non-whitespace prev char
= void 0; = void 0;
// find first non-whitespace prev char // find first non-whitespace prev char
@ -8019,7 +8019,7 @@
target.addEventListener(name, handler, supportsPassive ? { capture: capture, passive: passive } : capture); target.addEventListener(name, handler, supportsPassive ? { capture: capture, passive: passive } : capture);
} }
function remove(name, handler, capture, _target) { function remove(name, handler, capture, _target) {
(_target || target).removeEventListener(name, (_target || target).removeEventListener(name,
//@ts-expect-error //@ts-expect-error
handler._wrapper || handler, capture); handler._wrapper || handler, capture);
} }
@ -11018,7 +11018,7 @@
if (!el.plain || (el.pre && maybeComponent)) { if (!el.plain || (el.pre && maybeComponent)) {
data = genData(el, state); data = genData(el, state);
} }
var tag var tag
// check if this is a component in <script setup> // check if this is a component in <script setup>
= void 0; = void 0;
// check if this is a component in <script setup> // check if this is a component in <script setup>

View File

@ -17,7 +17,7 @@ fi
# switch vuejs to build mode # switch vuejs to build mode
$SCRIPTDIR/vuemode.sh $BUILDMODE $SCRIPTDIR/vuemode.sh $BUILDMODE
# run the grunt script in the scripts working directory # run the grunt script in the scripts working directory
# so we can be sure all javascript is properly built from the most recent source # so we can be sure all javascript is properly built from the most recent source
cd $SCRIPTDIR cd $SCRIPTDIR
rm -rf "${SCRIPTDIR}/amd/build/" # clear all files in the amd build dir so we don't have leftover files messing things up rm -rf "${SCRIPTDIR}/amd/build/" # clear all files in the amd build dir so we don't have leftover files messing things up

View File

@ -79,12 +79,12 @@ class completionscanner {
/** /**
* Filter a list of students to return only the students enrolled as student * Filter a list of students to return only the students enrolled as student
* in this scanner's course * in this scanner's course
* @param int[] $studentlist Array of student id's * @param int[] $studentlist Array of student id's
* @return int[] * @return int[]
*/ */
private function filter_studentlist(array $studentlist) : array { private function filter_studentlist(array $studentlist) : array {
$course_students = courseinfo::get_course_students($this->courseid); $coursestudents = courseinfo::get_course_students($this->courseid);
return array_intersect($studentlist, $course_students); return array_intersect($studentlist, $coursestudents);
} }
/** /**
@ -166,7 +166,9 @@ class completionscanner {
statistics are marred. statistics are marred.
*/ */
// Get completion info. // Get completion info.
$students = isset($studentlist) ? $this->filter_studentlist($studentlist) : courseinfo::get_course_students($this->courseid); $students = isset($studentlist) ?
$this->filter_studentlist($studentlist) :
courseinfo::get_course_students($this->courseid);
$completed = 0; $completed = 0;
$ungraded = 0; $ungraded = 0;
$completedpass = 0; $completedpass = 0;
@ -179,7 +181,7 @@ class completionscanner {
// Retrieve data for this object. // Retrieve data for this object.
$data = $this->completioninfo->get_data($this->cm, false, $userid); $data = $this->completioninfo->get_data($this->cm, false, $userid);
// If it's an activity completion, add all the relevant activities as sub-items. // If it's an activity completion, add all the relevant activities as sub-items.
$completionstatus = $data->completionstate; $completionstatus = $data->completionstate;
if ($completionstatus == COMPLETION_COMPLETE_PASS) { if ($completionstatus == COMPLETION_COMPLETE_PASS) {
$completedpass++; $completedpass++;

View File

@ -481,7 +481,7 @@ class corecompletioninfo {
// Retrieve data for this object. // Retrieve data for this object.
$data = $this->completion->get_data($cm, false, $userid); $data = $this->completion->get_data($cm, false, $userid);
// If it's an activity completion, add all the relevant activities as sub-items. // If it's an activity completion, add all the relevant activities as sub-items.
$completionstatus = $data->completionstate; $completionstatus = $data->completionstate;
$gradecompletion = $this->completion->get_grade_completion($cm, $userid); $gradecompletion = $this->completion->get_grade_completion($cm, $userid);
/* To comply with the moodle completion report, only count COMPLETED_PASS as completed if /* To comply with the moodle completion report, only count COMPLETED_PASS as completed if
@ -489,30 +489,33 @@ class corecompletioninfo {
and we want to show similar behaviour. This happens when completion data is reset and we want to show similar behaviour. This happens when completion data is reset
in a module in a module
*/ */
if(!$completion->is_complete() && in_array($gradecompletion, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS])) { if ( !$completion->is_complete()
&&
in_array($gradecompletion, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS])) {
/* If a passing grade was provided, but the activity was not completed, /* If a passing grade was provided, but the activity was not completed,
* most likely the completion data was erased. * most likely the completion data was erased.
*/ */
/*$completionstatus = COMPLETION_INCOMPLETE; */
if ( !is_null($cm->completiongradeitemnumber) || ($cm->completionpassgrade) ) { if ( !is_null($cm->completiongradeitemnumber) || ($cm->completionpassgrade) ) {
// Show a warning if this activity has grade completions to help make sense of the completion. // Show a warning if this activity has grade completions to help make sense of the completion.
$iinfo["warning"] = get_string("warning_incomplete_pass","local_treestudyplan"); $iinfo["warning"] = get_string("warning_incomplete_pass", "local_treestudyplan");
} else { } else {
// Show a warning if this activity has no grade requirment for completion. // Show a warning if this activity has no grade requirment for completion.
$iinfo["warning"] = get_string("warning_incomplete_nograderq","local_treestudyplan"); $iinfo["warning"] = get_string("warning_incomplete_nograderq", "local_treestudyplan");
} }
} }
$iinfo['status'] = self::completion_handle($data->completionstate); $iinfo['status'] = self::completion_handle($data->completionstate);
// Re-evaluate the completed value, to make sure COMPLETE_FAIL doesn't creep in as completed. // Re-evaluate the completed value, to make sure COMPLETE_FAIL doesn't creep in as completed.
if (($data->completionstate == COMPLETION_INCOMPLETE) || ($data->completionstate == COMPLETION_COMPLETE_FAIL)) { if (($data->completionstate == COMPLETION_INCOMPLETE)
||
($data->completionstate == COMPLETION_COMPLETE_FAIL)) {
$iinfo['completed'] = false; $iinfo['completed'] = false;
} else { } else {
$iinfo['completed'] = true; $iinfo['completed'] = true;
$progress += 1; // Add a point to the progress counter. $progress += 1; // Add a point to the progress counter.
} }
// Determine the grade (retrieve from grade item, not from completion). // Determine the grade (retrieve from grade item, not from completion).
$grade = $this->get_grade($cm, $userid); $grade = $this->get_grade($cm, $userid);
$iinfo['grade'] = $grade->grade; $iinfo['grade'] = $grade->grade;
@ -542,8 +545,7 @@ class corecompletioninfo {
if ($completion->is_complete()) { if ($completion->is_complete()) {
$progress += 1; // Add a point to the progress counter. $progress += 1; // Add a point to the progress counter.
} }
} } else {
else {
if ($completion->is_complete()) { if ($completion->is_complete()) {
$progress += 1; // Add a point to the progress counter. $progress += 1; // Add a point to the progress counter.
} }
@ -665,7 +667,7 @@ class corecompletioninfo {
// First, let's make sure completion is enabled. // First, let's make sure completion is enabled.
if (!$this->completion->is_enabled()) { if (!$this->completion->is_enabled()) {
debugging("Completion is not enabled for {$this->course->shortname}",DEBUG_NORMAL); debugging("Completion is not enabled for {$this->course->shortname}", DEBUG_NORMAL);
return (object)[ return (object)[
'count' => 0, 'count' => 0,
'completed' => 0, 'completed' => 0,

View File

@ -306,7 +306,7 @@ class courseinfo {
$timing = $this->timing(); $timing = $this->timing();
if (isset($studyitem)) { if (isset($studyitem)) {
$numenrolled = $this->count_enrolled_students($studyitem->studyline()->studyplan()->find_linked_userids()); $numenrolled = $this->count_enrolled_students($studyitem->studyline()->studyplan()->find_linked_userids());
} else { } else {
$numenrolled = 0; $numenrolled = 0;
@ -393,7 +393,7 @@ class courseinfo {
public function is_enrolled_student($userid) : bool { public function is_enrolled_student($userid) : bool {
global $CFG; global $CFG;
foreach (explode(', ', $CFG->gradebookroles) as $roleid) { foreach (explode(', ', $CFG->gradebookroles) as $roleid) {
if (user_has_role_assignment($userid, $roleid, $this->coursecontext->id)) { if (user_has_role_assignment($userid, $roleid, $this->coursecontext->id)) {
return true; return true;
} }
} }
@ -407,8 +407,8 @@ class courseinfo {
*/ */
private function count_enrolled_students(array $userids) { private function count_enrolled_students(array $userids) {
$count = 0; $count = 0;
foreach($userids as $userid){ foreach ($userids as $userid) {
if($this->is_enrolled_student($userid)){ if ($this->is_enrolled_student($userid)) {
$count++; $count++;
} }
} }
@ -447,7 +447,7 @@ class courseinfo {
$info['grades'][] = $gi->user_model($userid); $info['grades'][] = $gi->user_model($userid);
} }
} else if (isset($this->studyitem)) { } else if (isset($this->studyitem)) {
$cc = new corecompletioninfo($this->course,$this->studyitem); $cc = new corecompletioninfo($this->course, $this->studyitem);
$info['completion'] = $cc->user_model($userid); $info['completion'] = $cc->user_model($userid);
} }

View File

@ -444,8 +444,7 @@ class courseservice extends \external_api {
/** /**
* Scan criterium for progress statistics * Scan criterium for progress statistics
* @param mixed $criteriaid Id of criterium * @param mixed $criteriaid Id of criterium
* @param mixed $studyplanid Id of studyplan relevant to this criteria * @param mixed $studyitemid Id of studyplan relevant to this criteria
* @param mixed $courseid Id of course this cirteria is related to
* @return array * @return array
*/ */
public static function scan_completion_progress($criteriaid, $studyitemid) { public static function scan_completion_progress($criteriaid, $studyitemid) {

View File

@ -263,13 +263,13 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
// - completion::INCOMPLETE - All incoming states are incomplete. // - completion::INCOMPLETE - All incoming states are incomplete.
// - completion::PROGRESS - All other states. // - completion::PROGRESS - All other states.
$method = strtoupper($studyitem->conditions()); // One of ANY or ALL. $method = strtoupper($studyitem->conditions()); // One of ANY or ALL.
// First count all states. // First count all states.
$statecount = completion::count_states($completion); $statecount = completion::count_states($completion);
$total = count($completion); $total = count($completion);
if($method == "ANY"){ if ($method == "ANY") {
if ( $statecount[completion::EXCELLENT] >= 1 ) { if ( $statecount[completion::EXCELLENT] >= 1 ) {
return completion::EXCELLENT; return completion::EXCELLENT;
} else if ( $statecount[completion::GOOD] >= 1 ) { } else if ( $statecount[completion::GOOD] >= 1 ) {
@ -283,7 +283,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
} else { } else {
return completion::INCOMPLETE; return completion::INCOMPLETE;
} }
} else { /* ALL (default) */ } else { /* default value of ALL */
if ( $total == $statecount[completion::EXCELLENT]) { if ( $total == $statecount[completion::EXCELLENT]) {
return completion::EXCELLENT; return completion::EXCELLENT;
} else if ( $total == ( $statecount[completion::EXCELLENT] } else if ( $total == ( $statecount[completion::EXCELLENT]

View File

@ -203,13 +203,13 @@ class core_aggregator extends \local_treestudyplan\aggregator {
// - completion::INCOMPLETE - All incoming states are incomplete. // - completion::INCOMPLETE - All incoming states are incomplete.
// - completion::PROGRESS - All other states. // - completion::PROGRESS - All other states.
$method = strtoupper($studyitem->conditions()); // One of ANY or ALL. $method = strtoupper($studyitem->conditions()); // One of ANY or ALL.
// First count all states. // First count all states.
$statecount = completion::count_states($completion); $statecount = completion::count_states($completion);
$total = count($completion); $total = count($completion);
if($method == "ANY"){ if ($method == "ANY") {
if ( $statecount[completion::EXCELLENT] >= 1 ) { if ( $statecount[completion::EXCELLENT] >= 1 ) {
return completion::EXCELLENT; return completion::EXCELLENT;
} else if ( $statecount[completion::GOOD] >= 1 ) { } else if ( $statecount[completion::GOOD] >= 1 ) {
@ -223,7 +223,7 @@ class core_aggregator extends \local_treestudyplan\aggregator {
} else { } else {
return completion::INCOMPLETE; return completion::INCOMPLETE;
} }
} else { /* ALL (default) */ } else { /* default value of ALL */
if ( $total == $statecount[completion::EXCELLENT]) { if ( $total == $statecount[completion::EXCELLENT]) {
return completion::EXCELLENT; return completion::EXCELLENT;
} else if ( $total == ( $statecount[completion::EXCELLENT] } else if ( $total == ( $statecount[completion::EXCELLENT]

View File

@ -312,7 +312,7 @@ class period {
// Reload record after edit and ensure end dates of previous period are adjusted if needed. // Reload record after edit and ensure end dates of previous period are adjusted if needed.
$this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST); $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST);
// Adjust end date of previous period if needed // Adjust end date of previous period if needed.
if (isset($prev) && !empty($fields['startdate'])) { if (isset($prev) && !empty($fields['startdate'])) {
$maxdate = $this->startdate()->sub(new DateInterval("P1D")); // Subtract 1 day, since periods include the end day. $maxdate = $this->startdate()->sub(new DateInterval("P1D")); // Subtract 1 day, since periods include the end day.
$rqdate = $prev->enddate(); $rqdate = $prev->enddate();
@ -321,7 +321,7 @@ class period {
} }
} }
// Adjust start date of next period if needed // Adjust start date of next period if needed.
if (isset($next) && !empty($fields['enddate'])) { if (isset($next) && !empty($fields['enddate'])) {
$mindate = $this->enddate()->add(new DateInterval("P1D")); // Subtract 1 day, since periods include the end day. $mindate = $this->enddate()->add(new DateInterval("P1D")); // Subtract 1 day, since periods include the end day.
$rqdate = $next->startdate(); $rqdate = $next->startdate();

View File

@ -1471,7 +1471,7 @@ class studyplanservice extends \external_api {
*/ */
public static function get_period($id) { public static function get_period($id) {
$p = period::find_by_id($id); $p = period::find_by_id($id);
// Public data - no rights check needed // Public data - no rights check needed.
\external_api::validate_context($p->page()->studyplan()->context()); \external_api::validate_context($p->page()->studyplan()->context());
return $p->model(); return $p->model();
} }

View File

@ -27,7 +27,7 @@ require_once($CFG->libdir.'/weblib.php');
$systemcontext = context_system::instance(); $systemcontext = context_system::instance();
$PAGE->set_url("/local/treestudyplan/doc.php", array()); $PAGE->set_url("/local/treestudyplan/doc.php", array());
if($CFG->debugdeveloper){ if ($CFG->debugdeveloper) {
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
} }
$PAGE->set_pagelayout('base'); $PAGE->set_pagelayout('base');

View File

@ -18,12 +18,12 @@
<h3>Course manipulation</h3> <h3>Course manipulation</h3>
<p><b>Cascade cohort sync</b> can be used to manually enrol cohorts and users linked to the study plan into the courses that are in the studyplan. <p><b>Cascade cohort sync</b> can be used to manually enrol cohorts and users linked to the study plan into the courses that are in the studyplan.
If you do not have automatic cascading enabled, you must use this button to do it manually</p> If you do not have automatic cascading enabled, you must use this button to do it manually</p>
<p>It will create a cohort-sync enrolment to all courses in the studyplan for each cohort you have linked here. <p>It will create a cohort-sync enrolment to all courses in the studyplan for each cohort you have linked here.
It can also automatically remove those enrolments when a cohort is unlinked from a studyplan.</br> It can also automatically remove those enrolments when a cohort is unlinked from a studyplan.</br>
Individually linked students can also be cascaded down to the courses (if the feature is enabled). In those cases a manual enrolment is created for the users Individually linked students can also be cascaded down to the courses (if the feature is enabled). In those cases a manual enrolment is created for the users
in each of the studyplan's courses. Those enrolments will never be automatically removed, since that might deprive a student of study content. in each of the studyplan's courses. Those enrolments will never be automatically removed, since that might deprive a student of study content.
</p> </p>
<p><b>Force scales</b> Only useful in <i>Manual</i> aggregation mode. In those cases you may want to enforce a specific scale to all selected gradables in <p><b>Force scales</b> Only useful in <i>Manual</i> aggregation mode. In those cases you may want to enforce a specific scale to all selected gradables in
the studyplan. This can save time manually updating all the gradables used in the studyplan to a standerdized grade scale. This tool allows you to do so.</p> the studyplan. This can save time manually updating all the gradables used in the studyplan to a standerdized grade scale. This tool allows you to do so.</p>
<p></p> <p></p>
<h3>Backup and restore</h3> <h3>Backup and restore</h3>
@ -32,12 +32,12 @@
<p>Use <b>restore studylines from backup</b> to import the content (study lines and courses) from a studyplan into the current study plan</p> <p>Use <b>restore studylines from backup</b> to import the content (study lines and courses) from a studyplan into the current study plan</p>
<div class="generalbox alert alert-success"><b>PRO TIP:</b><br> <div class="generalbox alert alert-success"><b>PRO TIP:</b><br>
<p>If you are proficient in editing json like data structures, you can manually edit the .json backups and then restore them as new courses.</p> <p>If you are proficient in editing json like data structures, you can manually edit the .json backups and then restore them as new courses.</p>
<p>This is mostly useful if you need to create multiple study plans with only minor changes, or predictable changes <p>This is mostly useful if you need to create multiple study plans with only minor changes, or predictable changes
(like changing some text in course short names to link to new versions of courses)</p> (like changing some text in course short names to link to new versions of courses)</p>
</div> </div>
<h3>Deleting a studyplan</h3> <h3>Deleting a studyplan</h3>
<p>Sometimes you want to delete an entire study plan. This can be done using the button found in the <b>delete</b> tab</p> <p>Sometimes you want to delete an entire study plan. This can be done using the button found in the <b>delete</b> tab</p>
<h2>Setting a simple name for the courses</h2> <h2>Setting a simple name for the courses</h2>
<p>By default, the short name of the course will be displayed in the study plan. However, often this name is quite long and has some <p>By default, the short name of the course will be displayed in the study plan. However, often this name is quite long and has some
prefixes or suffixes to distinguish it from other courses that were given in earlier academic years. If you want to display a simpler name, prefixes or suffixes to distinguish it from other courses that were given in earlier academic years. If you want to display a simpler name,
@ -52,22 +52,22 @@
<a id="gradescale"><h2>Grade & Scale interpretation</h2></a> <a id="gradescale"><h2>Grade & Scale interpretation</h2></a>
<div class="generalbox alert alert-info"><b>NOTE:</b><br> <div class="generalbox alert alert-info"><b>NOTE:</b><br>
<p>The information in this category is mostly applicable to <i>manual</i> aggregation methods for study plans. <p>The information in this category is mostly applicable to <i>manual</i> aggregation methods for study plans.
If <i>Moodle course completion</i> is used, it is especially important to always configure <b>grade to pass</b></p> If <i>Moodle course completion</i> is used, it is especially important to always configure <b>grade to pass</b></p>
<p>Note that future versions may use this feature to automatically repair missing <b>grade to pass</b> in gradable activities.</p> <p>Note that future versions may use this feature to automatically repair missing <b>grade to pass</b> in gradable activities.</p>
</div> </div>
<p>Best practice is to set a <b>grade to pass</b> in all gradable activities in the studyplan. This way the entire system will know <p>Best practice is to set a <b>grade to pass</b> in all gradable activities in the studyplan. This way the entire system will know
if a gradable activity wass passed or failed.</p> if a gradable activity wass passed or failed.</p>
<p>But since teachers are all human, this may occasionally (or regularly) be forgotten. In the absence of a configured <b>grade to pass</b>, <p>But since teachers are all human, this may occasionally (or regularly) be forgotten. In the absence of a configured <b>grade to pass</b>,
the studyplan system will use the values configured here to determine if a grade was a passable grade.</p> the studyplan system will use the values configured here to determine if a grade was a passable grade.</p>
<p>Go to <b>Site administration</b> -> <b>Courses</b> -> <b>Studyplans</b> -> <b>Configure grade &amp; scale interpretation</b> to configure it.</p> <p>Go to <b>Site administration</b> -> <b>Courses</b> -> <b>Studyplans</b> -> <b>Configure grade &amp; scale interpretation</b> to configure it.</p>
<p>Each scale will need to have a <b>completion threshold</b> set. This is the scale value that starts a passing grade.</p> <p>Each scale will need to have a <b>completion threshold</b> set. This is the scale value that starts a passing grade.</p>
<p>You can also configure certain <b>maximum grade points</b> to have a default passing value (<b>completion threshold</b>)</p> <p>You can also configure certain <b>maximum grade points</b> to have a default passing value (<b>completion threshold</b>)</p>
<p>While scales are automatically added and cannot be removed, you can remove previously configured <i>maximum grade points</i>. <p>While scales are automatically added and cannot be removed, you can remove previously configured <i>maximum grade points</i>.
To do so, check the <b>delete</b> mark next to it, and press <b>save</b> </p> To do so, check the <b>delete</b> mark next to it, and press <b>save</b> </p>
<h2>Accessing studyplans in context</h2> <h2>Accessing studyplans in context</h2>
<p>The studyplan plugin is category context aware - meaning that every studyplan is placed in a context. And users can have rights to view and/or edit <p>The studyplan plugin is category context aware - meaning that every studyplan is placed in a context. And users can have rights to view and/or edit
studyplans within a specific context. </p> studyplans within a specific context. </p>
<p>To edit a studyplan in a specific context, you can use one or two ways:</p> <p>To edit a studyplan in a specific context, you can use one or two ways:</p>
<ol> <ol>
@ -83,14 +83,14 @@
<p> Viewing the studyplans of students is also a right that is context-aware. (Right <i>local/treestudyplan:viewuserreports</i>)</p> <p> Viewing the studyplans of students is also a right that is context-aware. (Right <i>local/treestudyplan:viewuserreports</i>)</p>
<p> Both methods outlined above also work for viewing student studyplans. The main difference is that the drop-down list on the top-left of the page <p> Both methods outlined above also work for viewing student studyplans. The main difference is that the drop-down list on the top-left of the page
only shows the categories that actually have studyplans that are viewable for the current user. (Which means that the user can not only see the study plan only shows the categories that actually have studyplans that are viewable for the current user. (Which means that the user can not only see the study plan
itself, but also the results of specific users). It is recommended to give this right to all teachers in a specific category, unless you have specific reasons itself, but also the results of specific users). It is recommended to give this right to all teachers in a specific category, unless you have specific reasons
why teachers might not be allowed to see all results of the students. why teachers might not be allowed to see all results of the students.
</p> </p>
<p><img src="img/Contexts-View.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Contexts-View.png" class="border border-primary rounded-lg ml-3"></p>
<h2>Block MyStudyplan</h2> <h2>Block MyStudyplan</h2>
<p>Using the block <i>My Studyplan</i> you can show students the studyplans they are enrolled in on the dashboard page. This may be useful for navigation.</p> <p>Using the block <i>My Studyplan</i> you can show students the studyplans they are enrolled in on the dashboard page. This may be useful for navigation.</p>
<p>This block shows <i>teachers</i> another view - namely all studyplans they are currently teaching courses in (orderd in tabs). For teachers, this view will often take a <p>This block shows <i>teachers</i> another view - namely all studyplans they are currently teaching courses in (orderd in tabs). For teachers, this view will often take a
few seconds to load, especialy if they teach in multiple study plans, since the result statistics for each course are collected each time</p> few seconds to load, especialy if they teach in multiple study plans, since the result statistics for each course are collected each time</p>
<p>In spite of the loading time, this view can be beneficial to teachers, since it can easily show them the statistics for the courses they are teaching</p> <p>In spite of the loading time, this view can be beneficial to teachers, since it can easily show them the statistics for the courses they are teaching</p>
@ -134,14 +134,14 @@
<td></td> <td></td>
<td>manager</td> <td>manager</td>
<td>System and/or category</td> <td>System and/or category</td>
</tr> </tr>
</table> </table>
<p>While the studyplan management rights are added by default to the <b>manager</b> role, the right to just view the studyplans for teachers and students is not <p>While the studyplan management rights are added by default to the <b>manager</b> role, the right to just view the studyplans for teachers and students is not
added to any default role. You should make a separate role for faculty that is allowed to view the study plans (or integrate it in an existing role), added to any default role. You should make a separate role for faculty that is allowed to view the study plans (or integrate it in an existing role),
grant the roght <i>local/treestudyplan:viewuserreports</i> to that role, and assign the role to all users that should have this right in a given context.</p> grant the roght <i>local/treestudyplan:viewuserreports</i> to that role, and assign the role to all users that should have this right in a given context.</p>
</div> </div>

View File

@ -16,12 +16,12 @@
<p>Basic configuration options for the studyplan plug-in</p> <p>Basic configuration options for the studyplan plug-in</p>
<ul> <ul>
<li><b>Default aggregation style</b><br> <li><b>Default aggregation style</b><br>
Select which aggregation style is selected by default upon creation of a new study plan. Setting this according to your preferences, will save you a Select which aggregation style is selected by default upon creation of a new study plan. Setting this according to your preferences, will save you a
few clicks when creating a lot of new study plans. few clicks when creating a lot of new study plans.
</li> </li>
<li><b>Course display name</b><br> <li><b>Course display name</b><br>
Select which course field is used as a short course name in the studyplan overview. You can choose between <i>shortname</i>, <i>ID</i> and Select which course field is used as a short course name in the studyplan overview. You can choose between <i>shortname</i>, <i>ID</i> and
any custom course field that you already have configured. It is recommended to create a custom course field specifically for the display name in any custom course field that you already have configured. It is recommended to create a custom course field specifically for the display name in
the studyplan, and put the appropriate text there.<br> the studyplan, and put the appropriate text there.<br>
<i>Shortname</i> is often not useful in the studyplan (especially on larger sites), since it requires prefixes/suffixes to ensure unique names. <i>Shortname</i> is often not useful in the studyplan (especially on larger sites), since it requires prefixes/suffixes to ensure unique names.
</li> </li>
@ -31,17 +31,17 @@
If you only use the <i>Moodle Course completion</i> method, you can safely ignore these parameters. If you only use the <i>Moodle Course completion</i> method, you can safely ignore these parameters.
</li> </li>
</ul> </ul>
<h3>Manage study plans</h3> <h3>Manage study plans</h3>
<p>This option is a direct link to the <b>manage studyplans</b> page. It may be useful for administrators.</p> <p>This option is a direct link to the <b>manage studyplans</b> page. It may be useful for administrators.</p>
<h3>Studyplan cohort sync</h3> <h3>Studyplan cohort sync</h3>
<p>Here you can configure automatic cohort syncing. This syncronizes the cohorts and students linked with a study plan to enrolment <p>Here you can configure automatic cohort syncing. This syncronizes the cohorts and students linked with a study plan to enrolment
within the courses in the study plan </p> within the courses in the study plan </p>
<p>Cohort syncing is done by creating a <i>cohort sync</i> enrolment method in each course, while students are synced by adding a <i>manual enrolment</i></p> <p>Cohort syncing is done by creating a <i>cohort sync</i> enrolment method in each course, while students are synced by adding a <i>manual enrolment</i></p>
<ul> <ul>
<li><b>Automatic Cohort sync</b><br> <li><b>Automatic Cohort sync</b><br>
Enable automatic cohort sync here. If enabled, a background process will automatically perform a synchronization each time a cohort is added or removed, Enable automatic cohort sync here. If enabled, a background process will automatically perform a synchronization each time a cohort is added or removed,
and each time a course is added. and each time a course is added.
</li> </li>
<li><b>Automatic deletion</b><br> <li><b>Automatic deletion</b><br>
@ -49,7 +49,7 @@
cohort to be enrolled. Note that individual student enrolments are never automatically deleted. cohort to be enrolled. Note that individual student enrolments are never automatically deleted.
</li> </li>
<li><b>Remember existing cohort syncs</b><br> <li><b>Remember existing cohort syncs</b><br>
Since there can only be one <i>cohort-sync</i> enrolment for each combination of course and cohort, existing cohort-sync enrolments are marked with the studyplans Since there can only be one <i>cohort-sync</i> enrolment for each combination of course and cohort, existing cohort-sync enrolments are marked with the studyplans
involved when a studyplan wants them as well. By checking this item, such existing cohort syncs are marked as <i>manual</i>, so they will not be automatically removed involved when a studyplan wants them as well. By checking this item, such existing cohort syncs are marked as <i>manual</i>, so they will not be automatically removed
when no study plans require them anymore when no study plans require them anymore
</li> </li>
@ -58,7 +58,7 @@
cohort. cohort.
</li> </li>
<li><b>Enrol linked users</b><br> <li><b>Enrol linked users</b><br>
You can choose to include linked users in the synchronization or not. Default is yes, since it would be unexpected behaviour if cohorts were automatically synced, You can choose to include linked users in the synchronization or not. Default is yes, since it would be unexpected behaviour if cohorts were automatically synced,
but users were not. but users were not.
</li> </li>
<li><b>Role</b><br> <li><b>Role</b><br>
@ -71,15 +71,15 @@
<h2>Theming guide</h2> <h2>Theming guide</h2>
<p>A number of colors in the studyplan plugin are based on the colors you define in your theme for <p>A number of colors in the studyplan plugin are based on the colors you define in your theme for
<b class="text-primary">primary (brand color)</b>, <b class="text-primary">primary (brand color)</b>,
<b class="text-secondary">secondary</b>, <b class="text-secondary">secondary</b>,
<b class="text-success">success</b>, <b class="text-success">success</b>,
<b class="text-info">info</b>, <b class="text-info">info</b>,
<b class="text-warning">warning</b> and <b class="text-warning">warning</b> and
<b class="text-danger">danger</b> <b class="text-danger">danger</b>
</p> </p>
<p>You may be able to define them in your theme's configuration. If not, follow the instructions below for <b>tweaking other colors</b> <p>You may be able to define them in your theme's configuration. If not, follow the instructions below for <b>tweaking other colors</b>
and use the following code as a starting point. (The values are the default boost theme colors)</p> and use the following code as a starting point. (The values are the default boost theme colors)</p>
<pre style="max-width: 30em;" class="p-3 border border-primary rounded-lg ml-3"> <pre style="max-width: 30em;" class="p-3 border border-primary rounded-lg ml-3">
:root { :root {
@ -91,8 +91,8 @@
--danger: #dc3545; --danger: #dc3545;
--light: #f8f9fa; --light: #f8f9fa;
--dark: #343a40; --dark: #343a40;
}</pre> }</pre>
<h3>Tweaking other colors</h3> <h3>Tweaking other colors</h3>
<p>There are some other custom css variables which you can tweak to change the styling. An overview:</p> <p>There are some other custom css variables which you can tweak to change the styling. An overview:</p>
<table> <table>
@ -121,7 +121,7 @@
--pending: var(--gray); --pending: var(--gray);
--incomplete: var(--gray); --incomplete: var(--gray);
}</pre> }</pre>
</div> </div>
</body> </body>

View File

@ -20,9 +20,9 @@
<p><img src="img/Newstudyplanscreen.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Newstudyplanscreen.png" class="border border-primary rounded-lg ml-3"></p>
<p>The screen allows you to specify the <b>full name</b>, <b>code</b> or short name and <b>description</b> for this specific plan</p> <p>The screen allows you to specify the <b>full name</b>, <b>code</b> or short name and <b>description</b> for this specific plan</p>
<p>You can also choose the category <b>context</b> in which to place this study plan. This is useful to manage access rights in a large moodle installation</p> <p>You can also choose the category <b>context</b> in which to place this study plan. This is useful to manage access rights in a large moodle installation</p>
<p>The next options give more information about the timing of the plan. The <b>number of slots</b> to place courses in the plan can be specified. <p>The next options give more information about the timing of the plan. The <b>number of slots</b> to place courses in the plan can be specified.
This is usually used to show the different periods in the academic year. You can expect this to become more explicit in upcoming versions of the plugin. This is usually used to show the different periods in the academic year. You can expect this to become more explicit in upcoming versions of the plugin.
The <b>start date</b> and <b>end date</b> of the plan help to show students and teachers which plans are current, and which were finished in the past. The <b>start date</b> and <b>end date</b> of the plan help to show students and teachers which plans are current, and which were finished in the past.
They will default to start on august 1 of this year and continue until august 1 of next year, since that matches most academic years.</p> They will default to start on august 1 of this year and continue until august 1 of next year, since that matches most academic years.</p>
<p>Finally we have the <b>aggregation style</b>, which is ised to determine how the courses show grades. There are a number of options available:</p> <p>Finally we have the <b>aggregation style</b>, which is ised to determine how the courses show grades. There are a number of options available:</p>
<ul> <ul>
@ -34,17 +34,17 @@
<p> <p>
With this aggregation method you manually select which grade items are used in the progress report. The design intent is to prepare a number of gradable items, With this aggregation method you manually select which grade items are used in the progress report. The design intent is to prepare a number of gradable items,
and describe the learning objectives for each course and use these to indicate completion. While any grade will be properly shown, it makes most sense to use a scale which indicates and describe the learning objectives for each course and use these to indicate completion. While any grade will be properly shown, it makes most sense to use a scale which indicates
if the student has either met the objective or not. The teacher or studyplan editor can also mark certain gradables/objectives as required or compulsory. if the student has either met the objective or not. The teacher or studyplan editor can also mark certain gradables/objectives as required or compulsory.
</p> </p>
<p>The final outcome for the courses will then be determined based on the percentage of met objectives, provided that all required/compulsory objectives are met</p> <p>The final outcome for the courses will then be determined based on the percentage of met objectives, provided that all required/compulsory objectives are met</p>
<p><img src="img/Newstudyplanscreen.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Newstudyplanscreen.png" class="border border-primary rounded-lg ml-3"></p>
<p>The added options allow you to set the <b>threshold</b> percentage of completed objectives in each course to determine the outcome as <i>excellent</i>, <i>good</i> <p>The added options allow you to set the <b>threshold</b> percentage of completed objectives in each course to determine the outcome as <i>excellent</i>, <i>good</i>
or <i>completed</i>. If an objective is not met it will be marked as either "progress" (meaning that the student's work needs to be improved) or or <i>completed</i>. If an objective is not met it will be marked as either "progress" (meaning that the student's work needs to be improved) or
"incomplete" (meaning that the student has not started working on this goal yet) </p> "incomplete" (meaning that the student has not started working on this goal yet) </p>
<p>When <i>failed</i> results are enabled, any "progress" will be marked as "failed" when the course end date has passed</p> <p>When <i>failed</i> results are enabled, any "progress" will be marked as "failed" when the course end date has passed</p>
<p>Finally, you can opt to have an object ive </p> <p>Finally, you can opt to have an object ive </p>
<a id="studylines"><h2>Setting up studylines</h2></a> <a id="studylines"><h2>Setting up studylines</h2></a>
<p>Before you can add courses to the studyplan, you have to define <i>study lines</i>. You use them to group together courses in logical categories. <p>Before you can add courses to the studyplan, you have to define <i>study lines</i>. You use them to group together courses in logical categories.
Most likely these are categories or themes within your curriculum.</p> Most likely these are categories or themes within your curriculum.</p>
<p>For performance reasons, study lines can only be edited when in <i>study line edit mode</i>. The courses and flow between them will be hidden, <p>For performance reasons, study lines can only be edited when in <i>study line edit mode</i>. The courses and flow between them will be hidden,
so the lines can be reshuffled and edited. You can toggle studyline edit mode with the toggle switch <i>edit stydy lines</i></p> so the lines can be reshuffled and edited. You can toggle studyline edit mode with the toggle switch <i>edit stydy lines</i></p>
@ -57,14 +57,14 @@
<p>Using the <i class="fa fa-pencil"></i> and <i class="fa fa-trash"></i> symbols you can edit and delete the studyline (only empty studylines can be deleted)</p> <p>Using the <i class="fa fa-pencil"></i> and <i class="fa fa-trash"></i> symbols you can edit and delete the studyline (only empty studylines can be deleted)</p>
<p><img src="img/Studylines-view.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Studylines-view.png" class="border border-primary rounded-lg ml-3"></p>
<div class="generalbox alert alert-success"><b>TIP:</b><br> <div class="generalbox alert alert-success"><b>TIP:</b><br>
If you have many studyplans using the same study line make-up you can export the first studyplan after you set up the study lines, and then import it on the next If you have many studyplans using the same study line make-up you can export the first studyplan after you set up the study lines, and then import it on the next
study plans.<br>(Use <b>Advanced</b> -> <b>Backup studyplan</b> to find the <i>backup</i> and <i>restore</i> features)</div> study plans.<br>(Use <b>Advanced</b> -> <b>Backup studyplan</b> to find the <i>backup</i> and <i>restore</i> features)</div>
<a id="studycourses"><h2>Adding courses</h2></a> <a id="studycourses"><h2>Adding courses</h2></a>
<p>To add courses, you need to exit <i>study line edit mode</i> and open the toolbox using the <button class="btn btn-primary">Open toolbox</button> button. <p>To add courses, you need to exit <i>study line edit mode</i> and open the toolbox using the <button class="btn btn-primary">Open toolbox</button> button.
The toolbox will now open on the right of the screen. (Using the toggle button on top, you can also switch it to the left of the screen if needed)</p> The toolbox will now open on the right of the screen. (Using the toggle button on top, you can also switch it to the left of the screen if needed)</p>
<p><img src="img/Toolbox-Courses.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Toolbox-Courses.png" class="border border-primary rounded-lg ml-3"></p>
<p>The toolbox shows you all the categories you have access to and the courses in them. Simply drag the course you want to include from the toolbox and <p>The toolbox shows you all the categories you have access to and the courses in them. Simply drag the course you want to include from the toolbox and
drop it in one of the slots in the studyplan that now appear.</p> drop it in one of the slots in the studyplan that now appear.</p>
<p><img src="img/Toolbox-dragcourse.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Toolbox-dragcourse.png" class="border border-primary rounded-lg ml-3"></p>
<p>To re-arrange the courses in the study plan, simply drag and drop them from the places they are now in.</p> <p>To re-arrange the courses in the study plan, simply drag and drop them from the places they are now in.</p>
@ -77,17 +77,17 @@
<p><img src="img/Studyplan-draglayers.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Studyplan-draglayers.png" class="border border-primary rounded-lg ml-3"></p>
<a id="periods"><h2>Configuring periods</h2></a> <a id="periods"><h2>Configuring periods</h2></a>
All periods in the study plan will have a label showing the timing and it's short name directly above their course columns in the All periods in the study plan will have a label showing the timing and it's short name directly above their course columns in the
timeline. Upon hovering over the short name, it's full name will appear.<br> timeline. Upon hovering over the short name, it's full name will appear.<br>
Also, the period matching the current date, will be highlighted in the color Also, the period matching the current date, will be highlighted in the color
indicated by the <pre style="display:inline;">--highlight</pre> css variable, which corresponds to the indicated by the <pre style="display:inline;">--highlight</pre> css variable, which corresponds to the
value of the <pre style="display:inline;">--info</pre> color by default. value of the <pre style="display:inline;">--info</pre> color by default.
<p></p> <p></p>
<p><img src="img/Periods-in-plan.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Periods-in-plan.png" class="border border-primary rounded-lg ml-3"></p>
<p><img src="img/Period-hover.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Period-hover.png" class="border border-primary rounded-lg ml-3"></p>
<p> At the start, the periods start and end times will be naively selected by splitting the study plan's duration among the number of periods.<br> <p> At the start, the periods start and end times will be naively selected by splitting the study plan's duration among the number of periods.<br>
By clicking on the <i class="text-primary fa fa-pencil"></i> icon, you can open the edit screen and change the period's <i>full name</i>, By clicking on the <i class="text-primary fa fa-pencil"></i> icon, you can open the edit screen and change the period's <i>full name</i>,
<i>short name</i>, <i>start time</i> and <i>end time</i> <i>short name</i>, <i>start time</i> and <i>end time</i>
</p> </p>
<p><img src="img/Period-edit.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Period-edit.png" class="border border-primary rounded-lg ml-3"></p>
@ -97,26 +97,26 @@
</div> </div>
<a id="studycourses"><h2>Configuring courses</h2></a> <a id="studycourses"><h2>Configuring courses</h2></a>
<p>Once a course is added to the studyplan, an icon shows if it needs to be configured (<i class='fa fa-exclamation-circle text-warning'></i>) or <p>Once a course is added to the studyplan, an icon shows if it needs to be configured (<i class='fa fa-exclamation-circle text-warning'></i>) or
is already configured (<i class='fa fa-check text-success'></i>).</p> is already configured (<i class='fa fa-check text-success'></i>).</p>
<p>By clicking on the course title (it's shortname, or a configured custom field), you can view the details, and configure it where applicable. <p>By clicking on the course title (it's shortname, or a configured custom field), you can view the details, and configure it where applicable.
The configuration is dependent on the aggregation method for the study plan:</p> The configuration is dependent on the aggregation method for the study plan:</p>
<h3>Moodle course completion</h3> <h3>Moodle course completion</h3>
<p>When using moodle course completion as a base, all configuration is done in the <i>course's completion settings</i>. <p>When using moodle course completion as a base, all configuration is done in the <i>course's completion settings</i>.
The course details show a <i class='fa fa-gear primary'></i> gear icon in the title, which takes you directly to the course completion configuration. The course details show a <i class='fa fa-gear primary'></i> gear icon in the title, which takes you directly to the course completion configuration.
Once it is configured, the course details will show you exactly how the course completion is configured</p> Once it is configured, the course details will show you exactly how the course completion is configured</p>
<p><img src="img/Course-config-corecompletion.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Course-config-corecompletion.png" class="border border-primary rounded-lg ml-3"></p>
<h3>Manual: Completed + required goals</h3> <h3>Manual: Completed + required goals</h3>
<p> In this mode, the course details show you all the gradable activities in the course. You need to select which of them will be used to determine progress <p> In this mode, the course details show you all the gradable activities in the course. You need to select which of them will be used to determine progress
and will be shown to the students and teachers in the studyplan view. You can also mark gradables ad <i>required</i>. Gradable activities marked as and will be shown to the students and teachers in the studyplan view. You can also mark gradables ad <i>required</i>. Gradable activities marked as
such will all need to be completed for the course to be marked as completed, regardless of the minimum percentage configured</p> such will all need to be completed for the course to be marked as completed, regardless of the minimum percentage configured</p>
<p><img src="img/Course-config-bistate.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Course-config-bistate.png" class="border border-primary rounded-lg ml-3"></p>
<h3>Course start and end times</h3> <h3>Course start and end times</h3>
<p> Often, the course will have their start and end times configured differently than the period you drop them in. If this is the case, <p> Often, the course will have their start and end times configured differently than the period you drop them in. If this is the case,
a screen will appear asking if you want to update the course's start and end times. The time details of both course and period will a screen will appear asking if you want to update the course's start and end times. The time details of both course and period will
be shown for your comparison. By clicking <button class="btn btn-danger">Yes</button>, the course's start and end times will be be shown for your comparison. By clicking <button class="btn btn-danger">Yes</button>, the course's start and end times will be
updated to those of the period. Any activity dates set will be adjusted according to the changed start date.</p> updated to those of the period. Any activity dates set will be adjusted according to the changed start date.</p>
<p><img src="img/Course-timing-mismatch-screen.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Course-timing-mismatch-screen.png" class="border border-primary rounded-lg ml-3"></p>
<div class="generalbox alert alert-info"><b>NOTE:</b><br> <div class="generalbox alert alert-info"><b>NOTE:</b><br>
@ -132,7 +132,7 @@
Do note however, that the available spans are limited by the available space to the right of the course in the studyplan</p> Do note however, that the available spans are limited by the available space to the right of the course in the studyplan</p>
<p><img src="img/Course-timing-indicators.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Course-timing-indicators.png" class="border border-primary rounded-lg ml-3"></p>
<p> Upon selecting the span you want, the course display will immediately be updated to reflect the changes.<br> <p> Upon selecting the span you want, the course display will immediately be updated to reflect the changes.<br>
immediately after this, you will see the screen asking if you want to change the course start and end times to reflect the immediately after this, you will see the screen asking if you want to change the course start and end times to reflect the
new period span.</p> new period span.</p>
<a id="studybadges"><h2>Adding badges</h2></a> <a id="studybadges"><h2>Adding badges</h2></a>
@ -142,7 +142,7 @@
<a id="flow"><h2>Drawing the flow</h2></a> <a id="flow"><h2>Drawing the flow</h2></a>
<p>You can draw arrows between the courses and add flow indicators to show students how different courses are related in the studyplan</p> <p>You can draw arrows between the courses and add flow indicators to show students how different courses are related in the studyplan</p>
<div class="generalbox alert alert-success"><b>TIP:</b><br> <div class="generalbox alert alert-success"><b>TIP:</b><br>
While there are many possible relations between courses, it is best to limit the arrows to the most obvious relations. The studyplan can While there are many possible relations between courses, it is best to limit the arrows to the most obvious relations. The studyplan can
easily become too crowded to give a good overview. Use <i>junctions</i> to combine multiple arrows into one if you do need to show the relation easily become too crowded to give a good overview. Use <i>junctions</i> to combine multiple arrows into one if you do need to show the relation
from many courses to a few.</div> from many courses to a few.</div>
<h3>Drawing arrows</h3> <h3>Drawing arrows</h3>
@ -152,7 +152,7 @@
</p> </p>
<p><img src="img/Arrows-drag.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Arrows-drag.png" class="border border-primary rounded-lg ml-3"></p>
<h3>Removing arrows</h3> <h3>Removing arrows</h3>
<p> To remove an arrow, click on the green square where the arrow starts. A red <i class='fa fa-trash text-danger'></i> trash can icon will appear for <p> To remove an arrow, click on the green square where the arrow starts. A red <i class='fa fa-trash text-danger'></i> trash can icon will appear for
each line that starts there. Hover over the trash can icons to find the line you want to remove. The arrow for each trash can icon will be highlighted in red each line that starts there. Hover over the trash can icons to find the line you want to remove. The arrow for each trash can icon will be highlighted in red
once you hover over it with your cursor. Once you find the arrow you want to remove, click the trash icon.</p> once you hover over it with your cursor. Once you find the arrow you want to remove, click the trash icon.</p>
<p> To close the trash icon menu without removing an arrow, click the green square (now red) again.</p> <p> To close the trash icon menu without removing an arrow, click the green square (now red) again.</p>
@ -165,7 +165,7 @@
<li><b>Start/continue</b> These indicators are always green and can be used to indicate the start of a series of courses</li> <li><b>Start/continue</b> These indicators are always green and can be used to indicate the start of a series of courses</li>
</ul> </ul>
<p><img src="img/Toolbox-flow.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Toolbox-flow.png" class="border border-primary rounded-lg ml-3"></p>
<p> By clicking on the <i class="fa fa-gear"></i> icon in the junction and finish indicators, you can select how the incoming arrows will be combined. <p> By clicking on the <i class="fa fa-gear"></i> icon in the junction and finish indicators, you can select how the incoming arrows will be combined.
The indicated numer of arrows need to be completed in order to show the indicator as completed</p> The indicated numer of arrows need to be completed in order to show the indicator as completed</p>
<ul> <ul>
<li><b>All entries</b></li> <li><b>All entries</b></li>
@ -181,7 +181,7 @@
<p>The recommended way is association by cohort, since that allows to dynamically allocate users to courses and studyplans by allocatong them to cohorts. <p>The recommended way is association by cohort, since that allows to dynamically allocate users to courses and studyplans by allocatong them to cohorts.
Assigning students to a studyplan individually is only useful in some fringe cases. Assigning students to a studyplan individually is only useful in some fringe cases.
</p> </p>
<p>To edit associations, click on the <span class="text-primary"><i class="fa fa-users"></i> associations</span> link in the studyplan, <p>To edit associations, click on the <span class="text-primary"><i class="fa fa-users"></i> associations</span> link in the studyplan,
or click on the <i class="fa fa-users text-primary"></i> icon in the studyplan overview.</p> or click on the <i class="fa fa-users text-primary"></i> icon in the studyplan overview.</p>
<p>You will now get the association window</p> <p>You will now get the association window</p>
<p><img src="img/Cohort-link.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Cohort-link.png" class="border border-primary rounded-lg ml-3"></p>
@ -192,14 +192,14 @@
<p>In order to link <i>individual students</i>, click <i>students</i> and follow the same procedure as lined out above for cohorts.</p> <p>In order to link <i>individual students</i>, click <i>students</i> and follow the same procedure as lined out above for cohorts.</p>
<div class="generalbox alert alert-warning"><b>IMPORTANT:</b><br> <div class="generalbox alert alert-warning"><b>IMPORTANT:</b><br>
<p>If you have cascading cohort sync enabled (see configuration), the system will automatically create a cohort-sync enrolment to all courses in the studyplan <p>If you have cascading cohort sync enabled (see configuration), the system will automatically create a cohort-sync enrolment to all courses in the studyplan
for each cohort you have linked here. It can also automatically remove those enrolments when a cohort is unlinked from a studyplan. for each cohort you have linked here. It can also automatically remove those enrolments when a cohort is unlinked from a studyplan.
</p> </p>
<p>Individually linked students can also be cascaded down to the courses (if the feature is enabled). In those cases a manual enrolment is created for the users <p>Individually linked students can also be cascaded down to the courses (if the feature is enabled). In those cases a manual enrolment is created for the users
in each of the studyplan's courses. Those enrolments will never be automatically removed, since that might deprive a student of study content. in each of the studyplan's courses. Those enrolments will never be automatically removed, since that might deprive a student of study content.
</p> </p>
</div> </div>
</div> </div>
</body> </body>

View File

@ -19,7 +19,7 @@
<a id="#postinstall"><h2>Configuration post install</h2></a> <a id="#postinstall"><h2>Configuration post install</h2></a>
<p>The studyplan plugin will automatically add links to the flat navigation bat used in Moodle 3.11 and below<p> <p>The studyplan plugin will automatically add links to the flat navigation bat used in Moodle 3.11 and below<p>
<p>However, Moodle 4.0-4.2 use a new "primary navigation" bar, and does not yet support customizing the primary navigation bar (it is a planned feature though)</p> <p>However, Moodle 4.0-4.2 use a new "primary navigation" bar, and does not yet support customizing the primary navigation bar (it is a planned feature though)</p>
<p><strong>Use the following workaround:</strong></p> <p><strong>Use the following workaround:</strong></p>
<p>You can add custom primary menu items in <b>Site administration</b> -> <b>Appearance</b>-> <b>Theme settings</b> <p>You can add custom primary menu items in <b>Site administration</b> -> <b>Appearance</b>-> <b>Theme settings</b>
That page contains an item called Custom menu items". Add the following into that area to custimize it That page contains an item called Custom menu items". Add the following into that area to custimize it
@ -34,7 +34,7 @@ Manage Study plans|/local/treestudyplan/edit-plan.php||en
</pre></p> </pre></p>
<p> The studyplan plugin wil automatically hide the primary navigation items that are not applicable for the current user.</p> <p> The studyplan plugin wil automatically hide the primary navigation items that are not applicable for the current user.</p>
<h2>Roles post install</h2> <h2>Roles post install</h2>
<p>While the studyplan management rights are added by default to the <b>manager</b> role, the right to just view the studyplans for teachers and students is not <p>While the studyplan management rights are added by default to the <b>manager</b> role, the right to just view the studyplans for teachers and students is not
added to any default role. You should make a separate role for faculty that is allowed to view the study plans (or integrate it in an existing role), added to any default role. You should make a separate role for faculty that is allowed to view the study plans (or integrate it in an existing role),
grant the roght <i>local/treestudyplan:viewuserreports</i> to that role, and assign the role to all users that should have this right in a given context.</p> grant the roght <i>local/treestudyplan:viewuserreports</i> to that role, and assign the role to all users that should have this right in a given context.</p>
<p>See <a href="advanced.htm#rights"><b>Advanced</b> -> <b>Roles and permissions</b></a> for more details</p> <p>See <a href="advanced.htm#rights"><b>Advanced</b> -> <b>Roles and permissions</b></a> for more details</p>
@ -62,7 +62,7 @@ Manage Study plans|/local/treestudyplan/edit-plan.php||en
</div> </div>
</li> </li>
</ul> </ul>
<p> Then assign the role <b>studyplanmanager</b> in a specific category context, or the system context to all users who should be able to create and edit studyplans <p> Then assign the role <b>studyplanmanager</b> in a specific category context, or the system context to all users who should be able to create and edit studyplans
in that specific context</p> in that specific context</p>
<p> Assign the role <b>studyplanviewer</b> to all users who should be able to view the teacher view of the studyplan and view the specific results of users associated <p> Assign the role <b>studyplanviewer</b> to all users who should be able to view the teacher view of the studyplan and view the specific results of users associated
with that studyplan. </p> with that studyplan. </p>

View File

@ -31,7 +31,7 @@
<p>The colors can be tweaked by changing the CSS vars <i>--past</i>, <i>--present</i> and <i>--future</i></p> <p>The colors can be tweaked by changing the CSS vars <i>--past</i>, <i>--present</i> and <i>--future</i></p>
<p>These same colors are also used to indicate the timing status of courses in the studyplan view itself</p> <p>These same colors are also used to indicate the timing status of courses in the studyplan view itself</p>
</div> </div>
<h2>Statistics view</h2> <h2>Statistics view</h2>
<p>The statistics view shows the studyplan in its entirety, but shows the completion statistics for each course or badge</p> <p>The statistics view shows the studyplan in its entirety, but shows the completion statistics for each course or badge</p>
<p><img src="img/Teacherview-statistics-badge-course.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Teacherview-statistics-badge-course.png" class="border border-primary rounded-lg ml-3"></p>
@ -64,7 +64,7 @@
<p>When using a manual aggregation method instead of the course completion method, the view is slightly different.</p> <p>When using a manual aggregation method instead of the course completion method, the view is slightly different.</p>
<p>Here, only the gradable activities for this course are shown.</p> <p>Here, only the gradable activities for this course are shown.</p>
<p><img src="img/Teacherview-course-manual.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Teacherview-course-manual.png" class="border border-primary rounded-lg ml-3"></p>
<h2>Badge details</h2> <h2>Badge details</h2>
<p>By clicking on the badge, more information about it are shown, like <b>description</b>, <b>award criteria</b> and a link to the <b>badge details</b> </p> <p>By clicking on the badge, more information about it are shown, like <b>description</b>, <b>award criteria</b> and a link to the <b>badge details</b> </p>
<p> A progress bar is also shown, indicating how many students in the study plan have earned this badge</p> <p> A progress bar is also shown, indicating how many students in the study plan have earned this badge</p>
@ -79,7 +79,7 @@
<p> This opens a drawer to the right of the screen, where the teacher can select any of the students linked to this study plan and view their results</p> <p> This opens a drawer to the right of the screen, where the teacher can select any of the students linked to this study plan and view their results</p>
<p>This view is identical to the view that the students themselves see when viewing their study plan.</p> <p>This view is identical to the view that the students themselves see when viewing their study plan.</p>
<p><img src="img/Teacherview-choose-student.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Teacherview-choose-student.png" class="border border-primary rounded-lg ml-3"></p>
<p>By selecting <button type="button" class="list-group-item list-group-item-primary list-group-item-action">Teacher view</button>, the teacher can <p>By selecting <button type="button" class="list-group-item list-group-item-primary list-group-item-action">Teacher view</button>, the teacher can
return to the teacher view</p> return to the teacher view</p>
</div> </div>

View File

@ -35,7 +35,7 @@
<td>Completed Passed</td> <td>Completed Passed</td>
<td class="text-center"><i title="Completed" style="float: none;" class="r-course-result fa fa-check-circle r-completion-completed"></i></td> <td class="text-center"><i title="Completed" style="float: none;" class="r-course-result fa fa-check-circle r-completion-completed"></i></td>
<td>Bootstrap color variable <i>--success</i></td> <td>Bootstrap color variable <i>--success</i></td>
</tr> </tr>
<tr> <tr>
<td>Good</td> <td>Good</td>
<td class="text-center"><i title="Good" style="float: none;" class="r-course-result fa fa-check-circle r-completion-good"></i></td> <td class="text-center"><i title="Good" style="float: none;" class="r-course-result fa fa-check-circle r-completion-good"></i></td>
@ -57,12 +57,12 @@
<td>Failed</td> <td>Failed</td>
<td class="text-center"><i title="In progress" style="float: none;" class="r-course-result fa fa-times-circle r-completion-failed"></i></td> <td class="text-center"><i title="In progress" style="float: none;" class="r-course-result fa fa-times-circle r-completion-failed"></i></td>
<td>Bootstrap color variable <i>--danger</i></td> <td>Bootstrap color variable <i>--danger</i></td>
</tr> </tr>
<tr> <tr>
<td>Not started</td> <td>Not started</td>
<td class="text-center"><i title="Not started" style="float: none;" class="r-course-result fa fa-circle-o r-completion-incomplete"></i></td> <td class="text-center"><i title="Not started" style="float: none;" class="r-course-result fa fa-circle-o r-completion-incomplete"></i></td>
<td>Color variable <i>--incomplete</i></td> <td>Color variable <i>--incomplete</i></td>
</tr> </tr>
</table> </table>
<br> <br>
<p> As you can see, the used colors depend on your theme. The configured colors for <i>success</i>, <i>warning</i>, <i>danger</i> and <i>info</i> are all <p> As you can see, the used colors depend on your theme. The configured colors for <i>success</i>, <i>warning</i>, <i>danger</i> and <i>info</i> are all
@ -71,13 +71,13 @@
</p> </p>
<p>The colored tabs in front of each course show if the course is currently given (blue), in the past (purple) or in the future (grey)</p> <p>The colored tabs in front of each course show if the course is currently given (blue), in the past (purple) or in the future (grey)</p>
<p> By coloring the arrows in the color of the progress, both students and teachers can quickly see a student's progress in a course</p> <p> By coloring the arrows in the color of the progress, both students and teachers can quickly see a student's progress in a course</p>
<p> Study plans that use the <i>Moodle course completion</i> aggregation method show a small circle that shows the progress percentage of all requirements, <p> Study plans that use the <i>Moodle course completion</i> aggregation method show a small circle that shows the progress percentage of all requirements,
instead of an icon</p> instead of an icon</p>
<h2>Course detail view</h2> <h2>Course detail view</h2>
By clicking on a course, a student can view more details about this course. By clicking on a course, a student can view more details about this course.
<p>By clicking on the <b>course title</b>, or any of the <b>activities</b> the student is directly sent to the course or activity page, in a new tab.</p> <p>By clicking on the <b>course title</b>, or any of the <b>activities</b> the student is directly sent to the course or activity page, in a new tab.</p>
<h3>Moodle course completion view</h3> <h3>Moodle course completion view</h3>
<p><img src="img/Student-coursedetails-corecompletion.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Student-coursedetails-corecompletion.png" class="border border-primary rounded-lg ml-3"></p>
<p>Per completion requirement, it is shown whether or not the student has <b>completed</b>, <b>passed</b> or <b>failed</b> the requirement.</p> <p>Per completion requirement, it is shown whether or not the student has <b>completed</b>, <b>passed</b> or <b>failed</b> the requirement.</p>
@ -92,7 +92,7 @@
<p><img src="img/Student-coursedetails-manual.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Student-coursedetails-manual.png" class="border border-primary rounded-lg ml-3"></p>
<p>Per selected activity, it is indicated if the student has completed this activity or not</p> <p>Per selected activity, it is indicated if the student has completed this activity or not</p>
<p>Until the course end date has passed, the end status is marked as <b>progress</b> as long as one or more requirements are met or graded. <p>Until the course end date has passed, the end status is marked as <b>progress</b> as long as one or more requirements are met or graded.
<i>After</i> the course end date has passed, ther course is indicated as <b>failed</b> if the completion requirements are not met <i>After</i> the course end date has passed, ther course is indicated as <b>failed</b> if the completion requirements are not met
<b>and the failed option is enabled</b> in the studplan configuration</b></p> <b>and the failed option is enabled</b> in the studplan configuration</b></p>
<p>If a teacher left feedback for an activity, the link <i>View feedback</i> is shownand shows the specific feedback upon clicking it.</p> <p>If a teacher left feedback for an activity, the link <i>View feedback</i> is shownand shows the specific feedback upon clicking it.</p>
<p>(This is due to the specific grading requirements of the first school to request the study plan plugin)</p> <p>(This is due to the specific grading requirements of the first school to request the study plan plugin)</p>
@ -105,9 +105,9 @@
<p>The student first enters the <i>Invite management</i> page where they can add, remove, edit or re-send invitations</p> <p>The student first enters the <i>Invite management</i> page where they can add, remove, edit or re-send invitations</p>
<p><img src="img/Share-studyplan-manage.png" class="border border-primary rounded-lg ml-3"></p> <p><img src="img/Share-studyplan-manage.png" class="border border-primary rounded-lg ml-3"></p>
<p>By clicking the <i class="fa fa-eye text-primary"></i> icon, the student can verify how their invitees see the studyplan</p> <p>By clicking the <i class="fa fa-eye text-primary"></i> icon, the student can verify how their invitees see the studyplan</p>
<p>the other icons are respecively <p>the other icons are respecively
<i class="fa fa-envelope text-primary">re-send mail</i>, <i class="fa fa-envelope text-primary">re-send mail</i>,
<i class="fa fa-pencil text-primary">edit</i> and <i class="fa fa-pencil text-primary">edit</i> and
<i class="fa fa-trash text-primary">delete</i></p> <i class="fa fa-trash text-primary">delete</i></p>
<p>When starting a new invite, the student is asked to provide the <i>name</i> and <i>email</i> of the person to send the invitation to</p> <p>When starting a new invite, the student is asked to provide the <i>name</i> and <i>email</i> of the person to send the invitation to</p>

View File

@ -75,7 +75,7 @@ if ($studyplancontext->id > 1) {
// Load javascripts and specific css. // Load javascripts and specific css.
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
if($CFG->debugdeveloper){ if ($CFG->debugdeveloper) {
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
} }
$PAGE->requires->js_call_amd('local_treestudyplan/page-edit-plan', 'init', [$studyplancontext->id, $categoryid]); $PAGE->requires->js_call_amd('local_treestudyplan/page-edit-plan', 'init', [$studyplancontext->id, $categoryid]);
@ -141,7 +141,12 @@ print $OUTPUT->header();
</div> </div>
<div class='t-studyplan-container'> <div class='t-studyplan-container'>
<t-studyplan v-if='activestudyplan' v-model='activestudyplan' @moved="movedStudyplan" @toggletoolbox="toggletoolbox"></t-studyplan> <t-studyplan
v-if='activestudyplan'
v-model='activestudyplan'
@moved="movedStudyplan"
@toggletoolbox="toggletoolbox"
></t-studyplan>
<div v-else-if='loadingstudyplan' class="spinner-border text-primary" role="status"> <div v-else-if='loadingstudyplan' class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span> <span class="sr-only">Loading...</span>
</div> </div>

View File

@ -65,7 +65,7 @@ if (empty($invite)) {
} else { } else {
// Load javascripts and specific css. // Load javascripts and specific css.
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
if($CFG->debugdeveloper){ if ($CFG->debugdeveloper) {
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
} }
$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init', ['invited', $invitekey]); $PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init', ['invited', $invitekey]);

26
lib.php
View File

@ -42,6 +42,10 @@ function local_treestudyplan_unit_get_editor_options(context $context) {
'trusttext' => 0]; 'trusttext' => 0];
} }
/**
* Create primary navigation links for studyplan if needed
*/
function local_treestudyplan_autofill_customusermenuitems() { function local_treestudyplan_autofill_customusermenuitems() {
if (get_config("local_treestudyplan", "primary_nav_autofill")) { if (get_config("local_treestudyplan", "primary_nav_autofill")) {
$lang = current_language(); $lang = current_language();
@ -51,12 +55,12 @@ function local_treestudyplan_autofill_customusermenuitems() {
"/local/treestudyplan/edit-plan.php" => ["included" => false, "strkey" => "link_editplan"], "/local/treestudyplan/edit-plan.php" => ["included" => false, "strkey" => "link_editplan"],
]; ];
// Load the custom menu items from config // Load the custom menu items from config.
$custommenuitems = get_config("core", "custommenuitems"); $custommenuitems = get_config("core", "custommenuitems");
// Scan through all the lines to see if it is a link to one of our nav items in the current language. // Scan through all the lines to see if it is a link to one of our nav items in the current language.
$lines = explode("\n", $custommenuitems); $lines = explode("\n", $custommenuitems);
//debugging("\n\nLines: ". print_r($lines,true)."\n");
$links = array_keys($navitems); $links = array_keys($navitems);
foreach ($lines as $line) { foreach ($lines as $line) {
$parms = explode('|', $line); $parms = explode('|', $line);
@ -69,13 +73,13 @@ function local_treestudyplan_autofill_customusermenuitems() {
} }
} }
// List through all the links to see if we need to add one or more // List through all the links to see if we need to add one or more.
foreach($navitems as $link => $details){ foreach ($navitems as $link => $details) {
if (!$details["included"]) { if (!$details["included"]) {
$line = implode("|",[ $line = implode("|", [
get_string($details["strkey"],"local_treestudyplan"), // Menu text. get_string($details["strkey"], "local_treestudyplan"), // Menu text.
$link, // Link. $link, // Link.
'', // Tooltip, '', // Tooltip.
$lang, // Language code. $lang, // Language code.
" #Automatically added by studyplan plugin. See setting 'primary_nav_autofill' to disable this" " #Automatically added by studyplan plugin. See setting 'primary_nav_autofill' to disable this"
]); ]);
@ -83,8 +87,8 @@ function local_treestudyplan_autofill_customusermenuitems() {
} }
} }
// Store the modified custom menu items. // Store the modified custom menu items.
set_config("custommenuitems",$custommenuitems); set_config("custommenuitems", $custommenuitems);
} }
} }
@ -113,8 +117,8 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
We will add all the hrefs that should be hidden to this variable below. We will add all the hrefs that should be hidden to this variable below.
*/ */
/* /*
In addition, the function local_treestudyplan_autofill_customusermenuitems() called below will In addition, the function local_treestudyplan_autofill_customusermenuitems() called below will
automatically generate the required lines if they are missing... automatically generate the required lines if they are missing...
*/ */
local_treestudyplan_autofill_customusermenuitems(); local_treestudyplan_autofill_customusermenuitems();

View File

@ -38,7 +38,7 @@ $PAGE->set_heading(get_string('report_invited', 'local_treestudyplan', "{$USER->
// Load javascripts and specific css. // Load javascripts and specific css.
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
if($CFG->debugdeveloper){ if ($CFG->debugdeveloper) {
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
} }
$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init'); $PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init');

View File

@ -46,7 +46,7 @@ if ($teachermode) {
// Load javascripts and specific css. // Load javascripts and specific css.
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
if($CFG->debugdeveloper){ if ($CFG->debugdeveloper) {
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
} }
$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init', [$teachermode ? 'teaching' : 'myreport']); $PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init', [$teachermode ? 'teaching' : 'myreport']);

View File

@ -4,5 +4,5 @@
} }
} }

View File

@ -12,5 +12,5 @@
--excellent: var(--blue); --excellent: var(--blue);
--pending: var(--gray); --pending: var(--gray);
--incomplete: var(--gray); --incomplete: var(--gray);
} }

View File

@ -1,4 +1,4 @@
.path-local-treestudyplan, .features-treestudyplan { .path-local-treestudyplan, .features-treestudyplan {
div.tab-pane:target { div.tab-pane:target {
margin-top: 0; margin-top: 0;
} }

View File

@ -1,5 +1,5 @@
The amount of css rules required for this plugin is quite high. To accomodate the The amount of css rules required for this plugin is quite high. To accomodate the
moodle plugin practice of plugin specific css selectors, this makes the css rules moodle plugin practice of plugin specific css selectors, this makes the css rules
quite hard to maintain. quite hard to maintain.
Unfortunately, moodle does not extend the sass/scss compiler support to plugins Unfortunately, moodle does not extend the sass/scss compiler support to plugins
@ -12,7 +12,7 @@ to compile 'scss/styles.scss' into 'css/devstyles.css' (and 'styles.css')
cache every time a style rule is changed during development. cache every time a style rule is changed during development.
Once I devise a method to use styles.css in production environtments and css/devstyles.css Once I devise a method to use styles.css in production environtments and css/devstyles.css
production environments, the compiler will compile it into both css/devstyles.css production environments, the compiler will compile it into both css/devstyles.css
and styles.css and styles.css
The grunt command to compile is 'grunt scssplugin' The grunt command to compile is 'grunt scssplugin'

View File

@ -16,7 +16,7 @@
.t-studyplan-headings, .t-studyplan-headings,
.r-studyplan-headings { .r-studyplan-headings {
display: block; display: block;
} }
.t-studyplan-wrapper, .t-studyplan-wrapper,
@ -50,10 +50,10 @@
.r-studyplan-scrollable::-webkit-scrollbar-track { .r-studyplan-scrollable::-webkit-scrollbar-track {
background: color-mix(in srgb, var(--primary) 20%, white); background: color-mix(in srgb, var(--primary) 20%, white);
} }
.t-studyplan-scrollable::-webkit-scrollbar-thumb, .t-studyplan-scrollable::-webkit-scrollbar-thumb,
.r-studyplan-scrollable::-webkit-scrollbar-thumb { .r-studyplan-scrollable::-webkit-scrollbar-thumb {
background:var(--primary); background:var(--primary);
} }
.t-studyplan-column-heading, .t-studyplan-column-heading,
@ -121,7 +121,7 @@
} }
.t-studyplan-controlbox .control { .t-studyplan-controlbox .control {
margin-left: 10px; margin-left: 10px;
margin-right: 5px; margin-right: 5px;
} }
@ -1059,10 +1059,10 @@
.s-required.complete { .s-required.complete {
color: var(--info); color: var(--info);
} }
.s-required.complete-pass, .s-required.complete-pass,
.s-required.good, .s-required.good,
.s-required.excellent, .s-required.excellent,
.s-required.allgraded { .s-required.allgraded {
color: var(--success); color: var(--success);
@ -1078,14 +1078,14 @@
} }
.r-tooltip.warning .arrow::before { .r-tooltip.warning .arrow::before {
border-right-color: color-mix(in srgb, var(--warning) 60%, #000); border-right-color: color-mix(in srgb, var(--warning) 60%, #000);
} }
.r-tooltip.info .tooltip-inner .r-tooltip.info .tooltip-inner
{ {
background-color: color-mix(in srgb, var(--info) 60%, #000); background-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.r-tooltip.info .arrow::before { .r-tooltip.info .arrow::before {
border-right-color: color-mix(in srgb, var(--info) 60%, #000); border-right-color: color-mix(in srgb, var(--info) 60%, #000);
} }
.r-tooltip.incomplete .tooltip-inner, .r-tooltip.incomplete .tooltip-inner,
.r-tooltip.complete-fail .tooltip-inner, .r-tooltip.complete-fail .tooltip-inner,
@ -1133,10 +1133,10 @@
margin-right: 1em; margin-right: 1em;
} }
.s-edit-mod-form [data-fieldtype=submit] { .s-edit-mod-form [data-fieldtype=submit] {
/* if not working, make selector more specific */ /* if not working, make selector more specific */
display: none; display: none;
} }
.s-edit-mod-form.genericonly form > fieldset:not(#id_general) { .s-edit-mod-form.genericonly form > fieldset:not(#id_general) {
/* if not working, make selector more specific */ /* if not working, make selector more specific */
display: none; display: none;

View File

@ -42,12 +42,12 @@ if ($hassiteconfig) {
$page = new admin_settingpage('local_treestudyplan_settings', $page = new admin_settingpage('local_treestudyplan_settings',
get_string('settingspage', 'local_treestudyplan', null, true)); get_string('settingspage', 'local_treestudyplan', null, true));
// NAVIGATION // NAVIGATION.
$page->add(new admin_setting_heading('local_treestudyplan/navigation_heading', $page->add(new admin_setting_heading('local_treestudyplan/navigation_heading',
get_string('setting_navigation_heading', 'local_treestudyplan'), get_string('setting_navigation_heading', 'local_treestudyplan'),
get_string('settingdesc_navigation_heading', 'local_treestudyplan') get_string('settingdesc_navigation_heading', 'local_treestudyplan')
)); ));
$page->add(new admin_setting_configcheckbox('local_treestudyplan/primary_nav_autofill', $page->add(new admin_setting_configcheckbox('local_treestudyplan/primary_nav_autofill',
get_string('setting_primary_nav_autofill', 'local_treestudyplan'), get_string('setting_primary_nav_autofill', 'local_treestudyplan'),
get_string('settingdesc_primary_nav_autofill', 'local_treestudyplan'), get_string('settingdesc_primary_nav_autofill', 'local_treestudyplan'),

View File

@ -24,7 +24,7 @@
<copyright>2016-2023 - BootstrapVue</copyright> <copyright>2016-2023 - BootstrapVue</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/portal-vue/</location> <location>amd/src/portal-vue/</location>
<name>Portal-Vue.js</name> <name>Portal-Vue.js</name>
@ -36,7 +36,7 @@
<copyright>2021 Thorsten Lünborg</copyright> <copyright>2021 Thorsten Lünborg</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/vue</location> <location>amd/src/vue</location>
<name>Vue</name> <name>Vue</name>
@ -48,8 +48,8 @@
<copyright>2014-2022 Evan You</copyright> <copyright>2014-2022 Evan You</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/vue-easy-dnd/vue-easy-dnd.esm.js</location> <location>amd/src/vue-easy-dnd/vue-easy-dnd.esm.js</location>
<name>Vue-Easy-DnD</name> <name>Vue-Easy-DnD</name>
@ -61,7 +61,7 @@
<copyright>2019 Régis Lemaigre</copyright> <copyright>2019 Régis Lemaigre</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/vue-easy-dnd/reflect-metadata.js</location> <location>amd/src/vue-easy-dnd/reflect-metadata.js</location>
<name>reflect-metadata</name> <name>reflect-metadata</name>
@ -76,7 +76,7 @@
<copyright>William Buchwalter</copyright> <copyright>William Buchwalter</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/vue-easy-dnd/vue-class-component.js</location> <location>amd/src/vue-easy-dnd/vue-class-component.js</location>
<name>vue-class-component</name> <name>vue-class-component</name>
@ -88,7 +88,7 @@
<copyright>2015-present Evan You</copyright> <copyright>2015-present Evan You</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/vue-easy-dnd/vue-property-decorator.js</location> <location>amd/src/vue-easy-dnd/vue-property-decorator.js</location>
<name>vue-property-decorator</name> <name>vue-property-decorator</name>
@ -100,7 +100,7 @@
<copyright>2015-present Evan You</copyright> <copyright>2015-present Evan You</copyright>
</copyrights> </copyrights>
</library> </library>
<library> <library>
<location>amd/src/simpleline</location> <location>amd/src/simpleline</location>
<name>SimpleLine</name> <name>SimpleLine</name>
@ -110,8 +110,8 @@
<repository>https://github.com/miqraeng/simpleline</repository> <repository>https://github.com/miqraeng/simpleline</repository>
<copyrights> <copyrights>
<copyright>2023 P.M. Kuipers</copyright> <copyright>2023 P.M. Kuipers</copyright>
<copyright>Morglod/jchnkl</copyright> <copyright>Morglod/jchnkl</copyright>
</copyrights> </copyrights>
</library> </library>
</libraries> </libraries>

View File

@ -75,7 +75,7 @@ if ($studyplancontext->id > 1) {
// Load javascripts and specific css. // Load javascripts and specific css.
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
if($CFG->debugdeveloper){ if ($CFG->debugdeveloper) {
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
} }
$PAGE->requires->js_call_amd('local_treestudyplan/page-view-plan', 'init', [$studyplancontext->id, $categoryid]); $PAGE->requires->js_call_amd('local_treestudyplan/page-view-plan', 'init', [$studyplancontext->id, $categoryid]);

View File

@ -14,12 +14,12 @@ if [[ "$VUEJS_MD5" == "$VUEDEV_MD5" ]]; then
elif [[ "$VUEJS_MD5" == "$VUEPROD_MD5" ]]; then elif [[ "$VUEJS_MD5" == "$VUEPROD_MD5" ]]; then
MODE="prod" MODE="prod"
elif [[ "$2" != "-f" && "$1" != "query" ]]; then elif [[ "$2" != "-f" && "$1" != "query" ]]; then
echo "ERROR: $VUELINK is not a a copy of either ../vue/vue-dev.js or ../vue/vue-prod.js" echo "ERROR: $VUELINK is not a a copy of either ../vue/vue-dev.js or ../vue/vue-prod.js"
echo "Maybe you manually changed it? Use -f to force overwrite in this case" echo "Maybe you manually changed it? Use -f to force overwrite in this case"
exit 1 exit 1
fi fi
# SINCE the production version of vue.js s already minified, we may get away with simply copying it # SINCE the production version of vue.js s already minified, we may get away with simply copying it
# to the build directory # to the build directory
if [ "$1" == "dev" ]; then if [ "$1" == "dev" ]; then
cp $VUEDEV/vue.js $VUEBUILD/vue.min.js cp $VUEDEV/vue.js $VUEBUILD/vue.min.js