From 5a88761f22de1d69bac386670aea7d8e72d3160c Mon Sep 17 00:00:00 2001 From: PMKuipers Date: Fri, 2 Feb 2024 23:09:16 +0100 Subject: [PATCH] Fixed badge search and some other things --- amd/build/page-edit-plan.min.js | 2 +- amd/build/page-edit-plan.min.js.map | 2 +- amd/build/primary-nav-tools.min.js | 2 +- amd/build/primary-nav-tools.min.js.map | 2 +- amd/src/page-edit-plan.js | 12 +- amd/src/primary-nav-tools.js | 11 +- classes/badgeinfo.php | 57 ++++++-- classes/local/randomimage.php | 174 +++++++++++++++++++++++++ classes/studyplanservice.php | 19 +-- cli/generate_badges.php | 146 +++++++++++++++++++++ css/devstyles.css | 14 ++ edit-plan.php | 8 +- lib.php | 45 ++++++- scss/bootstraptweaking.scss | 12 +- scss/studyplan.scss | 1 + styles.css | 14 ++ version.php | 2 +- 17 files changed, 480 insertions(+), 43 deletions(-) create mode 100644 classes/local/randomimage.php create mode 100644 cli/generate_badges.php diff --git a/amd/build/page-edit-plan.min.js b/amd/build/page-edit-plan.min.js index 0311b3a..b2c1be2 100644 --- a/amd/build/page-edit-plan.min.js +++ b/amd/build/page-edit-plan.min.js @@ -1,3 +1,3 @@ -define("local_treestudyplan/page-edit-plan",["exports","core/str","core/ajax","core/notification","./vue/vue","./studyplan-editor-components","./treestudyplan-components","./modedit-modal","./util/debugger","./util/string-helper","./studyplan-processor","./downloader","./util/date-helper","./portal-vue/portal-vue.esm","./bootstrap-vue/bootstrap-vue","./vue-easy-dnd/vue-easy-dnd.esm"],(function(_exports,_str,_ajax,_notification,_vue,_studyplanEditorComponents,_treestudyplanComponents,_modeditModal,_debugger,_stringHelper,_studyplanProcessor,_downloader,_dateHelper,_portalVue,_bootstrapVue,_vueEasyDnd){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(contextid,categoryid,options){contextid=void 0===contextid||!Number.isInteger(Number(contextid))||contextid<1?1:Number(contextid);categoryid=void 0!==categoryid&&Number.isInteger(Number(categoryid))?Number(categoryid):0;debug.info("options",options),null===options||"object"!=typeof options||Array.isArray(options)?options={defaultAggregation:"core",editMode:!1}:(options.defaultAggregation||(options.defaultAggregation="core"),options.editMode||(options.editMode=!1));let app=new _vue.default({el:"#root",data:{create:{studyplan:{name:"",shortname:"",description:"",idnumber:"",slots:4,startdate:"2020-08-01",enddate:"",context:contextid,aggregation:options.defaultAggregation,aggregation_config:""}},toolbox:{shown:!1,right:!0},filters:{systembadges:"",relatedbadges:""},activestudyplan:null,activepage:null,loadingstudyplan:!1,studyplans:[],frameworks:[],relatedbadges:[],systembadges:[],courses:[],text:strings.studyplan,usedcontexts:[],initialEditMode:!!options.editMode},created(){this.$root.$on("studyplanRemoved",(studyplan=>{app.activestudyplan==studyplan&&(app.activestudyplan=null);let index=null;for(let idx in app.studyplans)if(app.studyplans[idx].id==studyplan.id){index=idx;break}index&&app.studyplans.splice(index,1)}))},mounted(){this.initialize()},computed:{dropdown_title(){return this.activestudyplan&&this.activestudyplan.name?this.activestudyplan.name:this.text.studyplan_select_placeholder},contextid:()=>contextid,filterComponentType:()=>({item:!1,component:!0,span:1,type:"filter"})},methods:{initialize(){(0,_ajax.call)([{methodname:"local_treestudyplan_list_studyplans",args:{context_id:contextid}}])[0].then((function(response){const timingval={future:0,present:1,past:2};response.sort(((a,b)=>{const timinga=(0,_dateHelper.studyplanTiming)(a),timingb=(0,_dateHelper.studyplanTiming)(b);let t=timingval[timinga]-timingval[timingb];return 0==t&&(t=new Date(b.startdate).getTime()-new Date(a.startdate).getTime(),0==t&&(t=a.name.localeCompare(b.name))),t})),app.studyplans=response;const hash=location.hash.replace("#","");if(hash)for(let idx in app.studyplans)if(app.studyplans[idx].id==hash){app.selectStudyplan(app.studyplans[idx]);break}})).catch(_notification.default.exception),(0,_ajax.call)([{methodname:"local_treestudyplan_map_categories",args:{}}])[0].then((function(response){app.courses=response})).catch(_notification.default.exception),(0,_ajax.call)([{methodname:"local_treestudyplan_list_used_categories",args:{operation:"edit",refcontext_id:contextid}}])[0].then((function(response){app.usedcontexts=response})).catch(_notification.default.exception),this.filter_systembadges()},closeStudyplan(){app.activestudyplan=null,window.location.hash=""},movedStudyplan(plan,from,to){const params=new URLSearchParams(location.search);params.delete("categoryid"),params.set("contextid",to),setTimeout((()=>{window.location.search=params.toString()}),50)},onStudyPlanCreated(newstudyplan){if(newstudyplan.context_id!=contextid){const params=new URLSearchParams(location.search);params.delete("categoryid"),params.set("contextid",newstudyplan.context_id),setTimeout((()=>{window.location=window.location.pathname+"?"+params.toString()+"#"+newstudyplan.id}),50)}else app.studyplans.push(newstudyplan),app.selectStudyplan(newstudyplan)},switchContext(ctxid){const params=new URLSearchParams(location.search);params.delete("categoryid"),params.set("contextid",ctxid),setTimeout((()=>{window.location.href=window.location.pathname+"?"+params.toString()}),50)},selectStudyplan(studyplan){app.loadingstudyplan=!0,app.activestudyplan=null,(0,_ajax.call)([{methodname:"local_treestudyplan_get_studyplan_map",args:{id:studyplan.id}}])[0].then((function(response){app.activestudyplan=(0,_studyplanProcessor.ProcessStudyplan)(response),debug.info("studyplan processed"),app.loadingstudyplan=!1,window.location.hash=app.activestudyplan.id})).catch((function(error){_notification.default.exception(error),app.loadingstudyplan=!1}))},onPageChange(page){this.activepage=page,this.filter_relatedbadges()},import_studyplan(){const self=this;(0,_downloader.upload)(((filename,content)=>{(0,_ajax.call)([{methodname:"local_treestudyplan_import_plan",args:{content:content,format:"application/json",context_id:contextid}}])[0].then((function(response){response.success?self.initialize():debug.error("Import failed: ",response.msg)})).catch(_notification.default.exception)}),"application/json")},export_plan(plan,format){null!=format&&["json","csv"].includes(format)||(format="json"),(0,_ajax.call)([{methodname:"local_treestudyplan_export_plan",args:{studyplan_id:plan.id,format:format}}])[0].then((function(response){(0,_downloader.download)(plan.shortname+".json",response.content,response.format)})).catch(_notification.default.exception)},toggletoolbox(event){debug.info(event),this.toolbox.shown=event},filter_systembadges(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_search_badges",args:{search:this.filters.systembadges||""}}])[0].then((function(response){self.systembadges=response})).catch(_notification.default.exception)},filter_relatedbadges(){const self=this;this.activepage&&(0,_ajax.call)([{methodname:"local_treestudyplan_search_related_badges",args:{page_id:this.activepage.id,search:this.filters.relatedbadges||""}}])[0].then((function(response){self.relatedbadges=response})).catch(_notification.default.exception)},reset_systembadges(){this.filters.systembadges="",this.filter_systembadges()},reset_relatedbadges(){this.filters.relatedbadges="",this.filter_relatedbadges()}}})},_notification=_interopRequireDefault(_notification),_vue=_interopRequireDefault(_vue),_studyplanEditorComponents=_interopRequireDefault(_studyplanEditorComponents),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents),_modeditModal=_interopRequireDefault(_modeditModal),_debugger=_interopRequireDefault(_debugger),_portalVue=_interopRequireDefault(_portalVue),_bootstrapVue=_interopRequireDefault(_bootstrapVue),_vue.default.use(_studyplanEditorComponents.default),_vue.default.use(_treestudyplanComponents.default),_vue.default.use(_modeditModal.default),_vue.default.use(_portalVue.default),_vue.default.use(_bootstrapVue.default),_vue.default.component("drag",_vueEasyDnd.Drag),_vue.default.component("drop",_vueEasyDnd.Drop),_vue.default.component("drop-list",_vueEasyDnd.DropList);const debug=new _debugger.default("treestudyplan");let strings=(0,_stringHelper.load_strings)({studyplan:{studyplan_select_placeholder:"studyplan_select_placeholder"}})})); +define("local_treestudyplan/page-edit-plan",["exports","core/str","core/ajax","core/notification","./vue/vue","./studyplan-editor-components","./treestudyplan-components","./modedit-modal","./util/debugger","./util/string-helper","./studyplan-processor","./downloader","./util/date-helper","./portal-vue/portal-vue.esm","./bootstrap-vue/bootstrap-vue","./vue-easy-dnd/vue-easy-dnd.esm"],(function(_exports,_str,_ajax,_notification,_vue,_studyplanEditorComponents,_treestudyplanComponents,_modeditModal,_debugger,_stringHelper,_studyplanProcessor,_downloader,_dateHelper,_portalVue,_bootstrapVue,_vueEasyDnd){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(contextid,categoryid,options){contextid=void 0===contextid||!Number.isInteger(Number(contextid))||contextid<1?1:Number(contextid);categoryid=void 0!==categoryid&&Number.isInteger(Number(categoryid))?Number(categoryid):0;debug.info("options",options),null===options||"object"!=typeof options||Array.isArray(options)?options={defaultAggregation:"core",editMode:!1}:(options.defaultAggregation||(options.defaultAggregation="core"),options.editMode||(options.editMode=!1));let app=new _vue.default({el:"#root",data:{create:{studyplan:{name:"",shortname:"",description:"",idnumber:"",slots:4,startdate:"2020-08-01",enddate:"",context:contextid,aggregation:options.defaultAggregation,aggregation_config:""}},toolbox:{shown:!1,right:!0},filters:{systembadges:"",relatedbadges:""},activestudyplan:null,activepage:null,loadingstudyplan:!1,studyplans:[],frameworks:[],relatedbadges:[],systembadges:[],courses:[],text:strings.studyplan,usedcontexts:[],initialEditMode:!!options.editMode},created(){this.$root.$on("studyplanRemoved",(studyplan=>{app.activestudyplan==studyplan&&(app.activestudyplan=null);let index=null;for(let idx in app.studyplans)if(app.studyplans[idx].id==studyplan.id){index=idx;break}index&&app.studyplans.splice(index,1)}))},mounted(){this.initialize()},computed:{dropdown_title(){return this.activestudyplan&&this.activestudyplan.name?this.activestudyplan.name:this.text.studyplan_select_placeholder},contextid:()=>contextid,filterComponentType:()=>({item:!1,component:!0,span:1,type:"filter"})},methods:{initialize(){(0,_ajax.call)([{methodname:"local_treestudyplan_list_studyplans",args:{context_id:contextid}}])[0].then((function(response){const timingval={future:0,present:1,past:2};response.sort(((a,b)=>{const timinga=(0,_dateHelper.studyplanTiming)(a),timingb=(0,_dateHelper.studyplanTiming)(b);let t=timingval[timinga]-timingval[timingb];return 0==t&&(t=new Date(b.startdate).getTime()-new Date(a.startdate).getTime(),0==t&&(t=a.name.localeCompare(b.name))),t})),app.studyplans=response;const hash=location.hash.replace("#","");if(hash){const id=hash;app.selectStudyplan(id)}})).catch(_notification.default.exception),(0,_ajax.call)([{methodname:"local_treestudyplan_map_categories",args:{}}])[0].then((function(response){app.courses=response})).catch(_notification.default.exception),(0,_ajax.call)([{methodname:"local_treestudyplan_list_used_categories",args:{operation:"edit",refcontext_id:contextid}}])[0].then((function(response){app.usedcontexts=response})).catch(_notification.default.exception),this.filter_systembadges()},closeStudyplan(){app.activestudyplan=null,window.location.hash=""},movedStudyplan(plan,from,to){const params=new URLSearchParams(location.search);params.delete("categoryid"),params.set("contextid",to),setTimeout((()=>{window.location.search=params.toString()}),50)},onStudyPlanCreated(newstudyplan){if(newstudyplan.context_id!=contextid){const params=new URLSearchParams(location.search);params.delete("categoryid"),params.set("contextid",newstudyplan.context_id),setTimeout((()=>{window.location=window.location.pathname+"?"+params.toString()+"#"+newstudyplan.id}),50)}else app.studyplans.push(newstudyplan),app.selectStudyplan(newstudyplan)},switchContext(ctxid){const params=new URLSearchParams(location.search);params.delete("categoryid"),params.set("contextid",ctxid),setTimeout((()=>{window.location.href=window.location.pathname+"?"+params.toString()}),50)},selectStudyplan(studyplanid){app.loadingstudyplan=!0,app.activestudyplan=null,(0,_ajax.call)([{methodname:"local_treestudyplan_get_studyplan_map",args:{id:studyplanid}}])[0].then((function(response){app.activestudyplan=(0,_studyplanProcessor.ProcessStudyplan)(response),debug.info("studyplan processed"),app.loadingstudyplan=!1,window.location.hash=app.activestudyplan.id})).catch((function(error){_notification.default.exception(error),app.loadingstudyplan=!1}))},onPageChange(page){this.activepage=page,this.filter_relatedbadges()},import_studyplan(){const self=this;(0,_downloader.upload)(((filename,content)=>{(0,_ajax.call)([{methodname:"local_treestudyplan_import_plan",args:{content:content,format:"application/json",context_id:contextid}}])[0].then((function(response){response.success?self.initialize():debug.error("Import failed: ",response.msg)})).catch(_notification.default.exception)}),"application/json")},export_plan(plan,format){null!=format&&["json","csv"].includes(format)||(format="json"),(0,_ajax.call)([{methodname:"local_treestudyplan_export_plan",args:{studyplan_id:plan.id,format:format}}])[0].then((function(response){(0,_downloader.download)(plan.shortname+".json",response.content,response.format)})).catch(_notification.default.exception)},toggletoolbox(event){debug.info(event),this.toolbox.shown=event},filter_systembadges(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_search_badges",args:{search:this.filters.systembadges||""}}])[0].then((function(response){self.systembadges=response})).catch(_notification.default.exception)},filter_relatedbadges(){const self=this;this.activepage&&(0,_ajax.call)([{methodname:"local_treestudyplan_search_related_badges",args:{page_id:this.activepage.id,search:this.filters.relatedbadges||""}}])[0].then((function(response){self.relatedbadges=response})).catch(_notification.default.exception)},reset_systembadges(){this.filters.systembadges="",this.filter_systembadges()},reset_relatedbadges(){this.filters.relatedbadges="",this.filter_relatedbadges()}}})},_notification=_interopRequireDefault(_notification),_vue=_interopRequireDefault(_vue),_studyplanEditorComponents=_interopRequireDefault(_studyplanEditorComponents),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents),_modeditModal=_interopRequireDefault(_modeditModal),_debugger=_interopRequireDefault(_debugger),_portalVue=_interopRequireDefault(_portalVue),_bootstrapVue=_interopRequireDefault(_bootstrapVue),_vue.default.use(_studyplanEditorComponents.default),_vue.default.use(_treestudyplanComponents.default),_vue.default.use(_modeditModal.default),_vue.default.use(_portalVue.default),_vue.default.use(_bootstrapVue.default),_vue.default.component("drag",_vueEasyDnd.Drag),_vue.default.component("drop",_vueEasyDnd.Drop),_vue.default.component("drop-list",_vueEasyDnd.DropList);const debug=new _debugger.default("treestudyplan");let strings=(0,_stringHelper.load_strings)({studyplan:{studyplan_select_placeholder:"studyplan_select_placeholder"}})})); //# sourceMappingURL=page-edit-plan.min.js.map \ No newline at end of file diff --git a/amd/build/page-edit-plan.min.js.map b/amd/build/page-edit-plan.min.js.map index 2d98594..e1d9dfc 100644 --- a/amd/build/page-edit-plan.min.js.map +++ b/amd/build/page-edit-plan.min.js.map @@ -1 +1 @@ -{"version":3,"file":"page-edit-plan.min.js","sources":["../src/page-edit-plan.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 notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\n\nimport EditorComponents from './studyplan-editor-components';\nVue.use(EditorComponents);\n\nimport TSComponents from './treestudyplan-components';\nVue.use(TSComponents);\n\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\nimport Debugger from './util/debugger';\n\nimport {load_strings} from './util/string-helper';\nimport {ProcessStudyplan} from './studyplan-processor';\nimport {download,upload} from './downloader';\nimport {studyplanTiming} from './util/date-helper';\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\nimport {Drag, Drop, DropList} from './vue-easy-dnd/vue-easy-dnd.esm';\nVue.component('drag',Drag);\nVue.component('drop',Drop);\nVue.component('drop-list',DropList);\n\nconst debug = new Debugger(\"treestudyplan\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {int} contextid The context we should attempt to work in (1:1 related to the category)\n * @param {int} categoryid The category we shoud attempt to work in (1:1 related to the context)\n * @param {object} options Options to be passed to this script\n */\nexport function init(contextid,categoryid,options) {\n // Make sure the id's are numeric and integer\n if(undefined === contextid || !Number.isInteger(Number(contextid)) || contextid < 1 ){ contextid = 1;}\n else { contextid = Number(contextid);} // ensure a numeric value instead of string\n if(undefined === categoryid || !Number.isInteger(Number(categoryid))){ categoryid = 0;}\n else { categoryid = Number(categoryid);} // ensure a numeric value instead of string\n\n debug.info(\"options\",options);\n if ( options !== null && typeof options === 'object' && !Array.isArray(options) ) {\n if ( !options.defaultAggregation ) {\n options.defaultAggregation = \"core\";\n }\n if ( !options.editMode ) {\n options.editMode = false;\n }\n } else {\n options = { defaultAggregation: \"core\", editMode: false};\n }\n\n // Setup the initial Vue app for this page\n let app = new Vue({\n el: '#root',\n data: {\n create: {\n studyplan: {\n name: '',\n shortname: '',\n description: '',\n idnumber: '',\n slots : 4,\n startdate: '2020-08-01',\n enddate: '',\n context: contextid,\n aggregation: options.defaultAggregation,\n aggregation_config: '',\n }\n },\n toolbox: {\n shown: false,\n right: true,\n },\n filters: {\n systembadges: \"\",\n relatedbadges: \"\",\n },\n activestudyplan: null,\n activepage: null,\n loadingstudyplan: false,\n studyplans: [],\n frameworks: [],\n relatedbadges: [],\n systembadges: [],\n courses: [],\n text: strings.studyplan,\n usedcontexts: [],\n initialEditMode: !!options.editMode,\n },\n created() {\n this.$root.$on('studyplanRemoved',(studyplan)=>{\n\n if(app.activestudyplan == studyplan){\n app.activestudyplan = null;\n }\n\n // remove studyplan from index list\n let index = null;\n for(let idx in app.studyplans){\n if(app.studyplans[idx].id == studyplan.id){\n index = idx;\n break;\n }\n }\n if(index){\n app.studyplans.splice(index, 1);\n }\n\n });\n },\n mounted() {\n this.initialize();\n },\n computed: {\n dropdown_title(){\n if(this.activestudyplan && this.activestudyplan.name){\n return this.activestudyplan.name;\n }\n else{\n return this.text.studyplan_select_placeholder;\n }\n },\n contextid(){\n return contextid;\n },\n filterComponentType(){\n return {\n item: false,\n component: true,\n span: 1,\n type: 'filter',\n };\n },\n },\n methods: {\n initialize() {\n call([{\n methodname: 'local_treestudyplan_list_studyplans',\n args: { context_id: contextid}\n }])[0].then(function(response){\n const timingval = { future: 0, present: 1, past: 2, };\n response.sort((a,b) => {\n const timinga = studyplanTiming(a);\n const timingb = 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 = response;\n \n // load studyplan from hash if applicable\n const hash = location.hash.replace('#','');\n if(hash){\n for(let idx in app.studyplans){\n if(app.studyplans[idx].id == hash){\n app.selectStudyplan(app.studyplans[idx]);\n break;\n }\n }\n }\n }).catch(notification.exception);\n call([{\n methodname: 'local_treestudyplan_map_categories',\n args: {}\n }])[0].then(function(response){\n app.courses = response;\n }).catch(notification.exception);\n call([{\n methodname: 'local_treestudyplan_list_used_categories',\n args: { operation: 'edit', refcontext_id: contextid}\n }])[0].then(function(response){\n app.usedcontexts = response;\n }).catch(notification.exception);\n this.filter_systembadges();\n },\n closeStudyplan() {\n app.activestudyplan = null;\n window.location.hash = '';\n },\n movedStudyplan(plan,from,to) {\n // reload the page in the new context (needed, since a number of links are not reactive in the page)\n const params = new URLSearchParams(location.search);\n params.delete('categoryid');\n params.set(\"contextid\", to);\n setTimeout(() => {\n // Reload page in a timeout to give other form javasccript the change to remove the beforeunload handler.\n window.location.search = params.toString();\n },50);\n },\n onStudyPlanCreated(newstudyplan){\n if (newstudyplan.context_id != contextid) {\n // Study plan has changed context id - reload page into new context id and show the plan\n const params = new URLSearchParams(location.search);\n params.delete('categoryid');\n params.set(\"contextid\", newstudyplan.context_id);\n setTimeout(() => {\n // Reload page in a timeout to give other form javasccript the change to remove the beforeunload handler.\n window.location = window.location.pathname + \"?\" + params.toString() + \"#\" + newstudyplan.id;\n },50);\n } else {\n app.studyplans.push(newstudyplan);\n app.selectStudyplan(newstudyplan);\n }\n },\n switchContext(ctxid){\n const params = new URLSearchParams(location.search);\n params.delete('categoryid');\n params.set('contextid', ctxid);\n setTimeout(() => {\n // Reload page in a timeout to give other form javasccript the change to remove the beforeunload handler.\n window.location.href = window.location.pathname + \"?\" + params.toString();\n },50);\n },\n selectStudyplan(studyplan){\n // fetch studyplan\n app.loadingstudyplan = true;\n app.activestudyplan = null;\n call([{\n methodname: 'local_treestudyplan_get_studyplan_map',\n args: { id: studyplan.id}\n }])[0].then(function(response){\n app.activestudyplan = ProcessStudyplan(response);\n debug.info('studyplan processed');\n app.loadingstudyplan = false;\n window.location.hash = app.activestudyplan.id;\n }).catch(function(error){\n notification.exception(error);\n app.loadingstudyplan = false;\n });\n },\n onPageChange(page) {\n this.activepage = page;\n this.filter_relatedbadges();\n },\n import_studyplan(){\n const self = this;\n upload((filename,content)=>{\n call([{\n methodname: 'local_treestudyplan_import_plan',\n args: {\n content: content,\n format: \"application/json\",\n context_id: contextid,\n },\n }])[0].then(function(response){\n if(response.success){\n self.initialize();\n } else {\n debug.error(\"Import failed: \",response.msg);\n }\n\n }).catch(notification.exception);\n }, \"application/json\");\n },\n export_plan(plan,format){\n let self = this;\n if(format == undefined || ![\"json\",\"csv\"].includes(format)){\n format = \"json\";\n }\n call([{\n methodname: 'local_treestudyplan_export_plan',\n args: {\n studyplan_id: plan.id,\n format: format\n },\n }])[0].then(function(response){\n\n download(plan.shortname+\".json\",response.content,response.format);\n }).catch(notification.exception);\n },\n toggletoolbox(event) {\n debug.info(event);\n this.toolbox.shown = event;\n },\n filter_systembadges() {\n const self = this;\n call([{\n methodname: 'local_treestudyplan_search_badges',\n args: { \n search: this.filters.systembadges || \"\"\n }\n }])[0].then(function(response){\n self.systembadges = response;\n }).catch(notification.exception);\n },\n filter_relatedbadges() {\n const self = this;\n if (this.activepage) {\n call([{\n methodname: 'local_treestudyplan_search_related_badges',\n args: { \n page_id: this.activepage.id,\n search: this.filters.relatedbadges || \"\"\n }\n }])[0].then(function(response){\n self.relatedbadges = response;\n }).catch(notification.exception);\n }\n },\n reset_systembadges() {\n this.filters.systembadges = \"\";\n this.filter_systembadges();\n },\n reset_relatedbadges() {\n this.filters.relatedbadges = \"\";\n this.filter_relatedbadges();\n },\n },\n });\n}\n\n"],"names":["contextid","categoryid","options","undefined","Number","isInteger","debug","info","Array","isArray","defaultAggregation","editMode","app","Vue","el","data","create","studyplan","name","shortname","description","idnumber","slots","startdate","enddate","context","aggregation","aggregation_config","toolbox","shown","right","filters","systembadges","relatedbadges","activestudyplan","activepage","loadingstudyplan","studyplans","frameworks","courses","text","strings","usedcontexts","initialEditMode","created","$root","$on","index","idx","id","splice","mounted","initialize","computed","dropdown_title","this","studyplan_select_placeholder","filterComponentType","item","component","span","type","methods","methodname","args","context_id","then","response","timingval","future","present","past","sort","a","b","timinga","timingb","t","Date","getTime","localeCompare","hash","location","replace","selectStudyplan","catch","notification","exception","operation","refcontext_id","filter_systembadges","closeStudyplan","window","movedStudyplan","plan","from","to","params","URLSearchParams","search","delete","set","setTimeout","toString","onStudyPlanCreated","newstudyplan","pathname","push","switchContext","ctxid","href","error","onPageChange","page","filter_relatedbadges","import_studyplan","self","filename","content","format","success","msg","export_plan","includes","studyplan_id","toggletoolbox","event","page_id","reset_systembadges","reset_relatedbadges","use","EditorComponents","TSComponents","ModalComponents","PortalVue","BootstrapVue","Drag","Drop","DropList","Debugger"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"page-edit-plan.min.js","sources":["../src/page-edit-plan.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 notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\n\nimport EditorComponents from './studyplan-editor-components';\nVue.use(EditorComponents);\n\nimport TSComponents from './treestudyplan-components';\nVue.use(TSComponents);\n\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\nimport Debugger from './util/debugger';\n\nimport {load_strings} from './util/string-helper';\nimport {ProcessStudyplan} from './studyplan-processor';\nimport {download,upload} from './downloader';\nimport {studyplanTiming} from './util/date-helper';\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\nimport {Drag, Drop, DropList} from './vue-easy-dnd/vue-easy-dnd.esm';\nVue.component('drag',Drag);\nVue.component('drop',Drop);\nVue.component('drop-list',DropList);\n\nconst debug = new Debugger(\"treestudyplan\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {int} contextid The context we should attempt to work in (1:1 related to the category)\n * @param {int} categoryid The category we shoud attempt to work in (1:1 related to the context)\n * @param {object} options Options to be passed to this script\n */\nexport function init(contextid,categoryid,options) {\n // Make sure the id's are numeric and integer\n if(undefined === contextid || !Number.isInteger(Number(contextid)) || contextid < 1 ){ contextid = 1;}\n else { contextid = Number(contextid);} // ensure a numeric value instead of string\n if(undefined === categoryid || !Number.isInteger(Number(categoryid))){ categoryid = 0;}\n else { categoryid = Number(categoryid);} // ensure a numeric value instead of string\n\n debug.info(\"options\",options);\n if ( options !== null && typeof options === 'object' && !Array.isArray(options) ) {\n if ( !options.defaultAggregation ) {\n options.defaultAggregation = \"core\";\n }\n if ( !options.editMode ) {\n options.editMode = false;\n }\n } else {\n options = { defaultAggregation: \"core\", editMode: false};\n }\n\n // Setup the initial Vue app for this page\n let app = new Vue({\n el: '#root',\n data: {\n create: {\n studyplan: {\n name: '',\n shortname: '',\n description: '',\n idnumber: '',\n slots : 4,\n startdate: '2020-08-01',\n enddate: '',\n context: contextid,\n aggregation: options.defaultAggregation,\n aggregation_config: '',\n }\n },\n toolbox: {\n shown: false,\n right: true,\n },\n filters: {\n systembadges: \"\",\n relatedbadges: \"\",\n },\n activestudyplan: null,\n activepage: null,\n loadingstudyplan: false,\n studyplans: [],\n frameworks: [],\n relatedbadges: [],\n systembadges: [],\n courses: [],\n text: strings.studyplan,\n usedcontexts: [],\n initialEditMode: !!options.editMode,\n },\n created() {\n this.$root.$on('studyplanRemoved',(studyplan)=>{\n\n if(app.activestudyplan == studyplan){\n app.activestudyplan = null;\n }\n\n // remove studyplan from index list\n let index = null;\n for(let idx in app.studyplans){\n if(app.studyplans[idx].id == studyplan.id){\n index = idx;\n break;\n }\n }\n if(index){\n app.studyplans.splice(index, 1);\n }\n\n });\n },\n mounted() {\n this.initialize();\n },\n computed: {\n dropdown_title(){\n if(this.activestudyplan && this.activestudyplan.name){\n return this.activestudyplan.name;\n }\n else{\n return this.text.studyplan_select_placeholder;\n }\n },\n contextid(){\n return contextid;\n },\n filterComponentType(){\n return {\n item: false,\n component: true,\n span: 1,\n type: 'filter',\n };\n },\n },\n methods: {\n initialize() {\n call([{\n methodname: 'local_treestudyplan_list_studyplans',\n args: { context_id: contextid}\n }])[0].then(function(response){\n const timingval = { future: 0, present: 1, past: 2, };\n response.sort((a,b) => {\n const timinga = studyplanTiming(a);\n const timingb = 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 = response;\n \n // load studyplan from hash if applicable\n const hash = location.hash.replace('#','');\n if(hash){\n const id = hash;\n app.selectStudyplan(id);\n }\n }).catch(notification.exception);\n call([{\n methodname: 'local_treestudyplan_map_categories',\n args: {}\n }])[0].then(function(response){\n app.courses = response;\n }).catch(notification.exception);\n call([{\n methodname: 'local_treestudyplan_list_used_categories',\n args: { operation: 'edit', refcontext_id: contextid}\n }])[0].then(function(response){\n app.usedcontexts = response;\n }).catch(notification.exception);\n this.filter_systembadges();\n },\n closeStudyplan() {\n app.activestudyplan = null;\n window.location.hash = '';\n },\n movedStudyplan(plan,from,to) {\n // reload the page in the new context (needed, since a number of links are not reactive in the page)\n const params = new URLSearchParams(location.search);\n params.delete('categoryid');\n params.set(\"contextid\", to);\n setTimeout(() => {\n // Reload page in a timeout to give other form javasccript the change to remove the beforeunload handler.\n window.location.search = params.toString();\n },50);\n },\n onStudyPlanCreated(newstudyplan){\n if (newstudyplan.context_id != contextid) {\n // Study plan has changed context id - reload page into new context id and show the plan\n const params = new URLSearchParams(location.search);\n params.delete('categoryid');\n params.set(\"contextid\", newstudyplan.context_id);\n setTimeout(() => {\n // Reload page in a timeout to give other form javasccript the change to remove the beforeunload handler.\n window.location = window.location.pathname + \"?\" + params.toString() + \"#\" + newstudyplan.id;\n },50);\n } else {\n app.studyplans.push(newstudyplan);\n app.selectStudyplan(newstudyplan);\n }\n },\n switchContext(ctxid){\n const params = new URLSearchParams(location.search);\n params.delete('categoryid');\n params.set('contextid', ctxid);\n setTimeout(() => {\n // Reload page in a timeout to give other form javasccript the change to remove the beforeunload handler.\n window.location.href = window.location.pathname + \"?\" + params.toString();\n },50);\n },\n selectStudyplan(studyplanid){\n // fetch studyplan\n app.loadingstudyplan = true;\n app.activestudyplan = null;\n call([{\n methodname: 'local_treestudyplan_get_studyplan_map',\n args: { id: studyplanid}\n }])[0].then(function(response){\n app.activestudyplan = ProcessStudyplan(response);\n debug.info('studyplan processed');\n app.loadingstudyplan = false;\n window.location.hash = app.activestudyplan.id;\n }).catch(function(error){\n notification.exception(error);\n app.loadingstudyplan = false;\n });\n },\n onPageChange(page) {\n this.activepage = page;\n this.filter_relatedbadges();\n },\n import_studyplan(){\n const self = this;\n upload((filename,content)=>{\n call([{\n methodname: 'local_treestudyplan_import_plan',\n args: {\n content: content,\n format: \"application/json\",\n context_id: contextid,\n },\n }])[0].then(function(response){\n if(response.success){\n self.initialize();\n } else {\n debug.error(\"Import failed: \",response.msg);\n }\n\n }).catch(notification.exception);\n }, \"application/json\");\n },\n export_plan(plan,format){\n let self = this;\n if(format == undefined || ![\"json\",\"csv\"].includes(format)){\n format = \"json\";\n }\n call([{\n methodname: 'local_treestudyplan_export_plan',\n args: {\n studyplan_id: plan.id,\n format: format\n },\n }])[0].then(function(response){\n\n download(plan.shortname+\".json\",response.content,response.format);\n }).catch(notification.exception);\n },\n toggletoolbox(event) {\n debug.info(event);\n this.toolbox.shown = event;\n },\n filter_systembadges() {\n const self = this;\n call([{\n methodname: 'local_treestudyplan_search_badges',\n args: { \n search: this.filters.systembadges || \"\"\n }\n }])[0].then(function(response){\n self.systembadges = response;\n }).catch(notification.exception);\n },\n filter_relatedbadges() {\n const self = this;\n if (this.activepage) {\n call([{\n methodname: 'local_treestudyplan_search_related_badges',\n args: { \n page_id: this.activepage.id,\n search: this.filters.relatedbadges || \"\"\n }\n }])[0].then(function(response){\n self.relatedbadges = response;\n }).catch(notification.exception);\n }\n },\n reset_systembadges() {\n this.filters.systembadges = \"\";\n this.filter_systembadges();\n },\n reset_relatedbadges() {\n this.filters.relatedbadges = \"\";\n this.filter_relatedbadges();\n },\n },\n });\n}\n\n"],"names":["contextid","categoryid","options","undefined","Number","isInteger","debug","info","Array","isArray","defaultAggregation","editMode","app","Vue","el","data","create","studyplan","name","shortname","description","idnumber","slots","startdate","enddate","context","aggregation","aggregation_config","toolbox","shown","right","filters","systembadges","relatedbadges","activestudyplan","activepage","loadingstudyplan","studyplans","frameworks","courses","text","strings","usedcontexts","initialEditMode","created","$root","$on","index","idx","id","splice","mounted","initialize","computed","dropdown_title","this","studyplan_select_placeholder","filterComponentType","item","component","span","type","methods","methodname","args","context_id","then","response","timingval","future","present","past","sort","a","b","timinga","timingb","t","Date","getTime","localeCompare","hash","location","replace","selectStudyplan","catch","notification","exception","operation","refcontext_id","filter_systembadges","closeStudyplan","window","movedStudyplan","plan","from","to","params","URLSearchParams","search","delete","set","setTimeout","toString","onStudyPlanCreated","newstudyplan","pathname","push","switchContext","ctxid","href","studyplanid","error","onPageChange","page","filter_relatedbadges","import_studyplan","self","filename","content","format","success","msg","export_plan","includes","studyplan_id","toggletoolbox","event","page_id","reset_systembadges","reset_relatedbadges","use","EditorComponents","TSComponents","ModalComponents","PortalVue","BootstrapVue","Drag","Drop","DropList","Debugger"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/primary-nav-tools.min.js b/amd/build/primary-nav-tools.min.js index a869f85..8187446 100644 --- a/amd/build/primary-nav-tools.min.js +++ b/amd/build/primary-nav-tools.min.js @@ -1,3 +1,3 @@ -define("local_treestudyplan/primary-nav-tools",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.hide_primary=function(hrefs){let element=document.createElement("style");document.head.appendChild(element);let sheet=element.sheet;("string"==typeof hrefs||hrefs instanceof String)&&(hrefs=[hrefs]);if("object"==typeof hrefs&&Array.isArray(hrefs))for(const ix in hrefs){const href=hrefs[ix];let style=`\n li > a[href*="${href}"] {\n display: none;\n }\n `;sheet.insertRule(style,sheet.cssRules.length),style=`\n #usernavigation a[href*="${href}"] {\n display: none;\n }\n `,sheet.insertRule(style,sheet.cssRules.length)}}})); +define("local_treestudyplan/primary-nav-tools",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.hide_primary=function(hrefs){("string"==typeof hrefs||hrefs instanceof String)&&(hrefs=[hrefs]);if("object"==typeof hrefs&&Array.isArray(hrefs)){let element=document.createElement("style");document.head.appendChild(element);let sheet=element.sheet;for(const ix in hrefs){const href=hrefs[ix];console.info(`Hiding ${href}`);let style=`\n li > a[href*="${href}"] {\n display: none;\n }\n `;sheet.insertRule(style,sheet.cssRules.length),style=`\n #usernavigation a[href*="${href}"] {\n display: none;\n }\n `,sheet.insertRule(style,sheet.cssRules.length)}console.info(element),console.info(sheet)}}})); //# sourceMappingURL=primary-nav-tools.min.js.map \ No newline at end of file diff --git a/amd/build/primary-nav-tools.min.js.map b/amd/build/primary-nav-tools.min.js.map index abd3ca8..5adec79 100644 --- a/amd/build/primary-nav-tools.min.js.map +++ b/amd/build/primary-nav-tools.min.js.map @@ -1 +1 @@ -{"version":3,"file":"primary-nav-tools.min.js","sources":["../src/primary-nav-tools.js"],"sourcesContent":["/*eslint-env es6*/\n\n/**\n * Hide a primary navigation item by href\n * @param {string|Array} hrefs The link that should be hidden\n */\nexport function hide_primary(hrefs) {\n let element = document.createElement('style');\n document.head.appendChild(element);\n let sheet = element.sheet;\n\n if(typeof hrefs === 'string' || hrefs instanceof String){\n hrefs = [hrefs];\n }\n\n if(typeof hrefs === 'object' && Array.isArray(hrefs)){\n for(const ix in hrefs){\n const href = hrefs[ix];\n let style = `\n li > a[href*=\"${href}\"] {\n display: none;\n }\n `;\n sheet.insertRule(style, sheet.cssRules.length);\n style = `\n #usernavigation a[href*=\"${href}\"] {\n display: none;\n }\n `;\n sheet.insertRule(style, sheet.cssRules.length);\n }\n }\n}\n"],"names":["hrefs","element","document","createElement","head","appendChild","sheet","String","Array","isArray","ix","href","style","insertRule","cssRules","length"],"mappings":"sKAM6BA,WACrBC,QAAUC,SAASC,cAAc,SACrCD,SAASE,KAAKC,YAAYJ,aACtBK,MAAQL,QAAQK,OAEA,iBAAVN,OAAsBA,iBAAiBO,UAC7CP,MAAQ,CAACA,WAGO,iBAAVA,OAAsBQ,MAAMC,QAAQT,WACtC,MAAMU,MAAMV,MAAM,OACZW,KAAOX,MAAMU,QACfE,MAAS,mCACOD,gFAIpBL,MAAMO,WAAWD,MAAON,MAAMQ,SAASC,QACvCH,MAAS,8CACsBD,gFAI/BL,MAAMO,WAAWD,MAAON,MAAMQ,SAASC"} \ No newline at end of file +{"version":3,"file":"primary-nav-tools.min.js","sources":["../src/primary-nav-tools.js"],"sourcesContent":["/*eslint-env es6*/\n/*eslint no-console: \"off\"*/\n\n/**\n * Hide a primary navigation item by href\n * @param {string|Array} hrefs The link that should be hidden\n */\nexport function hide_primary(hrefs) {\n if(typeof hrefs === 'string' || hrefs instanceof String){\n hrefs = [hrefs];\n }\n\n if(typeof hrefs === 'object' && Array.isArray(hrefs)){\n let element = document.createElement('style');\n document.head.appendChild(element);\n let sheet = element.sheet;\n for(const ix in hrefs){\n const href = hrefs[ix];\n console.info(`Hiding ${href}`);\n let style = `\n li > a[href*=\"${href}\"] {\n display: none;\n }\n `;\n sheet.insertRule(style, sheet.cssRules.length);\n style = `\n #usernavigation a[href*=\"${href}\"] {\n display: none;\n }\n `;\n sheet.insertRule(style, sheet.cssRules.length);\n }\n console.info(element);\n console.info(sheet);\n }\n}\n"],"names":["hrefs","String","Array","isArray","element","document","createElement","head","appendChild","sheet","ix","href","console","info","style","insertRule","cssRules","length"],"mappings":"sKAO6BA,QACL,iBAAVA,OAAsBA,iBAAiBC,UAC7CD,MAAQ,CAACA,WAGO,iBAAVA,OAAsBE,MAAMC,QAAQH,OAAO,KAC7CI,QAAUC,SAASC,cAAc,SACrCD,SAASE,KAAKC,YAAYJ,aACtBK,MAAQL,QAAQK,UAChB,MAAMC,MAAMV,MAAM,OACZW,KAAOX,MAAMU,IACnBE,QAAQC,KAAM,UAASF,YACnBG,MAAS,mCACOH,gFAIpBF,MAAMM,WAAWD,MAAOL,MAAMO,SAASC,QACvCH,MAAS,8CACsBH,gFAI/BF,MAAMM,WAAWD,MAAOL,MAAMO,SAASC,QAE3CL,QAAQC,KAAKT,SACbQ,QAAQC,KAAKJ"} \ No newline at end of file diff --git a/amd/src/page-edit-plan.js b/amd/src/page-edit-plan.js index c4511da..b79f3d3 100644 --- a/amd/src/page-edit-plan.js +++ b/amd/src/page-edit-plan.js @@ -182,12 +182,8 @@ export function init(contextid,categoryid,options) { // load studyplan from hash if applicable const hash = location.hash.replace('#',''); if(hash){ - for(let idx in app.studyplans){ - if(app.studyplans[idx].id == hash){ - app.selectStudyplan(app.studyplans[idx]); - break; - } - } + const id = hash; + app.selectStudyplan(id); } }).catch(notification.exception); call([{ @@ -242,13 +238,13 @@ export function init(contextid,categoryid,options) { window.location.href = window.location.pathname + "?" + params.toString(); },50); }, - selectStudyplan(studyplan){ + selectStudyplan(studyplanid){ // fetch studyplan app.loadingstudyplan = true; app.activestudyplan = null; call([{ methodname: 'local_treestudyplan_get_studyplan_map', - args: { id: studyplan.id} + args: { id: studyplanid} }])[0].then(function(response){ app.activestudyplan = ProcessStudyplan(response); debug.info('studyplan processed'); diff --git a/amd/src/primary-nav-tools.js b/amd/src/primary-nav-tools.js index 62f5b25..af78273 100644 --- a/amd/src/primary-nav-tools.js +++ b/amd/src/primary-nav-tools.js @@ -1,21 +1,22 @@ /*eslint-env es6*/ +/*eslint no-console: "off"*/ /** * Hide a primary navigation item by href * @param {string|Array} hrefs The link that should be hidden */ export function hide_primary(hrefs) { - let element = document.createElement('style'); - document.head.appendChild(element); - let sheet = element.sheet; - if(typeof hrefs === 'string' || hrefs instanceof String){ hrefs = [hrefs]; } if(typeof hrefs === 'object' && Array.isArray(hrefs)){ + let element = document.createElement('style'); + document.head.appendChild(element); + let sheet = element.sheet; for(const ix in hrefs){ const href = hrefs[ix]; + console.info(`Hiding ${href}`); let style = ` li > a[href*="${href}"] { display: none; @@ -29,5 +30,7 @@ export function hide_primary(hrefs) { `; sheet.insertRule(style, sheet.cssRules.length); } + console.info(element); + console.info(sheet); } } diff --git a/classes/badgeinfo.php b/classes/badgeinfo.php index 40f48cf..5016339 100644 --- a/classes/badgeinfo.php +++ b/classes/badgeinfo.php @@ -107,14 +107,14 @@ class badgeinfo { public static function editor_structure($value = VALUE_REQUIRED) : \external_description { return new \external_single_structure([ "id" => new \external_value(PARAM_INT, 'id of badge'), - "infolink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL), - "name" => new \external_value(PARAM_TEXT, 'badge name'), + "infolink" => new \external_value(PARAM_RAW, 'badge issue information link', VALUE_OPTIONAL), + "name" => new \external_value(PARAM_RAW, 'badge name'), "status" => new \external_value(PARAM_TEXT, 'badge status'), "locked" => new \external_value(PARAM_TEXT, 'badge lock status'), "criteria" => new \external_multiple_structure( new \external_value(PARAM_RAW, 'criteria text'), 'badge criteria', VALUE_OPTIONAL), - "description" => new \external_value(PARAM_TEXT, 'badge description'), - "imageurl" => new \external_value(PARAM_TEXT, 'url of badge image'), + "description" => new \external_value(PARAM_RAW, 'badge description'), + "imageurl" => new \external_value(PARAM_RAW, 'url of badge image'), "studentcount" => new \external_value(PARAM_INT, 'number of studyplan students that can get this badge', VALUE_OPTIONAL), "issuedcount" => new \external_value(PARAM_INT, @@ -823,17 +823,16 @@ class badgeinfo { [$insql, $inparams] = $DB->get_in_or_equal([BADGE_STATUS_ACTIVE, BADGE_STATUS_ACTIVE_LOCKED]); $conditions .= " AND b.status $insql"; $basesqlparams = array_merge($basesqlparams,$inparams); - } // Get all course badges for this course - $coursebadges = []; + $badgesids = []; $sql = "SELECT DISTINCT b.id from {badge} b WHERE b.courseid = :courseid AND $conditions"; - $coursebadgesids = $DB->get_fieldset_sql($sql,$basesqlparams); + $badgesids = $DB->get_fieldset_sql($sql,$basesqlparams); - return $coursebadgesids; + return $badgesids; } /** @@ -879,4 +878,46 @@ class badgeinfo { return $badges; } + /** + * Search site badges + * @param string $search An optional search string to filter by + * @param bool $active Only list active badges + * @return array of badge + */ + public static function search_site_badges($search='', $active=false) { + global $DB; + + $search = trim($search); + $conditions = "TRUE"; + $basesqlparams = ['type' => \BADGE_TYPE_SITE]; + + if (strlen($search) > 0) { + $conditions .= " AND ".$DB->sql_like('b.name',':search'); + $basesqlparams["search"] = "%$search%"; + } + if ($active) { + [$insql, $inparams] = $DB->get_in_or_equal([BADGE_STATUS_ACTIVE, BADGE_STATUS_ACTIVE_LOCKED]); + $conditions .= " AND b.status $insql"; + $basesqlparams = array_merge($basesqlparams,$inparams); + } + + // Get all course badges for this course + $sql = "SELECT DISTINCT b.id from {badge} b + WHERE $conditions"; + + $badgeids = $DB->get_fieldset_sql($sql,$basesqlparams); + + $badges = []; + foreach (array_unique($badgeids) as $id) { + $badges[] = new self(new badge($id)); + } + + usort($badges,function($a,$b) { + return $a->name() <=> $b->name(); + }); + + return $badges; + } + + } diff --git a/classes/local/randomimage.php b/classes/local/randomimage.php new file mode 100644 index 0000000..3dcd5b6 --- /dev/null +++ b/classes/local/randomimage.php @@ -0,0 +1,174 @@ +. +/** + * Generate random grades and feedback to initialize development environment + * @package local_treestudyplan + * @copyright 2023 P.M. Kuipers - Chris Zuber + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_treestudyplan\local; + +use Exception; +/** + * Generate random grades and feedback to initialize development environment + */ +class randomimage { + + /** + * Image width + * @var integer + */ + private $width; + + /** + * Image height + * @var integer + */ + private $height ; + + /** + * Number of shapes to draw + * @var integer + */ + private $shapes; + + /** + * Image name + * @var string + */ + public $name; + + /** + * Image temporary file + * @var string + */ + public $tempfile; + + /** + * Various available styles for arcs + * @var Arary + */ + const ARC_STYLES = [ + IMG_ARC_PIE, + IMG_ARC_CHORD, + IMG_ARC_EDGED, + IMG_ARC_NOFILL + ]; + + /** + * Create a random polygon with number of points between 0 & $max_pts + * @param \GdImage $im The image reource + * @param integer $max_pts Max number of point to use + * @return void + */ + private function random_polygon($im, Int $max_pts = 20) + { + $color = imagecolorallocatealpha($im, ...$this->random_color_alpha()); + $pts = $this->random_pts(\random_int(3, $max_pts)); + imagefilledpolygon($im, $pts, $color); + } + + /** + * Creates a random arc of a random color + * @param \GdImage $im The image resource + * @return void + */ + private function random_arc($im) + { + $cx = \random_int(0, $this->width); + $cy = \random_int(0, $this->height); + $w = \random_int(0, $this->width); + $h = \random_int(0, $this->height); + $s = \random_int(0, 360); + $e = \random_int(0, 360); + $col = imagecolorallocatealpha($im, ...$this->random_color_alpha()); + $style = self::ARC_STYLES[\random_int(0, 3)]; + imagefilledarc($im, $cx, $cy, $w, $h, $s, $e, $col, $style); + } + + + /** + * Generates an array of random alpha color values. + * @return Array [r, g, b, a] + */ + private function random_color_alpha(): Array + { + return [ + \random_int(0, 255), + \random_int(0, 255), + \random_int(0, 255), + \random_int(0, 127) + ]; + } + + /** + * Generates a set of random points for a polygon [x1,y1, x2,y2,...] + * @param integer $length Number of sets of points to generate + * @return Array The resulting array of points + */ + private function random_pts($length): Array + { + $pts = []; + for($n = 0; $n < $length; $n++) { + $pts[] = \random_int(0, $this->width); + $pts[] = \random_int(0, $this->height); + } + return $pts; + } + + public function __construct($shapes=3,$width=256,$height=256) { + $this->shapes = $shapes; + $this->width = $width; + $this->height = $height; + + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $string = ''; + + for ($i = 0; $i < 20; $i++) { + $string .= $characters[\mt_rand(0, strlen($characters) - 1)]; + } + + $this->name = $string.".png"; + + $im = imagecreatetruecolor($this->width, $this->height); + for ($i = 0; $i < $this->shapes; $i++) { + switch(\random_int(1, 2)) { + case 1: + $this->random_arc($im); + break; + case 2: + $this->random_polygon($im); + break; + } + } + $this->tempfile = \tempnam("/tmp","tsp"); + imagepng($im,$this->tempfile); + imagedestroy($im); + } + + public function content() { + return $this->content = \file_get_contents($this->tempfile); + } + + public function __destruct() + { + if (isset($this->tempfile) && file_exists($this->tempfile)) { + unlink($this->tempfile); + } + } + +} \ No newline at end of file diff --git a/classes/studyplanservice.php b/classes/studyplanservice.php index 8b5e5c5..977456a 100644 --- a/classes/studyplanservice.php +++ b/classes/studyplanservice.php @@ -875,20 +875,15 @@ class studyplanservice extends \external_api { */ public static function search_badges($search,$active=false) { $systemcontext = webservicehelper::system_context(); - $search = trim($search); - - $result = []; - $badges = badges_get_badges(BADGE_TYPE_SITE, "timemodified"); + // Check system permission to + webservicehelper::require_capabilities("moodle/badges:viewbadges",$systemcontext); - foreach ($badges as $badge) { - if (!$active || $badge->is_active()) { - - if (strlen($search) == 0 || mb_stristr($badge->name,$search) ) { - $result[] = (new badgeinfo($badge))->editor_model(); - } - } + $results = []; + $badges = badgeinfo::search_site_badges($search,$active); + foreach ($badges as $badgeinfo) { + $results[] = $badgeinfo->editor_model(); } - return $result; + return $results; } diff --git a/cli/generate_badges.php b/cli/generate_badges.php new file mode 100644 index 0000000..5f62736 --- /dev/null +++ b/cli/generate_badges.php @@ -0,0 +1,146 @@ +. +/** + * Developer CLI tool. Primes grade generator with random student properties + * @package local_treestudyplan + * @copyright 2023 P.M. Kuipers + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_treestudyplan; + +define('CLI_SCRIPT', true); +require(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/clilib.php'); +use \core_badges\badge; +use local_treestudyplan\local\randomimage; +require_once($CFG->libdir.'/badgeslib.php'); + +$usage = "Generate a number of random badges +Usage: + # php generate_badges.php [--amount|-a=] + # php generate_badges.php --help|-h + +Options: + -h --help Print this help. + -a --amount= The amount of badges to generate. +"; + +list($options, $unrecognised) = cli_get_params([ + 'help' => false, + 'amount' => null +], [ + 'h' => 'help', + 'a' => 'amount', +]); + +if ($unrecognised) { + $unrecognised = implode(PHP_EOL . ' ', $unrecognised); + cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised)); +} + +if ($options['help']) { + cli_writeln($usage); + exit(2); +} +print_r($options); +if (empty($options['amount']) || !is_numeric($options['amount'])) { + cli_error('Missing mandatory integer argument "amount"', 2); +} +$amount = intval($options['amount']); +cli_writeln("Creating {$amount} new badges"); + +$fortunepath = "/usr/games/fortune"; +if (!is_executable($fortunepath)) { + // Get a fortune if it is available. + cli_error('/usr/bin/fortune not found, which is needed for this script. Please install fortune',2); +} + +// Get administrator role and log in as. +$user = get_admin(); + +if (!$user) { + cli_error("Unable to find admin user in DB."); +} + +$auth = empty($user->auth) ? 'manual' : $user->auth; +if ($auth == 'nologin' || !is_enabled_auth($auth)) { + cli_error(sprintf("User authentication is either 'nologin' or disabled. Check Moodle authentication method for '%s'", + $user->username)); +} + +$authplugin = get_auth_plugin($auth); +$authplugin->sync_roles($user); +login_attempt_valid($user); +complete_user_login($user); + + +for ($i=0; $i<$amount; $i++) { + $fortune = shell_exec("{$fortunepath} -n 160 "); + $pieces = explode(" ", $fortune); + $name = implode(" ", array_splice($pieces, 0, 4)); + + $fordb = new \stdClass(); + $fordb->id = null; + + $now = time(); + + $randomicon = new randomimage(10,256,256); + + + $fordb->name = $name; + $fordb->version = 1; + $fordb->language = "en"; + $fordb->description = $fortune; + $fordb->imageauthorname = "Generator"; + $fordb->imageauthoremail = "generator@null.null"; + $fordb->imageauthorurl = ""; + $fordb->imagecaption = $name; + $fordb->timecreated = $now; + $fordb->timemodified = $now; + $fordb->usercreated = $USER->id; + $fordb->usermodified = $USER->id; + + $url = parse_url($CFG->wwwroot); + $fordb->issuerurl = $url['scheme'] . '://' . $url['host']; + $fordb->issuername = $CFG->badges_defaultissuername; + $fordb->issuercontact = $CFG->badges_defaultissuercontact; + + $fordb->expiredate = null; + $fordb->expireperiod = null; + $fordb->type = \BADGE_TYPE_SITE; // Site badge. + $fordb->courseid = null; + $fordb->messagesubject = get_string('messagesubject', 'badges'); + $fordb->message = get_string('messagebody', 'badges', + \html_writer::link($CFG->wwwroot . '/badges/mybadges.php', get_string('managebadges', 'badges'))); + $fordb->attachment = 1; + $fordb->notification = \BADGE_MESSAGE_NEVER; + + $fordb->status = \BADGE_STATUS_INACTIVE; + + $newid = $DB->insert_record('badge', $fordb, true); + + // Trigger event, badge created. + $eventparams = array('objectid' => $newid, 'context' => $PAGE->context); + $event = \core\event\badge_created::create($eventparams); + $event->trigger(); + + $newbadge = new badge($newid); + badges_process_badge_image($newbadge, $randomicon->tempfile); + cli_writeln("Created new badge '{$name}'"); +} + + diff --git a/css/devstyles.css b/css/devstyles.css index 452f9fb..7f3fed8 100644 --- a/css/devstyles.css +++ b/css/devstyles.css @@ -665,6 +665,7 @@ top: -5px; right: -5px; font-size: 16px; + background-color: transparent; } .path-local-treestudyplan a.t-item-course-config, .features-treestudyplan a.t-item-course-config { @@ -1536,4 +1537,17 @@ .path-local-treestudyplan .b-modal-justify-footer-between .modal-footer, .features-treestudyplan .b-modal-justify-footer-between .modal-footer { justify-content: space-between; +} +.path-local-treestudyplan .collapse.show, +.features-treestudyplan .collapse.show { + height: auto; +} + +body.path-local-treestudyplan.bootstrap-polyfill, +.features-treestudyplan.bootstrap-polyfill { + /* Contains a number of CSS tweaks only needed if the current theme is nog based on boost with bootstrap */ +} +body.path-local-treestudyplan.bootstrap-polyfill .collapse.show, +.features-treestudyplan.bootstrap-polyfill .collapse.show { + height: auto; } \ No newline at end of file diff --git a/edit-plan.php b/edit-plan.php index e729145..6a5361d 100644 --- a/edit-plan.php +++ b/edit-plan.php @@ -106,7 +106,7 @@ print $OUTPUT->header();
-
+
header();   - + {{ studyplan.name }} + >{{ studyplan.name }}   header(); :key='studyplan.id' v-model='studyplans[planindex]' open - @open='selectStudyplan(studyplan)' + @open='selectStudyplan(studyplan.id)' >