From ce8d67fa350fb49824568c7f0cd46ecf9e9fa82a Mon Sep 17 00:00:00 2001 From: Stefan Hanauska Date: Tue, 26 Mar 2024 13:45:24 +0100 Subject: [PATCH 1/4] MBS-8970: Add option for WIP limit --- amd/build/column.min.js | 2 +- amd/build/column.min.js.map | 2 +- amd/build/exporter.min.js | 2 +- amd/build/exporter.min.js.map | 2 +- amd/build/selectors.min.js | 2 +- amd/build/selectors.min.js.map | 2 +- amd/src/column.js | 2 + amd/src/exporter.js | 5 ++ amd/src/selectors.js | 1 + classes/boardmanager.php | 80 +++++++++++++++++++++++++++++-- classes/form/edit_column_form.php | 12 +++++ lang/en/kanban.php | 4 ++ styles.css | 5 ++ templates/column.mustache | 10 +++- 14 files changed, 120 insertions(+), 11 deletions(-) diff --git a/amd/build/column.min.js b/amd/build/column.min.js index 2bf0bb79..25c8e442 100644 --- a/amd/build/column.min.js +++ b/amd/build/column.min.js @@ -1,3 +1,3 @@ -define("mod_kanban/column",["exports","core/reactive","mod_kanban/selectors","mod_kanban/capabilities","mod_kanban/exporter","core/notification","core/str","core_form/modalform","mod_kanban/kanbancomponent","core/log"],(function(_exports,_reactive,_selectors,_capabilities,_exporter,_notification,Str,_modalform,_kanbancomponent,_log){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_selectors=_interopRequireDefault(_selectors),_capabilities=_interopRequireDefault(_capabilities),_exporter=_interopRequireDefault(_exporter),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_modalform=_interopRequireDefault(_modalform),_kanbancomponent=_interopRequireDefault(_kanbancomponent),_log=_interopRequireDefault(_log);class _default extends _kanbancomponent.default{static init(target){return new this({element:document.getElementById(target)})}create(){this.id=this.element.dataset.id}getWatchers(){return[{watch:"columns[".concat(this.id,"]:updated"),handler:this._columnUpdated},{watch:"columns[".concat(this.id,"]:deleted"),handler:this._columnDeleted},{watch:"cards:created",handler:this._cardCreated}]}stateReady(state){this.addEventListener(this.getElement(_selectors.default.DELETECOLUMN,this.id),"click",this._removeConfirm),this.addEventListener(this.getElement(_selectors.default.ADDCARDFIRST),"click",this._addCard),this.addEventListener(this.getElement(_selectors.default.ADDCOLUMN,this.id),"click",this._addColumn),this.addEventListener(this.getElement(_selectors.default.LOCKCOLUMN,this.id),"click",this._lockColumn),this.addEventListener(this.getElement(_selectors.default.UNLOCKCOLUMN,this.id),"click",this._unlockColumn),this.addEventListener(this.getElement(_selectors.default.EDITDETAILS,this.id),"click",this._editDetails),this.addEventListener(this.getElement(_selectors.default.SHOWHIDDEN),"click",this._showHidden),this.addEventListener(this.getElement(_selectors.default.HIDEHIDDEN),"click",this._hideHidden),this.draggable=!1,this.dragdrop=new _reactive.DragDrop(this),this.checkDragging(state),this.boardid=state.board.id,this.cmid=state.common.id}_removeConfirm(event){Str.get_strings([{key:"deletecolumn",component:"mod_kanban"},{key:"deletecolumnconfirm",component:"mod_kanban"},{key:"delete",component:"core"}]).then((strings=>(0,_notification.saveCancel)(strings[0],strings[1],strings[2],(()=>{this._removeColumn(event)})))).catch((error=>_log.default.debug(error)))}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}getDraggableData(){return{id:this.id,type:"column"}}checkDragging(state){void 0===state&&(state=this.reactive.stateManager.state),state.capabilities.get(_capabilities.default.MANAGECOLUMNS).value&&0==state.columns.get(this.id).locked?this.dragdrop.setDraggable(!0):this.dragdrop.setDraggable(!1)}validateDropData(dropdata){let type=null==dropdata?void 0:dropdata.type;return"card"==type||"column"==type}drop(dropdata,event){if("card"==dropdata.type){let cards=this.getElements(_selectors.default.CARD),aftercard=0;for(let i=0;i{e.classList.remove("mod_kanban_insert")}))}_addColumn(event){document.activeElement.blur();let target=event.target.closest(_selectors.default.ADDCOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("addColumn",data.id)}async _cardCreated(_ref){let{element:element}=_ref;if(element.kanban_column==this.id){let data=JSON.parse(JSON.stringify(element));Object.assign(data,_exporter.default.exportCapabilities(this.reactive.state));let placeholder=document.createElement("li");placeholder.setAttribute("data-id",data.id);let node=this.getElement(_selectors.default.COLUMNINNER,this.id);node.appendChild(placeholder);const newelement=(await this.renderComponent(placeholder,"mod_kanban/card",data)).getElement();node.replaceChild(newelement,placeholder)}}_addCard(event){document.activeElement.blur();let target=event.target.closest(_selectors.default.ADDCARD),data=Object.assign({},target.dataset);this.reactive.dispatch("addCard",data.columnid,0)}_columnUpdated(_ref2){let{element:element}=_ref2;const el=this.getElement(_selectors.default.COLUMNINNER,this.id);if(void 0!==element.sequence){let sequence=element.sequence.split(",");[...el.children].forEach((node=>{node.classList.contains("mod_kanban_card")&&!sequence.includes(node.dataset.id)&&el.removeChild(node)})),[...el.children].sort(((a,b)=>sequence.indexOf(a.dataset.id)>sequence.indexOf(b.dataset.id)?1:-1)).forEach((node=>el.appendChild(node)))}if(void 0!==element.locked&&(this.toggleClass(0!=element.locked,"mod_kanban_locked_column"),0!=element.locked?this.getElement(_selectors.default.INPLACEEDITABLE).removeAttribute("data-inplaceeditable"):this.getElement(_selectors.default.INPLACEEDITABLE).setAttribute("data-inplaceeditable","1")),void 0!==element.title){let doc=(new DOMParser).parseFromString(element.title,"text/html");this.getElement(_selectors.default.INPLACEEDITABLE).setAttribute("data-value",doc.documentElement.textContent),this.getElement(_selectors.default.INPLACEEDITABLE).querySelector("a").innerHTML=element.title}if(void 0!==element.options){let options=JSON.parse(element.options);this.toggleClass(options.autohide,"mod_kanban_autohide")}this.checkDragging()}_columnDeleted(){this.destroy()}_removeColumn(event){let target=event.target.closest(_selectors.default.DELETECOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("deleteColumn",data.id)}_lockColumn(event){let target=event.target.closest(_selectors.default.LOCKCOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("lockColumn",data.id)}_unlockColumn(event){let target=event.target.closest(_selectors.default.UNLOCKCOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("unlockColumn",data.id)}_editDetails(event){event.preventDefault();const modalForm=new _modalform.default({formClass:"mod_kanban\\form\\edit_column_form",args:{id:this.id,boardid:this.boardid,cmid:this.cmid},modalConfig:{title:(0,Str.get_string)("editcolumn","mod_kanban")},returnFocus:this.getElement()});this.addEventListener(modalForm,modalForm.events.FORM_SUBMITTED,this._updateColumn),modalForm.show()}_updateColumn(event){this.reactive.dispatch("processUpdates",event.detail)}_showHidden(){this.getElement().classList.add("mod_kanban_show_hidden")}_hideHidden(){this.getElement().classList.remove("mod_kanban_show_hidden")}}return _exports.default=_default,_exports.default})); +define("mod_kanban/column",["exports","core/reactive","mod_kanban/selectors","mod_kanban/capabilities","mod_kanban/exporter","core/notification","core/str","core_form/modalform","mod_kanban/kanbancomponent","core/log"],(function(_exports,_reactive,_selectors,_capabilities,_exporter,_notification,Str,_modalform,_kanbancomponent,_log){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_selectors=_interopRequireDefault(_selectors),_capabilities=_interopRequireDefault(_capabilities),_exporter=_interopRequireDefault(_exporter),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_modalform=_interopRequireDefault(_modalform),_kanbancomponent=_interopRequireDefault(_kanbancomponent),_log=_interopRequireDefault(_log);class _default extends _kanbancomponent.default{static init(target){return new this({element:document.getElementById(target)})}create(){this.id=this.element.dataset.id}getWatchers(){return[{watch:"columns[".concat(this.id,"]:updated"),handler:this._columnUpdated},{watch:"columns[".concat(this.id,"]:deleted"),handler:this._columnDeleted},{watch:"cards:created",handler:this._cardCreated}]}stateReady(state){this.addEventListener(this.getElement(_selectors.default.DELETECOLUMN,this.id),"click",this._removeConfirm),this.addEventListener(this.getElement(_selectors.default.ADDCARDFIRST),"click",this._addCard),this.addEventListener(this.getElement(_selectors.default.ADDCOLUMN,this.id),"click",this._addColumn),this.addEventListener(this.getElement(_selectors.default.LOCKCOLUMN,this.id),"click",this._lockColumn),this.addEventListener(this.getElement(_selectors.default.UNLOCKCOLUMN,this.id),"click",this._unlockColumn),this.addEventListener(this.getElement(_selectors.default.EDITDETAILS,this.id),"click",this._editDetails),this.addEventListener(this.getElement(_selectors.default.SHOWHIDDEN),"click",this._showHidden),this.addEventListener(this.getElement(_selectors.default.HIDEHIDDEN),"click",this._hideHidden),this.draggable=!1,this.dragdrop=new _reactive.DragDrop(this),this.checkDragging(state),this.boardid=state.board.id,this.cmid=state.common.id}_removeConfirm(event){Str.get_strings([{key:"deletecolumn",component:"mod_kanban"},{key:"deletecolumnconfirm",component:"mod_kanban"},{key:"delete",component:"core"}]).then((strings=>(0,_notification.saveCancel)(strings[0],strings[1],strings[2],(()=>{this._removeColumn(event)})))).catch((error=>_log.default.debug(error)))}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}getDraggableData(){return{id:this.id,type:"column"}}checkDragging(state){void 0===state&&(state=this.reactive.stateManager.state),state.capabilities.get(_capabilities.default.MANAGECOLUMNS).value&&0==state.columns.get(this.id).locked?this.dragdrop.setDraggable(!0):this.dragdrop.setDraggable(!1)}validateDropData(dropdata){let type=null==dropdata?void 0:dropdata.type;return"card"==type||"column"==type}drop(dropdata,event){if("card"==dropdata.type){let cards=this.getElements(_selectors.default.CARD),aftercard=0;for(let i=0;i{e.classList.remove("mod_kanban_insert")}))}_addColumn(event){document.activeElement.blur();let target=event.target.closest(_selectors.default.ADDCOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("addColumn",data.id)}async _cardCreated(_ref){let{element:element}=_ref;if(element.kanban_column==this.id){let data=JSON.parse(JSON.stringify(element));Object.assign(data,_exporter.default.exportCapabilities(this.reactive.state));let placeholder=document.createElement("li");placeholder.setAttribute("data-id",data.id);let node=this.getElement(_selectors.default.COLUMNINNER,this.id);node.appendChild(placeholder);const newelement=(await this.renderComponent(placeholder,"mod_kanban/card",data)).getElement();node.replaceChild(newelement,placeholder)}}_addCard(event){document.activeElement.blur();let target=event.target.closest(_selectors.default.ADDCARD),data=Object.assign({},target.dataset);this.reactive.dispatch("addCard",data.columnid,0)}_columnUpdated(_ref2){let{element:element}=_ref2;const el=this.getElement(_selectors.default.COLUMNINNER,this.id);if(void 0!==element.sequence){let sequence=element.sequence.split(",");[...el.children].forEach((node=>{node.classList.contains("mod_kanban_card")&&!sequence.includes(node.dataset.id)&&el.removeChild(node)})),[...el.children].sort(((a,b)=>sequence.indexOf(a.dataset.id)>sequence.indexOf(b.dataset.id)?1:-1)).forEach((node=>el.appendChild(node)))}if(void 0!==element.locked&&(this.toggleClass(0!=element.locked,"mod_kanban_locked_column"),0!=element.locked?this.getElement(_selectors.default.INPLACEEDITABLE).removeAttribute("data-inplaceeditable"):this.getElement(_selectors.default.INPLACEEDITABLE).setAttribute("data-inplaceeditable","1")),void 0!==element.title){let doc=(new DOMParser).parseFromString(element.title,"text/html");this.getElement(_selectors.default.INPLACEEDITABLE).setAttribute("data-value",doc.documentElement.textContent),this.getElement(_selectors.default.INPLACEEDITABLE).querySelector("a").innerHTML=element.title}if(void 0!==element.options){let options=JSON.parse(element.options);this.toggleClass(options.autohide,"mod_kanban_autohide"),this.toggleClass(options.wiplimit>0,"mod_kanban_wiplimit")}this.getElement(_selectors.default.CARDCOUNT).innerHTML=this.getElements(_selectors.default.CARD).length,this.checkDragging()}_columnDeleted(){this.destroy()}_removeColumn(event){let target=event.target.closest(_selectors.default.DELETECOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("deleteColumn",data.id)}_lockColumn(event){let target=event.target.closest(_selectors.default.LOCKCOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("lockColumn",data.id)}_unlockColumn(event){let target=event.target.closest(_selectors.default.UNLOCKCOLUMN),data=Object.assign({},target.dataset);this.reactive.dispatch("unlockColumn",data.id)}_editDetails(event){event.preventDefault();const modalForm=new _modalform.default({formClass:"mod_kanban\\form\\edit_column_form",args:{id:this.id,boardid:this.boardid,cmid:this.cmid},modalConfig:{title:(0,Str.get_string)("editcolumn","mod_kanban")},returnFocus:this.getElement()});this.addEventListener(modalForm,modalForm.events.FORM_SUBMITTED,this._updateColumn),modalForm.show()}_updateColumn(event){this.reactive.dispatch("processUpdates",event.detail)}_showHidden(){this.getElement().classList.add("mod_kanban_show_hidden")}_hideHidden(){this.getElement().classList.remove("mod_kanban_show_hidden")}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=column.min.js.map \ No newline at end of file diff --git a/amd/build/column.min.js.map b/amd/build/column.min.js.map index b0846614..b0fb6642 100644 --- a/amd/build/column.min.js.map +++ b/amd/build/column.min.js.map @@ -1 +1 @@ -{"version":3,"file":"column.min.js","sources":["../src/column.js"],"sourcesContent":["import {DragDrop} from 'core/reactive';\nimport selectors from 'mod_kanban/selectors';\nimport capabilities from 'mod_kanban/capabilities';\nimport exporter from 'mod_kanban/exporter';\nimport {saveCancel} from 'core/notification';\nimport * as Str from 'core/str';\nimport {get_string as getString} from 'core/str';\nimport ModalForm from 'core_form/modalform';\nimport KanbanComponent from 'mod_kanban/kanbancomponent';\nimport Log from \"core/log\";\n\n/**\n * Component representing a column in a kanban board.\n */\nexport default class extends KanbanComponent {\n /**\n * Function to initialize component, called by mustache template.\n * @param {*} target The id of the HTMLElement to attach to\n * @returns {BaseComponent} New component attached to the HTMLElement represented by target\n */\n static init(target) {\n let element = document.getElementById(target);\n return new this({\n element: element,\n });\n }\n\n /**\n * Called after the component was created.\n */\n create() {\n this.id = this.element.dataset.id;\n }\n\n /**\n * Watchers for this component.\n * @returns {array}\n */\n getWatchers() {\n return [\n {watch: `columns[${this.id}]:updated`, handler: this._columnUpdated},\n {watch: `columns[${this.id}]:deleted`, handler: this._columnDeleted},\n {watch: `cards:created`, handler: this._cardCreated}\n ];\n }\n\n /**\n * Called once when state is ready, attaching event listeners and initializing drag and drop.\n * @param {object} state\n */\n stateReady(state) {\n this.addEventListener(\n this.getElement(selectors.DELETECOLUMN, this.id),\n 'click',\n this._removeConfirm\n );\n this.addEventListener(\n this.getElement(selectors.ADDCARDFIRST),\n 'click',\n this._addCard\n );\n this.addEventListener(\n this.getElement(selectors.ADDCOLUMN, this.id),\n 'click',\n this._addColumn\n );\n this.addEventListener(\n this.getElement(selectors.LOCKCOLUMN, this.id),\n 'click',\n this._lockColumn\n );\n this.addEventListener(\n this.getElement(selectors.UNLOCKCOLUMN, this.id),\n 'click',\n this._unlockColumn\n );\n this.addEventListener(\n this.getElement(selectors.EDITDETAILS, this.id),\n 'click',\n this._editDetails\n );\n this.addEventListener(\n this.getElement(selectors.SHOWHIDDEN),\n 'click',\n this._showHidden\n );\n this.addEventListener(\n this.getElement(selectors.HIDEHIDDEN),\n 'click',\n this._hideHidden\n );\n this.draggable = false;\n this.dragdrop = new DragDrop(this);\n this.checkDragging(state);\n this.boardid = state.board.id;\n this.cmid = state.common.id;\n }\n\n /**\n * Display confirmation modal for deleting a card.\n * @param {*} event\n */\n _removeConfirm(event) {\n Str.get_strings([\n {key: 'deletecolumn', component: 'mod_kanban'},\n {key: 'deletecolumnconfirm', component: 'mod_kanban'},\n {key: 'delete', component: 'core'},\n ]).then((strings) => {\n return saveCancel(\n strings[0],\n strings[1],\n strings[2],\n () => {\n this._removeColumn(event);\n }\n );\n }).catch((error) => Log.debug(error));\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} the draggable data.\n */\n getDraggableData() {\n return {id: this.id, type: 'column'};\n }\n\n /**\n * Conditionally enable / disable dragging.\n * @param {*} state\n */\n checkDragging(state) {\n if (state === undefined) {\n state = this.reactive.stateManager.state;\n }\n\n if (state.capabilities.get(capabilities.MANAGECOLUMNS).value && state.columns.get(this.id).locked == 0) {\n this.dragdrop.setDraggable(true);\n } else {\n this.dragdrop.setDraggable(false);\n }\n }\n\n /**\n * Validate draggable data. This component accepts cards and columns.\n * @param {object} dropdata\n * @returns {boolean} if the data is valid for this drop-zone.\n */\n validateDropData(dropdata) {\n let type = dropdata?.type;\n return type == 'card' || type == 'column';\n }\n\n /**\n * Executed when a valid dropdata is dropped over the drop-zone.\n * @param {object} dropdata\n * @param {object} event\n */\n drop(dropdata, event) {\n if (dropdata.type == 'card') {\n let cards = this.getElements(selectors.CARD);\n let aftercard = 0;\n for (let i = 0; i < cards.length; i++) {\n if (cards[i].offsetTop + cards[i].clientHeight / 2 <= event.layerY) {\n aftercard = cards[i].dataset.id;\n }\n }\n this.reactive.dispatch('moveCard', dropdata.id, this.id, aftercard);\n }\n if (dropdata.type == 'column') {\n if (dropdata.id != this.id) {\n this.reactive.dispatch('moveColumn', dropdata.id, this.id);\n }\n }\n }\n\n /**\n * Show some visual hints to the user.\n * @param {object} dropdata\n * @param {object} event\n */\n showDropZone(dropdata, event) {\n if (dropdata.type == 'card') {\n let cards = this.getElements(selectors.CARD);\n let aftercard = 0;\n for (let i = 0; i < cards.length; i++) {\n if (cards[i].offsetTop + cards[i].clientHeight / 2 <= event.layerY) {\n aftercard = cards[i].dataset.id;\n }\n }\n if (aftercard == 0) {\n this.getElement(selectors.ADDCARDCONTAINER).classList.add('mod_kanban_insert');\n } else {\n this.getElement(selectors.ADDCARDCONTAINER, aftercard).classList.add('mod_kanban_insert');\n }\n }\n if (dropdata.type == 'column') {\n this.getElement(selectors.ADDCOLUMNCONTAINER).classList.add('mod_kanban_insert');\n }\n }\n\n /**\n * Remove visual hints to the user.\n */\n hideDropZone() {\n this.getElement(selectors.ADDCOLUMNCONTAINER).classList.remove('mod_kanban_insert');\n this.getElements(selectors.ADDCARDCONTAINER).forEach((e) => {\n e.classList.remove('mod_kanban_insert');\n });\n }\n\n /**\n * Dispatch event to add a column after this column.\n * @param {*} event\n */\n _addColumn(event) {\n document.activeElement.blur();\n let target = event.target.closest(selectors.ADDCOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('addColumn', data.id);\n }\n\n /**\n * Called when a card was created in this column.\n * @param {*} param0\n */\n async _cardCreated({element}) {\n if (element.kanban_column == this.id) {\n let data = JSON.parse(JSON.stringify(element));\n Object.assign(data, exporter.exportCapabilities(this.reactive.state));\n let placeholder = document.createElement('li');\n placeholder.setAttribute('data-id', data.id);\n let node = this.getElement(selectors.COLUMNINNER, this.id);\n node.appendChild(placeholder);\n const newcomponent = await this.renderComponent(placeholder, 'mod_kanban/card', data);\n const newelement = newcomponent.getElement();\n node.replaceChild(newelement, placeholder);\n }\n }\n\n /**\n * Dispatch event to add a card in this column.\n * @param {*} event\n */\n _addCard(event) {\n document.activeElement.blur();\n let target = event.target.closest(selectors.ADDCARD);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('addCard', data.columnid, 0);\n }\n\n /**\n * Called when column is updated.\n * @param {*} param0\n */\n _columnUpdated({element}) {\n const el = this.getElement(selectors.COLUMNINNER, this.id);\n if (element.sequence !== undefined) {\n let sequence = element.sequence.split(',');\n // Remove all cards from frontend that are no longer present in the database.\n [...el.children]\n .forEach((node) => {\n if (node.classList.contains('mod_kanban_card') && !sequence.includes(node.dataset.id)) {\n el.removeChild(node);\n }\n });\n // Reorder cards according to sequence from the database.\n [...el.children]\n .sort((a, b) => sequence.indexOf(a.dataset.id) > sequence.indexOf(b.dataset.id) ? 1 : -1)\n .forEach(node => el.appendChild(node));\n }\n if (element.locked !== undefined) {\n this.toggleClass(element.locked != 0, 'mod_kanban_locked_column');\n // Inplace editing of the column title is disabled if the column is locked.\n if (element.locked != 0) {\n this.getElement(selectors.INPLACEEDITABLE).removeAttribute('data-inplaceeditable');\n } else {\n this.getElement(selectors.INPLACEEDITABLE).setAttribute('data-inplaceeditable', '1');\n }\n }\n // Update data for inplace editing if title was updated (this is important if title was modified by another user).\n if (element.title !== undefined) {\n // For Moodle inplace editing title is once needed plain and once with html entities encoded.\n // This avoids double encoding of html entities as the value of \"data-value\" is exactly what is shown\n // in the input field when clicking on the inplace editable.\n let doc = new DOMParser().parseFromString(element.title, 'text/html');\n this.getElement(selectors.INPLACEEDITABLE).setAttribute('data-value', doc.documentElement.textContent);\n this.getElement(selectors.INPLACEEDITABLE).querySelector('a').innerHTML = element.title;\n }\n // Only autohide option is relevant for the frontend for now. autoclose option is handled by the backend.\n if (element.options !== undefined) {\n let options = JSON.parse(element.options);\n this.toggleClass(options.autohide, 'mod_kanban_autohide');\n }\n // Enable/disable dragging (e.g. if column is locked).\n this.checkDragging();\n }\n\n /**\n * Called when this column is deleted.\n */\n _columnDeleted() {\n this.destroy();\n }\n\n /**\n * Dispatch event to remove this column.\n * @param {*} event\n */\n _removeColumn(event) {\n let target = event.target.closest(selectors.DELETECOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('deleteColumn', data.id);\n }\n\n /**\n * Dispatch event to lock this column.\n * @param {*} event\n */\n _lockColumn(event) {\n let target = event.target.closest(selectors.LOCKCOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('lockColumn', data.id);\n }\n\n /**\n * Dispatch event to unlock this column.\n * @param {*} event\n */\n _unlockColumn(event) {\n let target = event.target.closest(selectors.UNLOCKCOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('unlockColumn', data.id);\n }\n\n /**\n * Show modal form to edit column details.\n * @param {*} event\n */\n _editDetails(event) {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n formClass: \"mod_kanban\\\\form\\\\edit_column_form\",\n args: {\n id: this.id,\n boardid: this.boardid,\n cmid: this.cmid\n },\n modalConfig: {title: getString('editcolumn', 'mod_kanban')},\n returnFocus: this.getElement(),\n });\n this.addEventListener(modalForm, modalForm.events.FORM_SUBMITTED, this._updateColumn);\n modalForm.show();\n }\n\n /**\n * Dispatch an event to update column data from the detail modal.\n * @param {*} event\n */\n _updateColumn(event) {\n this.reactive.dispatch('processUpdates', event.detail);\n }\n\n /**\n * Show hidden cards.\n */\n _showHidden() {\n this.getElement().classList.add('mod_kanban_show_hidden');\n }\n\n /**\n * Hide hidden cards.\n */\n _hideHidden() {\n this.getElement().classList.remove('mod_kanban_show_hidden');\n }\n}\n"],"names":["KanbanComponent","target","this","element","document","getElementById","create","id","dataset","getWatchers","watch","handler","_columnUpdated","_columnDeleted","_cardCreated","stateReady","state","addEventListener","getElement","selectors","DELETECOLUMN","_removeConfirm","ADDCARDFIRST","_addCard","ADDCOLUMN","_addColumn","LOCKCOLUMN","_lockColumn","UNLOCKCOLUMN","_unlockColumn","EDITDETAILS","_editDetails","SHOWHIDDEN","_showHidden","HIDEHIDDEN","_hideHidden","draggable","dragdrop","DragDrop","checkDragging","boardid","board","cmid","common","event","Str","get_strings","key","component","then","strings","_removeColumn","catch","error","Log","debug","destroy","undefined","unregister","getDraggableData","type","reactive","stateManager","capabilities","get","MANAGECOLUMNS","value","columns","locked","setDraggable","validateDropData","dropdata","drop","cards","getElements","CARD","aftercard","i","length","offsetTop","clientHeight","layerY","dispatch","showDropZone","ADDCARDCONTAINER","classList","add","ADDCOLUMNCONTAINER","hideDropZone","remove","forEach","e","activeElement","blur","closest","data","Object","assign","kanban_column","JSON","parse","stringify","exporter","exportCapabilities","placeholder","createElement","setAttribute","node","COLUMNINNER","appendChild","newelement","renderComponent","replaceChild","ADDCARD","columnid","el","sequence","split","children","contains","includes","removeChild","sort","a","b","indexOf","toggleClass","INPLACEEDITABLE","removeAttribute","title","doc","DOMParser","parseFromString","documentElement","textContent","querySelector","innerHTML","options","autohide","preventDefault","modalForm","ModalForm","formClass","args","modalConfig","returnFocus","events","FORM_SUBMITTED","_updateColumn","show","detail"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"column.min.js","sources":["../src/column.js"],"sourcesContent":["import {DragDrop} from 'core/reactive';\nimport selectors from 'mod_kanban/selectors';\nimport capabilities from 'mod_kanban/capabilities';\nimport exporter from 'mod_kanban/exporter';\nimport {saveCancel} from 'core/notification';\nimport * as Str from 'core/str';\nimport {get_string as getString} from 'core/str';\nimport ModalForm from 'core_form/modalform';\nimport KanbanComponent from 'mod_kanban/kanbancomponent';\nimport Log from \"core/log\";\n\n/**\n * Component representing a column in a kanban board.\n */\nexport default class extends KanbanComponent {\n /**\n * Function to initialize component, called by mustache template.\n * @param {*} target The id of the HTMLElement to attach to\n * @returns {BaseComponent} New component attached to the HTMLElement represented by target\n */\n static init(target) {\n let element = document.getElementById(target);\n return new this({\n element: element,\n });\n }\n\n /**\n * Called after the component was created.\n */\n create() {\n this.id = this.element.dataset.id;\n }\n\n /**\n * Watchers for this component.\n * @returns {array}\n */\n getWatchers() {\n return [\n {watch: `columns[${this.id}]:updated`, handler: this._columnUpdated},\n {watch: `columns[${this.id}]:deleted`, handler: this._columnDeleted},\n {watch: `cards:created`, handler: this._cardCreated}\n ];\n }\n\n /**\n * Called once when state is ready, attaching event listeners and initializing drag and drop.\n * @param {object} state\n */\n stateReady(state) {\n this.addEventListener(\n this.getElement(selectors.DELETECOLUMN, this.id),\n 'click',\n this._removeConfirm\n );\n this.addEventListener(\n this.getElement(selectors.ADDCARDFIRST),\n 'click',\n this._addCard\n );\n this.addEventListener(\n this.getElement(selectors.ADDCOLUMN, this.id),\n 'click',\n this._addColumn\n );\n this.addEventListener(\n this.getElement(selectors.LOCKCOLUMN, this.id),\n 'click',\n this._lockColumn\n );\n this.addEventListener(\n this.getElement(selectors.UNLOCKCOLUMN, this.id),\n 'click',\n this._unlockColumn\n );\n this.addEventListener(\n this.getElement(selectors.EDITDETAILS, this.id),\n 'click',\n this._editDetails\n );\n this.addEventListener(\n this.getElement(selectors.SHOWHIDDEN),\n 'click',\n this._showHidden\n );\n this.addEventListener(\n this.getElement(selectors.HIDEHIDDEN),\n 'click',\n this._hideHidden\n );\n this.draggable = false;\n this.dragdrop = new DragDrop(this);\n this.checkDragging(state);\n this.boardid = state.board.id;\n this.cmid = state.common.id;\n }\n\n /**\n * Display confirmation modal for deleting a card.\n * @param {*} event\n */\n _removeConfirm(event) {\n Str.get_strings([\n {key: 'deletecolumn', component: 'mod_kanban'},\n {key: 'deletecolumnconfirm', component: 'mod_kanban'},\n {key: 'delete', component: 'core'},\n ]).then((strings) => {\n return saveCancel(\n strings[0],\n strings[1],\n strings[2],\n () => {\n this._removeColumn(event);\n }\n );\n }).catch((error) => Log.debug(error));\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} the draggable data.\n */\n getDraggableData() {\n return {id: this.id, type: 'column'};\n }\n\n /**\n * Conditionally enable / disable dragging.\n * @param {*} state\n */\n checkDragging(state) {\n if (state === undefined) {\n state = this.reactive.stateManager.state;\n }\n\n if (state.capabilities.get(capabilities.MANAGECOLUMNS).value && state.columns.get(this.id).locked == 0) {\n this.dragdrop.setDraggable(true);\n } else {\n this.dragdrop.setDraggable(false);\n }\n }\n\n /**\n * Validate draggable data. This component accepts cards and columns.\n * @param {object} dropdata\n * @returns {boolean} if the data is valid for this drop-zone.\n */\n validateDropData(dropdata) {\n let type = dropdata?.type;\n return type == 'card' || type == 'column';\n }\n\n /**\n * Executed when a valid dropdata is dropped over the drop-zone.\n * @param {object} dropdata\n * @param {object} event\n */\n drop(dropdata, event) {\n if (dropdata.type == 'card') {\n let cards = this.getElements(selectors.CARD);\n let aftercard = 0;\n for (let i = 0; i < cards.length; i++) {\n if (cards[i].offsetTop + cards[i].clientHeight / 2 <= event.layerY) {\n aftercard = cards[i].dataset.id;\n }\n }\n this.reactive.dispatch('moveCard', dropdata.id, this.id, aftercard);\n }\n if (dropdata.type == 'column') {\n if (dropdata.id != this.id) {\n this.reactive.dispatch('moveColumn', dropdata.id, this.id);\n }\n }\n }\n\n /**\n * Show some visual hints to the user.\n * @param {object} dropdata\n * @param {object} event\n */\n showDropZone(dropdata, event) {\n if (dropdata.type == 'card') {\n let cards = this.getElements(selectors.CARD);\n let aftercard = 0;\n for (let i = 0; i < cards.length; i++) {\n if (cards[i].offsetTop + cards[i].clientHeight / 2 <= event.layerY) {\n aftercard = cards[i].dataset.id;\n }\n }\n if (aftercard == 0) {\n this.getElement(selectors.ADDCARDCONTAINER).classList.add('mod_kanban_insert');\n } else {\n this.getElement(selectors.ADDCARDCONTAINER, aftercard).classList.add('mod_kanban_insert');\n }\n }\n if (dropdata.type == 'column') {\n this.getElement(selectors.ADDCOLUMNCONTAINER).classList.add('mod_kanban_insert');\n }\n }\n\n /**\n * Remove visual hints to the user.\n */\n hideDropZone() {\n this.getElement(selectors.ADDCOLUMNCONTAINER).classList.remove('mod_kanban_insert');\n this.getElements(selectors.ADDCARDCONTAINER).forEach((e) => {\n e.classList.remove('mod_kanban_insert');\n });\n }\n\n /**\n * Dispatch event to add a column after this column.\n * @param {*} event\n */\n _addColumn(event) {\n document.activeElement.blur();\n let target = event.target.closest(selectors.ADDCOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('addColumn', data.id);\n }\n\n /**\n * Called when a card was created in this column.\n * @param {*} param0\n */\n async _cardCreated({element}) {\n if (element.kanban_column == this.id) {\n let data = JSON.parse(JSON.stringify(element));\n Object.assign(data, exporter.exportCapabilities(this.reactive.state));\n let placeholder = document.createElement('li');\n placeholder.setAttribute('data-id', data.id);\n let node = this.getElement(selectors.COLUMNINNER, this.id);\n node.appendChild(placeholder);\n const newcomponent = await this.renderComponent(placeholder, 'mod_kanban/card', data);\n const newelement = newcomponent.getElement();\n node.replaceChild(newelement, placeholder);\n }\n }\n\n /**\n * Dispatch event to add a card in this column.\n * @param {*} event\n */\n _addCard(event) {\n document.activeElement.blur();\n let target = event.target.closest(selectors.ADDCARD);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('addCard', data.columnid, 0);\n }\n\n /**\n * Called when column is updated.\n * @param {*} param0\n */\n _columnUpdated({element}) {\n const el = this.getElement(selectors.COLUMNINNER, this.id);\n if (element.sequence !== undefined) {\n let sequence = element.sequence.split(',');\n // Remove all cards from frontend that are no longer present in the database.\n [...el.children]\n .forEach((node) => {\n if (node.classList.contains('mod_kanban_card') && !sequence.includes(node.dataset.id)) {\n el.removeChild(node);\n }\n });\n // Reorder cards according to sequence from the database.\n [...el.children]\n .sort((a, b) => sequence.indexOf(a.dataset.id) > sequence.indexOf(b.dataset.id) ? 1 : -1)\n .forEach(node => el.appendChild(node));\n }\n if (element.locked !== undefined) {\n this.toggleClass(element.locked != 0, 'mod_kanban_locked_column');\n // Inplace editing of the column title is disabled if the column is locked.\n if (element.locked != 0) {\n this.getElement(selectors.INPLACEEDITABLE).removeAttribute('data-inplaceeditable');\n } else {\n this.getElement(selectors.INPLACEEDITABLE).setAttribute('data-inplaceeditable', '1');\n }\n }\n // Update data for inplace editing if title was updated (this is important if title was modified by another user).\n if (element.title !== undefined) {\n // For Moodle inplace editing title is once needed plain and once with html entities encoded.\n // This avoids double encoding of html entities as the value of \"data-value\" is exactly what is shown\n // in the input field when clicking on the inplace editable.\n let doc = new DOMParser().parseFromString(element.title, 'text/html');\n this.getElement(selectors.INPLACEEDITABLE).setAttribute('data-value', doc.documentElement.textContent);\n this.getElement(selectors.INPLACEEDITABLE).querySelector('a').innerHTML = element.title;\n }\n // Only autohide option is relevant for the frontend for now. autoclose option is handled by the backend.\n if (element.options !== undefined) {\n let options = JSON.parse(element.options);\n this.toggleClass(options.autohide, 'mod_kanban_autohide');\n this.toggleClass(options.wiplimit > 0, 'mod_kanban_wiplimit');\n }\n this.getElement(selectors.CARDCOUNT).innerHTML = this.getElements(selectors.CARD).length;\n // Enable/disable dragging (e.g. if column is locked).\n this.checkDragging();\n }\n\n /**\n * Called when this column is deleted.\n */\n _columnDeleted() {\n this.destroy();\n }\n\n /**\n * Dispatch event to remove this column.\n * @param {*} event\n */\n _removeColumn(event) {\n let target = event.target.closest(selectors.DELETECOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('deleteColumn', data.id);\n }\n\n /**\n * Dispatch event to lock this column.\n * @param {*} event\n */\n _lockColumn(event) {\n let target = event.target.closest(selectors.LOCKCOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('lockColumn', data.id);\n }\n\n /**\n * Dispatch event to unlock this column.\n * @param {*} event\n */\n _unlockColumn(event) {\n let target = event.target.closest(selectors.UNLOCKCOLUMN);\n let data = Object.assign({}, target.dataset);\n this.reactive.dispatch('unlockColumn', data.id);\n }\n\n /**\n * Show modal form to edit column details.\n * @param {*} event\n */\n _editDetails(event) {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n formClass: \"mod_kanban\\\\form\\\\edit_column_form\",\n args: {\n id: this.id,\n boardid: this.boardid,\n cmid: this.cmid\n },\n modalConfig: {title: getString('editcolumn', 'mod_kanban')},\n returnFocus: this.getElement(),\n });\n this.addEventListener(modalForm, modalForm.events.FORM_SUBMITTED, this._updateColumn);\n modalForm.show();\n }\n\n /**\n * Dispatch an event to update column data from the detail modal.\n * @param {*} event\n */\n _updateColumn(event) {\n this.reactive.dispatch('processUpdates', event.detail);\n }\n\n /**\n * Show hidden cards.\n */\n _showHidden() {\n this.getElement().classList.add('mod_kanban_show_hidden');\n }\n\n /**\n * Hide hidden cards.\n */\n _hideHidden() {\n this.getElement().classList.remove('mod_kanban_show_hidden');\n }\n}\n"],"names":["KanbanComponent","target","this","element","document","getElementById","create","id","dataset","getWatchers","watch","handler","_columnUpdated","_columnDeleted","_cardCreated","stateReady","state","addEventListener","getElement","selectors","DELETECOLUMN","_removeConfirm","ADDCARDFIRST","_addCard","ADDCOLUMN","_addColumn","LOCKCOLUMN","_lockColumn","UNLOCKCOLUMN","_unlockColumn","EDITDETAILS","_editDetails","SHOWHIDDEN","_showHidden","HIDEHIDDEN","_hideHidden","draggable","dragdrop","DragDrop","checkDragging","boardid","board","cmid","common","event","Str","get_strings","key","component","then","strings","_removeColumn","catch","error","Log","debug","destroy","undefined","unregister","getDraggableData","type","reactive","stateManager","capabilities","get","MANAGECOLUMNS","value","columns","locked","setDraggable","validateDropData","dropdata","drop","cards","getElements","CARD","aftercard","i","length","offsetTop","clientHeight","layerY","dispatch","showDropZone","ADDCARDCONTAINER","classList","add","ADDCOLUMNCONTAINER","hideDropZone","remove","forEach","e","activeElement","blur","closest","data","Object","assign","kanban_column","JSON","parse","stringify","exporter","exportCapabilities","placeholder","createElement","setAttribute","node","COLUMNINNER","appendChild","newelement","renderComponent","replaceChild","ADDCARD","columnid","el","sequence","split","children","contains","includes","removeChild","sort","a","b","indexOf","toggleClass","INPLACEEDITABLE","removeAttribute","title","doc","DOMParser","parseFromString","documentElement","textContent","querySelector","innerHTML","options","autohide","wiplimit","CARDCOUNT","preventDefault","modalForm","ModalForm","formClass","args","modalConfig","returnFocus","events","FORM_SUBMITTED","_updateColumn","show","detail"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/exporter.min.js b/amd/build/exporter.min.js index adb89760..5c0c88c0 100644 --- a/amd/build/exporter.min.js +++ b/amd/build/exporter.min.js @@ -1,3 +1,3 @@ -define("mod_kanban/exporter",["exports","mod_kanban/capabilities"],(function(_exports,_capabilities){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_capabilities=(obj=_capabilities)&&obj.__esModule?obj:{default:obj};return _exports.default=class{static exportStateForTemplate(state){let columnOrder=state.board.sequence.split(","),columns=[],hascolumns=""!=state.board.sequence;hascolumns&&(columns=columnOrder.map((value=>this.exportCardsForColumn(state,value))));let showactionmenu=1==state.common.userboards||""!=state.common.groupselector||state.capabilities.get(_capabilities.default.MANAGEBOARD).value||2==state.common.userboards&&state.capabilities.get(_capabilities.default.VIEWALLBOARDS).value;return Object.assign({cmid:state.common.id,id:state.board.id,sequence:state.board.sequence,hascolumns:hascolumns,columns:columns,locked:state.board.locked,hastemplate:0!=state.common.template,istemplate:0!=state.board.template,heading:state.board.heading,groupselector:state.common.groupselector,userboards:state.common.userboards,history:state.common.history&&state.capabilities.get(_capabilities.default.VIEWHISTORY).value,groupmode:state.common.groupmode,ismyuserboard:state.common.userid==state.board.userid,myuserid:state.common.userid,showactionmenu:showactionmenu,userboardsonly:2==state.common.userboards,iscourseboard:0==state.board.userid&&0==state.board.groupid&&0==state.board.template,users:JSON.parse(JSON.stringify(state.users))},this.exportCapabilities(state))}static exportCardsForColumn(state,columnid){let col=JSON.parse(JSON.stringify(state.columns.get(columnid))),options=JSON.parse(col.options);if(col.hascards=""!=col.sequence,col.autoclose=options.autoclose,col.autohide=options.autohide,col.hascards){let cardOrder=col.sequence.split(",");col.cards=cardOrder.map((value=>this.exportCard(state,value)))}return col}static exportCard(state,cardid){let card={id:cardid,title:"-",assignees:[],options:"{}",canedit:!1};void 0!==state.cards.get(cardid)&&(card=JSON.parse(JSON.stringify(state.cards.get(cardid)))),card.cardid=card.id,card.hasassignees=card.assignees.length>0;let options=JSON.parse(card.options);return card.hasassignees&&"number"==typeof card.assignees[0]&&(card.assignees=card.assignees.map((userid=>state.users.get(userid))),card.assignees=[...new Set(card.assignees)]),Object.assign(card,options)}static exportCapabilities(state){let capabilities=[];return state.capabilities.forEach((c=>{capabilities[c.id]=c.value})),Object.assign({},capabilities)}static exportDiscussion(state,cardId){let d=[];return state.discussions.forEach((c=>{c.kanban_card==cardId&&d.push(c)})),d=d.sort(((a,b)=>parseInt(a.timecreated)>parseInt(b.timecreated))),d}static exportHistory(state,cardId){let d=[];return state.history.forEach((c=>{c.kanban_card==cardId&&d.push(c)})),d=d.sort(((a,b)=>parseInt(a.timestamp)>parseInt(b.timestamp))),d}},_exports.default})); +define("mod_kanban/exporter",["exports","mod_kanban/capabilities"],(function(_exports,_capabilities){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_capabilities=(obj=_capabilities)&&obj.__esModule?obj:{default:obj};return _exports.default=class{static exportStateForTemplate(state){let columnOrder=state.board.sequence.split(","),columns=[],hascolumns=""!=state.board.sequence;hascolumns&&(columns=columnOrder.map((value=>this.exportCardsForColumn(state,value))));let showactionmenu=1==state.common.userboards||""!=state.common.groupselector||state.capabilities.get(_capabilities.default.MANAGEBOARD).value||2==state.common.userboards&&state.capabilities.get(_capabilities.default.VIEWALLBOARDS).value;return Object.assign({cmid:state.common.id,id:state.board.id,sequence:state.board.sequence,hascolumns:hascolumns,columns:columns,locked:state.board.locked,hastemplate:0!=state.common.template,istemplate:0!=state.board.template,heading:state.board.heading,groupselector:state.common.groupselector,userboards:state.common.userboards,history:state.common.history&&state.capabilities.get(_capabilities.default.VIEWHISTORY).value,groupmode:state.common.groupmode,ismyuserboard:state.common.userid==state.board.userid,myuserid:state.common.userid,showactionmenu:showactionmenu,userboardsonly:2==state.common.userboards,iscourseboard:0==state.board.userid&&0==state.board.groupid&&0==state.board.template,users:JSON.parse(JSON.stringify(state.users))},this.exportCapabilities(state))}static exportCardsForColumn(state,columnid){let col=JSON.parse(JSON.stringify(state.columns.get(columnid))),options=JSON.parse(col.options);if(col.hascards=""!=col.sequence,col.autoclose=options.autoclose,col.autohide=options.autohide,options.wiplimit>0&&(col.wiplimit=options.wiplimit),col.cardcount=0,col.hascards){let cardOrder=col.sequence.split(",");col.cards=cardOrder.map((value=>this.exportCard(state,value))),col.cardcount=cardOrder.length}return col}static exportCard(state,cardid){let card={id:cardid,title:"-",assignees:[],options:"{}",canedit:!1};void 0!==state.cards.get(cardid)&&(card=JSON.parse(JSON.stringify(state.cards.get(cardid)))),card.cardid=card.id,card.hasassignees=card.assignees.length>0;let options=JSON.parse(card.options);return card.hasassignees&&"number"==typeof card.assignees[0]&&(card.assignees=card.assignees.map((userid=>state.users.get(userid))),card.assignees=[...new Set(card.assignees)]),Object.assign(card,options)}static exportCapabilities(state){let capabilities=[];return state.capabilities.forEach((c=>{capabilities[c.id]=c.value})),Object.assign({},capabilities)}static exportDiscussion(state,cardId){let d=[];return state.discussions.forEach((c=>{c.kanban_card==cardId&&d.push(c)})),d=d.sort(((a,b)=>parseInt(a.timecreated)>parseInt(b.timecreated))),d}static exportHistory(state,cardId){let d=[];return state.history.forEach((c=>{c.kanban_card==cardId&&d.push(c)})),d=d.sort(((a,b)=>parseInt(a.timestamp)>parseInt(b.timestamp))),d}},_exports.default})); //# sourceMappingURL=exporter.min.js.map \ No newline at end of file diff --git a/amd/build/exporter.min.js.map b/amd/build/exporter.min.js.map index 9db53237..0c99e5f4 100644 --- a/amd/build/exporter.min.js.map +++ b/amd/build/exporter.min.js.map @@ -1 +1 @@ -{"version":3,"file":"exporter.min.js","sources":["../src/exporter.js"],"sourcesContent":["import capabilities from 'mod_kanban/capabilities';\n\n/**\n * Exporter for use in mustache template.\n */\nexport default class {\n /**\n * Exports the complete state (for initial rendering).\n * @param {*} state\n * @returns {object}\n */\n static exportStateForTemplate(state) {\n let columnOrder = state.board.sequence.split(',');\n let columns = [];\n let hascolumns = state.board.sequence != '';\n if (hascolumns) {\n columns = columnOrder.map((value) => {\n return this.exportCardsForColumn(state, value);\n });\n }\n\n let showactionmenu = state.common.userboards == 1 || state.common.groupselector != '' ||\n state.capabilities.get(capabilities.MANAGEBOARD).value ||\n (state.common.userboards == 2 && state.capabilities.get(capabilities.VIEWALLBOARDS).value);\n\n return Object.assign({\n cmid: state.common.id,\n id: state.board.id,\n sequence: state.board.sequence,\n hascolumns: hascolumns,\n columns: columns,\n locked: state.board.locked,\n hastemplate: state.common.template != 0,\n istemplate: state.board.template != 0,\n heading: state.board.heading,\n groupselector: state.common.groupselector,\n userboards: state.common.userboards,\n history: state.common.history && state.capabilities.get(capabilities.VIEWHISTORY).value,\n groupmode: state.common.groupmode,\n ismyuserboard: state.common.userid == state.board.userid,\n myuserid: state.common.userid,\n showactionmenu: showactionmenu,\n userboardsonly: state.common.userboards == 2,\n iscourseboard: state.board.userid == 0 && state.board.groupid == 0 && state.board.template == 0,\n users: JSON.parse(JSON.stringify(state.users)),\n }, this.exportCapabilities(state));\n }\n\n /**\n * Exports the card for one column.\n * @param {*} state\n * @param {*} columnid\n * @returns {object}\n */\n static exportCardsForColumn(state, columnid) {\n let col = JSON.parse(JSON.stringify(state.columns.get(columnid)));\n let options = JSON.parse(col.options);\n col.hascards = col.sequence != '';\n col.autoclose = options.autoclose;\n col.autohide = options.autohide;\n if (col.hascards) {\n let cardOrder = col.sequence.split(',');\n col.cards = cardOrder.map((value) => {\n return this.exportCard(state, value);\n });\n }\n return col;\n }\n\n /**\n * Exports a card.\n * @param {*} state\n * @param {*} cardid\n * @returns {object}\n */\n static exportCard(state, cardid) {\n let card = {\n id: cardid,\n title: '-',\n assignees: [],\n options: '{}',\n canedit: false\n };\n if (state.cards.get(cardid) !== undefined) {\n card = JSON.parse(JSON.stringify(state.cards.get(cardid)));\n }\n card.cardid = card.id;\n card.hasassignees = card.assignees.length > 0;\n let options = JSON.parse(card.options);\n if (card.hasassignees && typeof card.assignees[0] == 'number') {\n card.assignees = card.assignees.map((userid) => {\n return state.users.get(userid);\n });\n card.assignees = [...new Set(card.assignees)];\n }\n return Object.assign(card, options);\n }\n\n /**\n * Exports the capabilities.\n * @param {*} state\n * @returns {object}\n */\n static exportCapabilities(state) {\n let capabilities = [];\n state.capabilities.forEach((c) => {\n capabilities[c.id] = c.value;\n });\n return Object.assign({}, capabilities);\n }\n\n /**\n * Exports the discussion for a card.\n * @param {*} state\n * @param {number} cardId\n * @returns {array}\n */\n static exportDiscussion(state, cardId) {\n let d = [];\n state.discussions.forEach((c) => {\n if (c.kanban_card == cardId) {\n d.push(c);\n }\n });\n d = d.sort((a, b) => parseInt(a.timecreated) > parseInt(b.timecreated));\n return d;\n }\n\n /**\n * Exports history for a card.\n * @param {*} state\n * @param {number} cardId\n * @returns {array}\n */\n static exportHistory(state, cardId) {\n let d = [];\n // Only get history of this card.\n state.history.forEach((c) => {\n if (c.kanban_card == cardId) {\n d.push(c);\n }\n });\n // Sort by timestamp.\n d = d.sort((a, b) => parseInt(a.timestamp) > parseInt(b.timestamp));\n return d;\n }\n}"],"names":["state","columnOrder","board","sequence","split","columns","hascolumns","map","value","this","exportCardsForColumn","showactionmenu","common","userboards","groupselector","capabilities","get","MANAGEBOARD","VIEWALLBOARDS","Object","assign","cmid","id","locked","hastemplate","template","istemplate","heading","history","VIEWHISTORY","groupmode","ismyuserboard","userid","myuserid","userboardsonly","iscourseboard","groupid","users","JSON","parse","stringify","exportCapabilities","columnid","col","options","hascards","autoclose","autohide","cardOrder","cards","exportCard","cardid","card","title","assignees","canedit","undefined","hasassignees","length","Set","forEach","c","cardId","d","discussions","kanban_card","push","sort","a","b","parseInt","timecreated","timestamp"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"exporter.min.js","sources":["../src/exporter.js"],"sourcesContent":["import capabilities from 'mod_kanban/capabilities';\n\n/**\n * Exporter for use in mustache template.\n */\nexport default class {\n /**\n * Exports the complete state (for initial rendering).\n * @param {*} state\n * @returns {object}\n */\n static exportStateForTemplate(state) {\n let columnOrder = state.board.sequence.split(',');\n let columns = [];\n let hascolumns = state.board.sequence != '';\n if (hascolumns) {\n columns = columnOrder.map((value) => {\n return this.exportCardsForColumn(state, value);\n });\n }\n\n let showactionmenu = state.common.userboards == 1 || state.common.groupselector != '' ||\n state.capabilities.get(capabilities.MANAGEBOARD).value ||\n (state.common.userboards == 2 && state.capabilities.get(capabilities.VIEWALLBOARDS).value);\n\n return Object.assign({\n cmid: state.common.id,\n id: state.board.id,\n sequence: state.board.sequence,\n hascolumns: hascolumns,\n columns: columns,\n locked: state.board.locked,\n hastemplate: state.common.template != 0,\n istemplate: state.board.template != 0,\n heading: state.board.heading,\n groupselector: state.common.groupselector,\n userboards: state.common.userboards,\n history: state.common.history && state.capabilities.get(capabilities.VIEWHISTORY).value,\n groupmode: state.common.groupmode,\n ismyuserboard: state.common.userid == state.board.userid,\n myuserid: state.common.userid,\n showactionmenu: showactionmenu,\n userboardsonly: state.common.userboards == 2,\n iscourseboard: state.board.userid == 0 && state.board.groupid == 0 && state.board.template == 0,\n users: JSON.parse(JSON.stringify(state.users)),\n }, this.exportCapabilities(state));\n }\n\n /**\n * Exports the card for one column.\n * @param {*} state\n * @param {*} columnid\n * @returns {object}\n */\n static exportCardsForColumn(state, columnid) {\n let col = JSON.parse(JSON.stringify(state.columns.get(columnid)));\n let options = JSON.parse(col.options);\n col.hascards = col.sequence != '';\n col.autoclose = options.autoclose;\n col.autohide = options.autohide;\n if (options.wiplimit > 0) {\n col.wiplimit = options.wiplimit;\n }\n col.cardcount = 0;\n if (col.hascards) {\n let cardOrder = col.sequence.split(',');\n col.cards = cardOrder.map((value) => {\n return this.exportCard(state, value);\n });\n col.cardcount = cardOrder.length;\n }\n return col;\n }\n\n /**\n * Exports a card.\n * @param {*} state\n * @param {*} cardid\n * @returns {object}\n */\n static exportCard(state, cardid) {\n let card = {\n id: cardid,\n title: '-',\n assignees: [],\n options: '{}',\n canedit: false\n };\n if (state.cards.get(cardid) !== undefined) {\n card = JSON.parse(JSON.stringify(state.cards.get(cardid)));\n }\n card.cardid = card.id;\n card.hasassignees = card.assignees.length > 0;\n let options = JSON.parse(card.options);\n if (card.hasassignees && typeof card.assignees[0] == 'number') {\n card.assignees = card.assignees.map((userid) => {\n return state.users.get(userid);\n });\n card.assignees = [...new Set(card.assignees)];\n }\n return Object.assign(card, options);\n }\n\n /**\n * Exports the capabilities.\n * @param {*} state\n * @returns {object}\n */\n static exportCapabilities(state) {\n let capabilities = [];\n state.capabilities.forEach((c) => {\n capabilities[c.id] = c.value;\n });\n return Object.assign({}, capabilities);\n }\n\n /**\n * Exports the discussion for a card.\n * @param {*} state\n * @param {number} cardId\n * @returns {array}\n */\n static exportDiscussion(state, cardId) {\n let d = [];\n state.discussions.forEach((c) => {\n if (c.kanban_card == cardId) {\n d.push(c);\n }\n });\n d = d.sort((a, b) => parseInt(a.timecreated) > parseInt(b.timecreated));\n return d;\n }\n\n /**\n * Exports history for a card.\n * @param {*} state\n * @param {number} cardId\n * @returns {array}\n */\n static exportHistory(state, cardId) {\n let d = [];\n // Only get history of this card.\n state.history.forEach((c) => {\n if (c.kanban_card == cardId) {\n d.push(c);\n }\n });\n // Sort by timestamp.\n d = d.sort((a, b) => parseInt(a.timestamp) > parseInt(b.timestamp));\n return d;\n }\n}"],"names":["state","columnOrder","board","sequence","split","columns","hascolumns","map","value","this","exportCardsForColumn","showactionmenu","common","userboards","groupselector","capabilities","get","MANAGEBOARD","VIEWALLBOARDS","Object","assign","cmid","id","locked","hastemplate","template","istemplate","heading","history","VIEWHISTORY","groupmode","ismyuserboard","userid","myuserid","userboardsonly","iscourseboard","groupid","users","JSON","parse","stringify","exportCapabilities","columnid","col","options","hascards","autoclose","autohide","wiplimit","cardcount","cardOrder","cards","exportCard","length","cardid","card","title","assignees","canedit","undefined","hasassignees","Set","forEach","c","cardId","d","discussions","kanban_card","push","sort","a","b","parseInt","timecreated","timestamp"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/selectors.min.js b/amd/build/selectors.min.js index 4f8b2233..dd9f819a 100644 --- a/amd/build/selectors.min.js +++ b/amd/build/selectors.min.js @@ -1,3 +1,3 @@ -define("mod_kanban/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={ADDCARD:'[data-action="add_card"]',ADDCARDCONTAINER:".mod_kanban_addcard_container",ADDCARDFIRST:".mod_kanban_addcard_first",ADDCOLUMN:'[data-action="add_column"]',ADDCOLUMNCONTAINER:".mod_kanban_addcolumn_container",ADDCOLUMNFIRST:".mod_kanban_addcolumn_first",ASSIGNEES:".mod_kanban_assignees",ASSIGNSELF:'[data-action="assign_self"]',ASSIGNUSER:'[data-action="assign_user"]',ASSIGNEDUSER:".mod_kanban_assigned_user",BOARD:".mod_kanban_board",CARD:".mod_kanban_card",COLUMN:".mod_kanban_column",COLUMNCONTAINER:".mod_kanban_column_container",COLUMNINNER:".mod_kanban_column_inner",COMPLETE:'[data-action="complete_card"]',COMPLETIONSTATE:".mod_kanban_card_completion",CONTAINER:".mod_kanban_render_container",DELETEBOARD:'[data-action="delete_board"]',DELETECARD:'[data-action="delete_card"]',DELETECOLUMN:'[data-action="delete_column"]',DELETEMESSAGE:'[data-action="delete_message"]',DELETETEMPLATE:'[data-action="delete_template"]',DESCRIPTIONMODAL:".mod_kanban_description",DESCRIPTIONMODALBODY:".mod_kanban_description_modal .modal-body",DESCRIPTIONMODALFOOTER:".mod_kanban_description_modal .modal-footer",DESCRIPTIONMODALTITLE:".mod_kanban_description_modal .modal-title",DESCRIPTIONTOGGLE:".mod_kanban_description",DISCUSSION:".mod_kanban_discussion",DISCUSSIONINPUT:".mod_kanban_discussion_input",DISCUSSIONMESSAGES:".mod_kanban_discussion_messages",DISCUSSIONMODAL:".mod_kanban_discussion_modal",DISCUSSIONMODALTITLE:".mod_kanban_discussion_modal .modal-title",DISCUSSIONMODALTRIGGER:".mod_kanban_discussion_trigger",DISCUSSIONSEND:'[data-action="send_discussion_message"]',DISCUSSIONSHOW:'[data-action="show_discussion"]',DUEDATE:".mod_kanban_duedate",EDITDETAILS:'[data-action="edit_details"]',HIDEHIDDEN:'[data-action="hide_hidden"]',HISTORY:".mod_kanban_history",HISTORYITEMS:".mod_kanban_history_items",HISTORYMODAL:".mod_kanban_history_modal",HISTORYMODALTRIGGER:'[data-action="show_history"]',INPLACEEDITABLE:".inplaceeditable",LOCKCOLUMN:'[data-action="lock_column"]',LOCKBOARDCOLUMNS:'[data-action="lock_board_columns"]',MAIN:".mod_kanban_main",MOVECARDAFTERCARD:".mod_kanban_move_card_aftercard",MOVECARDCOLUMN:".mod_kanban_move_card_column",MOVEMODALTRIGGER:'[data-action="move_card"]',PUSHCARD:'[data-action="push_card"]',SAVEASTEMPLATE:'[data-action="create_template"]',SCROLLLEFT:".mod_kanban_scroll_left button",SCROLLRIGHT:".mod_kanban_scroll_right button",SHOWBOARD:'[data-action="show_board"]',SHOWHIDDEN:'[data-action="show_hidden"]',SHOWTEMPLATE:'[data-action="show_template"]',UNASSIGNSELF:'[data-action="unassign_self"]',UNASSIGNUSER:'[data-action="unassign_user"]',UNCOMPLETE:'[data-action="uncomplete_card"]',UNLOCKCOLUMN:'[data-action="unlock_column"]',UNLOCKBOARDCOLUMNS:'[data-action="unlock_board_columns"]'},_exports.default})); +define("mod_kanban/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={ADDCARD:'[data-action="add_card"]',ADDCARDCONTAINER:".mod_kanban_addcard_container",ADDCARDFIRST:".mod_kanban_addcard_first",ADDCOLUMN:'[data-action="add_column"]',ADDCOLUMNCONTAINER:".mod_kanban_addcolumn_container",ADDCOLUMNFIRST:".mod_kanban_addcolumn_first",ASSIGNEES:".mod_kanban_assignees",ASSIGNSELF:'[data-action="assign_self"]',ASSIGNUSER:'[data-action="assign_user"]',ASSIGNEDUSER:".mod_kanban_assigned_user",BOARD:".mod_kanban_board",CARD:".mod_kanban_card",CARDCOUNT:".mod_kanban_cardcount",COLUMN:".mod_kanban_column",COLUMNCONTAINER:".mod_kanban_column_container",COLUMNINNER:".mod_kanban_column_inner",COMPLETE:'[data-action="complete_card"]',COMPLETIONSTATE:".mod_kanban_card_completion",CONTAINER:".mod_kanban_render_container",DELETEBOARD:'[data-action="delete_board"]',DELETECARD:'[data-action="delete_card"]',DELETECOLUMN:'[data-action="delete_column"]',DELETEMESSAGE:'[data-action="delete_message"]',DELETETEMPLATE:'[data-action="delete_template"]',DESCRIPTIONMODAL:".mod_kanban_description",DESCRIPTIONMODALBODY:".mod_kanban_description_modal .modal-body",DESCRIPTIONMODALFOOTER:".mod_kanban_description_modal .modal-footer",DESCRIPTIONMODALTITLE:".mod_kanban_description_modal .modal-title",DESCRIPTIONTOGGLE:".mod_kanban_description",DISCUSSION:".mod_kanban_discussion",DISCUSSIONINPUT:".mod_kanban_discussion_input",DISCUSSIONMESSAGES:".mod_kanban_discussion_messages",DISCUSSIONMODAL:".mod_kanban_discussion_modal",DISCUSSIONMODALTITLE:".mod_kanban_discussion_modal .modal-title",DISCUSSIONMODALTRIGGER:".mod_kanban_discussion_trigger",DISCUSSIONSEND:'[data-action="send_discussion_message"]',DISCUSSIONSHOW:'[data-action="show_discussion"]',DUEDATE:".mod_kanban_duedate",EDITDETAILS:'[data-action="edit_details"]',HIDEHIDDEN:'[data-action="hide_hidden"]',HISTORY:".mod_kanban_history",HISTORYITEMS:".mod_kanban_history_items",HISTORYMODAL:".mod_kanban_history_modal",HISTORYMODALTRIGGER:'[data-action="show_history"]',INPLACEEDITABLE:".inplaceeditable",LOCKCOLUMN:'[data-action="lock_column"]',LOCKBOARDCOLUMNS:'[data-action="lock_board_columns"]',MAIN:".mod_kanban_main",MOVECARDAFTERCARD:".mod_kanban_move_card_aftercard",MOVECARDCOLUMN:".mod_kanban_move_card_column",MOVEMODALTRIGGER:'[data-action="move_card"]',PUSHCARD:'[data-action="push_card"]',SAVEASTEMPLATE:'[data-action="create_template"]',SCROLLLEFT:".mod_kanban_scroll_left button",SCROLLRIGHT:".mod_kanban_scroll_right button",SHOWBOARD:'[data-action="show_board"]',SHOWHIDDEN:'[data-action="show_hidden"]',SHOWTEMPLATE:'[data-action="show_template"]',UNASSIGNSELF:'[data-action="unassign_self"]',UNASSIGNUSER:'[data-action="unassign_user"]',UNCOMPLETE:'[data-action="uncomplete_card"]',UNLOCKCOLUMN:'[data-action="unlock_column"]',UNLOCKBOARDCOLUMNS:'[data-action="unlock_board_columns"]'},_exports.default})); //# sourceMappingURL=selectors.min.js.map \ No newline at end of file diff --git a/amd/build/selectors.min.js.map b/amd/build/selectors.min.js.map index 155b5350..d08511de 100644 --- a/amd/build/selectors.min.js.map +++ b/amd/build/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"sourcesContent":["/**\n * Selectors for mod_kanban.\n */\nexport default {\n ADDCARD: `[data-action=\"add_card\"]`,\n ADDCARDCONTAINER: `.mod_kanban_addcard_container`,\n ADDCARDFIRST: `.mod_kanban_addcard_first`,\n ADDCOLUMN: `[data-action=\"add_column\"]`,\n ADDCOLUMNCONTAINER: `.mod_kanban_addcolumn_container`,\n ADDCOLUMNFIRST: `.mod_kanban_addcolumn_first`,\n ASSIGNEES: `.mod_kanban_assignees`,\n ASSIGNSELF: `[data-action=\"assign_self\"]`,\n ASSIGNUSER: `[data-action=\"assign_user\"]`,\n ASSIGNEDUSER: `.mod_kanban_assigned_user`,\n BOARD: `.mod_kanban_board`,\n CARD: `.mod_kanban_card`,\n COLUMN: `.mod_kanban_column`,\n COLUMNCONTAINER: `.mod_kanban_column_container`,\n COLUMNINNER: `.mod_kanban_column_inner`,\n COMPLETE: `[data-action=\"complete_card\"]`,\n COMPLETIONSTATE: `.mod_kanban_card_completion`,\n CONTAINER: `.mod_kanban_render_container`,\n DELETEBOARD: `[data-action=\"delete_board\"]`,\n DELETECARD: `[data-action=\"delete_card\"]`,\n DELETECOLUMN: `[data-action=\"delete_column\"]`,\n DELETEMESSAGE: `[data-action=\"delete_message\"]`,\n DELETETEMPLATE: `[data-action=\"delete_template\"]`,\n DESCRIPTIONMODAL: `.mod_kanban_description`,\n DESCRIPTIONMODALBODY: `.mod_kanban_description_modal .modal-body`,\n DESCRIPTIONMODALFOOTER: `.mod_kanban_description_modal .modal-footer`,\n DESCRIPTIONMODALTITLE: `.mod_kanban_description_modal .modal-title`,\n DESCRIPTIONTOGGLE: `.mod_kanban_description`,\n DISCUSSION: `.mod_kanban_discussion`,\n DISCUSSIONINPUT: `.mod_kanban_discussion_input`,\n DISCUSSIONMESSAGES: `.mod_kanban_discussion_messages`,\n DISCUSSIONMODAL: `.mod_kanban_discussion_modal`,\n DISCUSSIONMODALTITLE: `.mod_kanban_discussion_modal .modal-title`,\n DISCUSSIONMODALTRIGGER: `.mod_kanban_discussion_trigger`,\n DISCUSSIONSEND: `[data-action=\"send_discussion_message\"]`,\n DISCUSSIONSHOW: `[data-action=\"show_discussion\"]`,\n DUEDATE: `.mod_kanban_duedate`,\n EDITDETAILS: `[data-action=\"edit_details\"]`,\n HIDEHIDDEN: `[data-action=\"hide_hidden\"]`,\n HISTORY: `.mod_kanban_history`,\n HISTORYITEMS: `.mod_kanban_history_items`,\n HISTORYMODAL: `.mod_kanban_history_modal`,\n HISTORYMODALTRIGGER: `[data-action=\"show_history\"]`,\n INPLACEEDITABLE: `.inplaceeditable`,\n LOCKCOLUMN: `[data-action=\"lock_column\"]`,\n LOCKBOARDCOLUMNS: `[data-action=\"lock_board_columns\"]`,\n MAIN: `.mod_kanban_main`,\n MOVECARDAFTERCARD: `.mod_kanban_move_card_aftercard`,\n MOVECARDCOLUMN: `.mod_kanban_move_card_column`,\n MOVEMODALTRIGGER: `[data-action=\"move_card\"]`,\n PUSHCARD: `[data-action=\"push_card\"]`,\n SAVEASTEMPLATE: `[data-action=\"create_template\"]`,\n SCROLLLEFT: `.mod_kanban_scroll_left button`,\n SCROLLRIGHT: `.mod_kanban_scroll_right button`,\n SHOWBOARD: `[data-action=\"show_board\"]`,\n SHOWHIDDEN: `[data-action=\"show_hidden\"]`,\n SHOWTEMPLATE: `[data-action=\"show_template\"]`,\n UNASSIGNSELF: `[data-action=\"unassign_self\"]`,\n UNASSIGNUSER: `[data-action=\"unassign_user\"]`,\n UNCOMPLETE: `[data-action=\"uncomplete_card\"]`,\n UNLOCKCOLUMN: `[data-action=\"unlock_column\"]`,\n UNLOCKBOARDCOLUMNS: `[data-action=\"unlock_board_columns\"]`,\n};"],"names":["ADDCARD","ADDCARDCONTAINER","ADDCARDFIRST","ADDCOLUMN","ADDCOLUMNCONTAINER","ADDCOLUMNFIRST","ASSIGNEES","ASSIGNSELF","ASSIGNUSER","ASSIGNEDUSER","BOARD","CARD","COLUMN","COLUMNCONTAINER","COLUMNINNER","COMPLETE","COMPLETIONSTATE","CONTAINER","DELETEBOARD","DELETECARD","DELETECOLUMN","DELETEMESSAGE","DELETETEMPLATE","DESCRIPTIONMODAL","DESCRIPTIONMODALBODY","DESCRIPTIONMODALFOOTER","DESCRIPTIONMODALTITLE","DESCRIPTIONTOGGLE","DISCUSSION","DISCUSSIONINPUT","DISCUSSIONMESSAGES","DISCUSSIONMODAL","DISCUSSIONMODALTITLE","DISCUSSIONMODALTRIGGER","DISCUSSIONSEND","DISCUSSIONSHOW","DUEDATE","EDITDETAILS","HIDEHIDDEN","HISTORY","HISTORYITEMS","HISTORYMODAL","HISTORYMODALTRIGGER","INPLACEEDITABLE","LOCKCOLUMN","LOCKBOARDCOLUMNS","MAIN","MOVECARDAFTERCARD","MOVECARDCOLUMN","MOVEMODALTRIGGER","PUSHCARD","SAVEASTEMPLATE","SCROLLLEFT","SCROLLRIGHT","SHOWBOARD","SHOWHIDDEN","SHOWTEMPLATE","UNASSIGNSELF","UNASSIGNUSER","UNCOMPLETE","UNLOCKCOLUMN","UNLOCKBOARDCOLUMNS"],"mappings":"sKAGe,CACXA,mCACAC,iDACAC,yCACAC,uCACAC,qDACAC,6CACAC,kCACAC,yCACAC,yCACAC,yCACAC,0BACAC,wBACAC,4BACAC,+CACAC,uCACAC,yCACAC,8CACAC,yCACAC,2CACAC,yCACAC,6CACAC,+CACAC,iDACAC,2CACAC,iEACAC,qEACAC,mEACAC,4CACAC,oCACAC,+CACAC,qDACAC,+CACAC,iEACAC,wDACAC,yDACAC,iDACAC,8BACAC,2CACAC,yCACAC,8BACAC,yCACAC,yCACAC,mDACAC,mCACAC,yCACAC,sDACAC,wBACAC,oDACAC,8CACAC,6CACAC,qCACAC,iDACAC,4CACAC,8CACAC,uCACAC,yCACAC,6CACAC,6CACAC,6CACAC,6CACAC,6CACAC"} \ No newline at end of file +{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"sourcesContent":["/**\n * Selectors for mod_kanban.\n */\nexport default {\n ADDCARD: `[data-action=\"add_card\"]`,\n ADDCARDCONTAINER: `.mod_kanban_addcard_container`,\n ADDCARDFIRST: `.mod_kanban_addcard_first`,\n ADDCOLUMN: `[data-action=\"add_column\"]`,\n ADDCOLUMNCONTAINER: `.mod_kanban_addcolumn_container`,\n ADDCOLUMNFIRST: `.mod_kanban_addcolumn_first`,\n ASSIGNEES: `.mod_kanban_assignees`,\n ASSIGNSELF: `[data-action=\"assign_self\"]`,\n ASSIGNUSER: `[data-action=\"assign_user\"]`,\n ASSIGNEDUSER: `.mod_kanban_assigned_user`,\n BOARD: `.mod_kanban_board`,\n CARD: `.mod_kanban_card`,\n CARDCOUNT: `.mod_kanban_cardcount`,\n COLUMN: `.mod_kanban_column`,\n COLUMNCONTAINER: `.mod_kanban_column_container`,\n COLUMNINNER: `.mod_kanban_column_inner`,\n COMPLETE: `[data-action=\"complete_card\"]`,\n COMPLETIONSTATE: `.mod_kanban_card_completion`,\n CONTAINER: `.mod_kanban_render_container`,\n DELETEBOARD: `[data-action=\"delete_board\"]`,\n DELETECARD: `[data-action=\"delete_card\"]`,\n DELETECOLUMN: `[data-action=\"delete_column\"]`,\n DELETEMESSAGE: `[data-action=\"delete_message\"]`,\n DELETETEMPLATE: `[data-action=\"delete_template\"]`,\n DESCRIPTIONMODAL: `.mod_kanban_description`,\n DESCRIPTIONMODALBODY: `.mod_kanban_description_modal .modal-body`,\n DESCRIPTIONMODALFOOTER: `.mod_kanban_description_modal .modal-footer`,\n DESCRIPTIONMODALTITLE: `.mod_kanban_description_modal .modal-title`,\n DESCRIPTIONTOGGLE: `.mod_kanban_description`,\n DISCUSSION: `.mod_kanban_discussion`,\n DISCUSSIONINPUT: `.mod_kanban_discussion_input`,\n DISCUSSIONMESSAGES: `.mod_kanban_discussion_messages`,\n DISCUSSIONMODAL: `.mod_kanban_discussion_modal`,\n DISCUSSIONMODALTITLE: `.mod_kanban_discussion_modal .modal-title`,\n DISCUSSIONMODALTRIGGER: `.mod_kanban_discussion_trigger`,\n DISCUSSIONSEND: `[data-action=\"send_discussion_message\"]`,\n DISCUSSIONSHOW: `[data-action=\"show_discussion\"]`,\n DUEDATE: `.mod_kanban_duedate`,\n EDITDETAILS: `[data-action=\"edit_details\"]`,\n HIDEHIDDEN: `[data-action=\"hide_hidden\"]`,\n HISTORY: `.mod_kanban_history`,\n HISTORYITEMS: `.mod_kanban_history_items`,\n HISTORYMODAL: `.mod_kanban_history_modal`,\n HISTORYMODALTRIGGER: `[data-action=\"show_history\"]`,\n INPLACEEDITABLE: `.inplaceeditable`,\n LOCKCOLUMN: `[data-action=\"lock_column\"]`,\n LOCKBOARDCOLUMNS: `[data-action=\"lock_board_columns\"]`,\n MAIN: `.mod_kanban_main`,\n MOVECARDAFTERCARD: `.mod_kanban_move_card_aftercard`,\n MOVECARDCOLUMN: `.mod_kanban_move_card_column`,\n MOVEMODALTRIGGER: `[data-action=\"move_card\"]`,\n PUSHCARD: `[data-action=\"push_card\"]`,\n SAVEASTEMPLATE: `[data-action=\"create_template\"]`,\n SCROLLLEFT: `.mod_kanban_scroll_left button`,\n SCROLLRIGHT: `.mod_kanban_scroll_right button`,\n SHOWBOARD: `[data-action=\"show_board\"]`,\n SHOWHIDDEN: `[data-action=\"show_hidden\"]`,\n SHOWTEMPLATE: `[data-action=\"show_template\"]`,\n UNASSIGNSELF: `[data-action=\"unassign_self\"]`,\n UNASSIGNUSER: `[data-action=\"unassign_user\"]`,\n UNCOMPLETE: `[data-action=\"uncomplete_card\"]`,\n UNLOCKCOLUMN: `[data-action=\"unlock_column\"]`,\n UNLOCKBOARDCOLUMNS: `[data-action=\"unlock_board_columns\"]`,\n};"],"names":["ADDCARD","ADDCARDCONTAINER","ADDCARDFIRST","ADDCOLUMN","ADDCOLUMNCONTAINER","ADDCOLUMNFIRST","ASSIGNEES","ASSIGNSELF","ASSIGNUSER","ASSIGNEDUSER","BOARD","CARD","CARDCOUNT","COLUMN","COLUMNCONTAINER","COLUMNINNER","COMPLETE","COMPLETIONSTATE","CONTAINER","DELETEBOARD","DELETECARD","DELETECOLUMN","DELETEMESSAGE","DELETETEMPLATE","DESCRIPTIONMODAL","DESCRIPTIONMODALBODY","DESCRIPTIONMODALFOOTER","DESCRIPTIONMODALTITLE","DESCRIPTIONTOGGLE","DISCUSSION","DISCUSSIONINPUT","DISCUSSIONMESSAGES","DISCUSSIONMODAL","DISCUSSIONMODALTITLE","DISCUSSIONMODALTRIGGER","DISCUSSIONSEND","DISCUSSIONSHOW","DUEDATE","EDITDETAILS","HIDEHIDDEN","HISTORY","HISTORYITEMS","HISTORYMODAL","HISTORYMODALTRIGGER","INPLACEEDITABLE","LOCKCOLUMN","LOCKBOARDCOLUMNS","MAIN","MOVECARDAFTERCARD","MOVECARDCOLUMN","MOVEMODALTRIGGER","PUSHCARD","SAVEASTEMPLATE","SCROLLLEFT","SCROLLRIGHT","SHOWBOARD","SHOWHIDDEN","SHOWTEMPLATE","UNASSIGNSELF","UNASSIGNUSER","UNCOMPLETE","UNLOCKCOLUMN","UNLOCKBOARDCOLUMNS"],"mappings":"sKAGe,CACXA,mCACAC,iDACAC,yCACAC,uCACAC,qDACAC,6CACAC,kCACAC,yCACAC,yCACAC,yCACAC,0BACAC,wBACAC,kCACAC,4BACAC,+CACAC,uCACAC,yCACAC,8CACAC,yCACAC,2CACAC,yCACAC,6CACAC,+CACAC,iDACAC,2CACAC,iEACAC,qEACAC,mEACAC,4CACAC,oCACAC,+CACAC,qDACAC,+CACAC,iEACAC,wDACAC,yDACAC,iDACAC,8BACAC,2CACAC,yCACAC,8BACAC,yCACAC,yCACAC,mDACAC,mCACAC,yCACAC,sDACAC,wBACAC,oDACAC,8CACAC,6CACAC,qCACAC,iDACAC,4CACAC,8CACAC,uCACAC,yCACAC,6CACAC,6CACAC,6CACAC,6CACAC,6CACAC"} \ No newline at end of file diff --git a/amd/src/column.js b/amd/src/column.js index 1b558526..84dfe810 100644 --- a/amd/src/column.js +++ b/amd/src/column.js @@ -301,7 +301,9 @@ export default class extends KanbanComponent { if (element.options !== undefined) { let options = JSON.parse(element.options); this.toggleClass(options.autohide, 'mod_kanban_autohide'); + this.toggleClass(options.wiplimit > 0, 'mod_kanban_wiplimit'); } + this.getElement(selectors.CARDCOUNT).innerHTML = this.getElements(selectors.CARD).length; // Enable/disable dragging (e.g. if column is locked). this.checkDragging(); } diff --git a/amd/src/exporter.js b/amd/src/exporter.js index 4f0f7c19..f4e94ebb 100644 --- a/amd/src/exporter.js +++ b/amd/src/exporter.js @@ -58,11 +58,16 @@ export default class { col.hascards = col.sequence != ''; col.autoclose = options.autoclose; col.autohide = options.autohide; + if (options.wiplimit > 0) { + col.wiplimit = options.wiplimit; + } + col.cardcount = 0; if (col.hascards) { let cardOrder = col.sequence.split(','); col.cards = cardOrder.map((value) => { return this.exportCard(state, value); }); + col.cardcount = cardOrder.length; } return col; } diff --git a/amd/src/selectors.js b/amd/src/selectors.js index 04000b37..d99da2f5 100644 --- a/amd/src/selectors.js +++ b/amd/src/selectors.js @@ -14,6 +14,7 @@ export default { ASSIGNEDUSER: `.mod_kanban_assigned_user`, BOARD: `.mod_kanban_board`, CARD: `.mod_kanban_card`, + CARDCOUNT: `.mod_kanban_cardcount`, COLUMN: `.mod_kanban_column`, COLUMNCONTAINER: `.mod_kanban_column_container`, COLUMNINNER: `.mod_kanban_column_inner`, diff --git a/classes/boardmanager.php b/classes/boardmanager.php index ba8d67c7..7519f395 100644 --- a/classes/boardmanager.php +++ b/classes/boardmanager.php @@ -28,6 +28,9 @@ use cm_info; use context_module; use context_system; +use core_h5p\core; +use core_user; +use moodle_exception; use stdClass; /** @@ -540,6 +543,13 @@ public function move_card(int $cardid, int $aftercard, int $columnid = 0): void } else { $targetcolumn = $DB->get_record('kanban_column', ['id' => $columnid]); + $options = json_decode($targetcolumn->options); + $wiplimit = $options->wiplimit ?? 0; + + if ($wiplimit > 0) { + self::check_wiplimit($columnid, $cardid, $wiplimit); + } + // Card needs to be processed first, because column sorting in frontend will only // work if card is already moved in the right position. $updatecard = ['id' => $cardid, 'kanban_column' => $columnid, 'timemodified' => time()]; @@ -596,6 +606,51 @@ public function move_card(int $cardid, int $aftercard, int $columnid = 0): void helper::update_cached_timestamp($this->board->id, constants::MOD_KANBAN_COLUMN, $update['timemodified']); } + /** + * Checks whether the WIP limit is reached for a certain column and card. Raises an exception if limit is reached. + * @param int $columnid Id of the column + * @param int $cardid Id of the current card + * @param int $wiplimit WIP limit + * @param array $assignees Array of user ids that should be checked for WIP limit. If empty, checking will be done + * for the current assignees. + * @throws moodle_exception + */ + public function check_wiplimit(int $columnid, int $cardid, int $wiplimit, array $assignees = []): void { + if (empty($assignees)) { + $assignees = $this->get_card_assignees($cardid); + } + $overlimit = []; + foreach ($assignees as $assignee) { + $wip = $this->get_wip($columnid, $assignee, $cardid); + if ($wip >= $wiplimit) { + $user = core_user::get_user($assignee); + $overlimit[] = fullname($user); + } + } + if (count($overlimit) > 0) { + throw new moodle_exception('wiplimitreached', 'mod_kanban', '', ['users' => implode(', ', $overlimit)]); + } + } + + /** + * Returns the number of cards in a column a certain user is currently assigned to. + * @param int $columnid Id of the column + * @param int $userid Id of the user + * @param int $cardtoexclude Id of a card to exclude from the count + */ + public function get_wip(int $columnid, int $userid, int $cardtoexclude = 0): int { + global $DB; + $count = $DB->get_field_sql( + 'SELECT COUNT(*) + FROM {kanban_card} c + INNER JOIN {kanban_assignee} a + ON a.kanban_card = c.id + WHERE a.userid = :userid AND c.kanban_column = :columnid AND c.id != :cardid', + ['columnid' => $columnid, 'userid' => $userid, 'cardid' => $cardtoexclude] + ); + return $count; + } + /** * Assigns a user to a card. * @@ -605,8 +660,17 @@ public function move_card(int $cardid, int $aftercard, int $columnid = 0): void */ public function assign_user(int $cardid, int $userid): void { global $DB, $OUTPUT, $USER; - $DB->insert_record('kanban_assignee', ['kanban_card' => $cardid, 'userid' => $userid]); $card = $this->get_card($cardid); + $column = $this->get_column($card->kanban_column); + $options = json_decode($column->options); + $wiplimit = $options->wiplimit ?? 0; + + if ($wiplimit > 0) { + self::check_wiplimit($card->kanban_column, $cardid, $wiplimit, [$userid]); + } + + $DB->insert_record('kanban_assignee', ['kanban_card' => $cardid, 'userid' => $userid]); + $update = [ 'id' => $cardid, 'timemodified' => time(), @@ -900,6 +964,15 @@ public function update_card(int $cardid, array $data): void { $cardupdate['assignees'] = $assignees; } $assignees = []; + + $column = $this->get_column($cardupdate['kanban_column']); + $options = json_decode($column->options); + $wiplimit = $options->wiplimit ?? 0; + + if ($wiplimit > 0) { + self::check_wiplimit($cardupdate['kanban_column'], $cardid, $wiplimit, $toinsert); + } + foreach ($toinsert as $assignee) { $assignees[] = ['kanban_card' => $cardid, 'userid' => $assignee]; $user = \core_user::get_user($assignee); @@ -966,8 +1039,9 @@ public function update_column(int $columnid, array $data): void { global $DB; $column = $this->get_column($columnid); $options = [ - 'autoclose' => $data['autoclose'], - 'autohide' => $data['autohide'], + 'autoclose' => !empty($data['autoclose']), + 'autohide' => !empty($data['autohide']), + 'wiplimit' => empty($data['wiplimitenable']) ? 0 : $data['wiplimit'], ]; if (isset($data['title'])) { $data['title'] = s($data['title']); diff --git a/classes/form/edit_column_form.php b/classes/form/edit_column_form.php index cfcab379..366fe047 100644 --- a/classes/form/edit_column_form.php +++ b/classes/form/edit_column_form.php @@ -58,6 +58,16 @@ public function definition() { $mform->addElement('advcheckbox', 'autohide', get_string('autohide', 'kanban')); $mform->setType('autohide', PARAM_BOOL); + + $wiparray = []; + $wiparray[] = $mform->createElement('advcheckbox', 'wiplimitenable', get_string('wiplimitenable', 'kanban')); + $wiparray[] = $mform->createElement('text', 'wiplimit', get_string('wiplimit', 'kanban'), ['size' => '5']); + $mform->addGroup($wiparray, 'wipgroup', '', '', false); + + $mform->setType('wiplimit', PARAM_INT); + $mform->setType('wiplimitenable', PARAM_BOOL); + + $mform->disabledIf('wiplimit', 'wiplimitenable', 'notchecked'); } /** @@ -124,6 +134,8 @@ public function set_data_for_dynamic_submission(): void { $options = json_decode($column->options); $column->autoclose = $options->autoclose; $column->autohide = $options->autohide; + $column->wiplimitenable = !empty($options->wiplimit); + $column->wiplimit = (empty($options->wiplimit) ? 0 : $options->wiplimit); $this->set_data($column); } diff --git a/lang/en/kanban.php b/lang/en/kanban.php index 0d6a2782..5ded7b6d 100644 --- a/lang/en/kanban.php +++ b/lang/en/kanban.php @@ -37,6 +37,7 @@ $string['autohide'] = 'Auto hide closed cards'; $string['cachedef_board'] = 'Cache for a board instance'; $string['cachedef_timestamp'] = 'Timestamp of last modification of card, column or board instance'; +$string['cardcount'] = 'Number of cards in this column'; $string['cardtitle'] = 'Card title'; $string['changegroup'] = 'Change group board'; $string['changeuser'] = 'Change user board'; @@ -205,3 +206,6 @@ $string['userboards_help'] = 'Enables personal boards for the participants (only visible to them and to the trainers)'; $string['userboardsenabled'] = 'Personal boards enabled'; $string['userboardsonly'] = 'Personal boards only'; +$string['wiplimit'] = 'WIP limit per person'; +$string['wiplimitenable'] = 'Enable WIP limit'; +$string['wiplimitreached'] = 'WIP limit is reached for {$a->users}.'; diff --git a/styles.css b/styles.css index d779c89b..24f6c0d5 100644 --- a/styles.css +++ b/styles.css @@ -337,6 +337,7 @@ a.mod_kanban_attachment_item { .mod_kanban_column.mod_kanban_autohide.mod_kanban_show_hidden [data-action="show_hidden"], .mod_kanban_column:not(.mod_kanban_autohide) .mod_kanban_hidden_actions, .mod_kanban_column.mod_kanban_autohide:not(.mod_kanban_show_hidden) .mod_kanban_card.mod_kanban_closed, +.mod_kanban_column:not(.mod_kanban_column_wiplimit) .mod_kanban_wipinfo, .mod_kanban_card.mod_kanban_closed .mod_kanban_complete_card, .mod_kanban_card.mod_kanban_closed .mod_kanban_duedate, .mod_kanban_card:not(.mod_kanban_closed) .mod_kanban_uncomplete_card, @@ -437,3 +438,7 @@ a.mod_kanban_attachment_item { .row.mod_kanban_update_error { display: none; } + +.mod_kanban_column_stats { + text-align: right; +} diff --git a/templates/column.mustache b/templates/column.mustache index ee5a5011..d093901d 100644 --- a/templates/column.mustache +++ b/templates/column.mustache @@ -26,10 +26,12 @@ "managecolumns": true, "hascards": false, "autohide": true, - "locked": false + "locked": false, + "wiplimit": 5, + "cardcount": 12 } }} -
  • +
  • @@ -38,6 +40,10 @@
    +
    + {{cardcount}} {{#pix}} e/template, core, {{#str}} cardcount, mod_kanban{{/str}} {{/pix}} + {{wiplimit}} / {{#pix}} t/user, core, {{#str}} wiplimit, mod_kanban{{/str}} {{/pix}} +
    From 0d420c051cd86a8256090716c7c5cd37f78ed293 Mon Sep 17 00:00:00 2001 From: Stefan Hanauska Date: Tue, 26 Mar 2024 14:10:23 +0100 Subject: [PATCH 2/4] Coding style --- classes/boardmanager.php | 14 +++++++------- classes/form/edit_column_form.php | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/classes/boardmanager.php b/classes/boardmanager.php index 7519f395..227bad68 100644 --- a/classes/boardmanager.php +++ b/classes/boardmanager.php @@ -611,7 +611,7 @@ public function move_card(int $cardid, int $aftercard, int $columnid = 0): void * @param int $columnid Id of the column * @param int $cardid Id of the current card * @param int $wiplimit WIP limit - * @param array $assignees Array of user ids that should be checked for WIP limit. If empty, checking will be done + * @param array $assignees Array of user ids that should be checked for WIP limit. If empty, checking will be done * for the current assignees. * @throws moodle_exception */ @@ -642,9 +642,9 @@ public function get_wip(int $columnid, int $userid, int $cardtoexclude = 0): int global $DB; $count = $DB->get_field_sql( 'SELECT COUNT(*) - FROM {kanban_card} c - INNER JOIN {kanban_assignee} a - ON a.kanban_card = c.id + FROM {kanban_card} c + INNER JOIN {kanban_assignee} a + ON a.kanban_card = c.id WHERE a.userid = :userid AND c.kanban_column = :columnid AND c.id != :cardid', ['columnid' => $columnid, 'userid' => $userid, 'cardid' => $cardtoexclude] ); @@ -670,7 +670,7 @@ public function assign_user(int $cardid, int $userid): void { } $DB->insert_record('kanban_assignee', ['kanban_card' => $cardid, 'userid' => $userid]); - + $update = [ 'id' => $cardid, 'timemodified' => time(), @@ -968,11 +968,11 @@ public function update_card(int $cardid, array $data): void { $column = $this->get_column($cardupdate['kanban_column']); $options = json_decode($column->options); $wiplimit = $options->wiplimit ?? 0; - + if ($wiplimit > 0) { self::check_wiplimit($cardupdate['kanban_column'], $cardid, $wiplimit, $toinsert); } - + foreach ($toinsert as $assignee) { $assignees[] = ['kanban_card' => $cardid, 'userid' => $assignee]; $user = \core_user::get_user($assignee); diff --git a/classes/form/edit_column_form.php b/classes/form/edit_column_form.php index 366fe047..468310ad 100644 --- a/classes/form/edit_column_form.php +++ b/classes/form/edit_column_form.php @@ -63,10 +63,10 @@ public function definition() { $wiparray[] = $mform->createElement('advcheckbox', 'wiplimitenable', get_string('wiplimitenable', 'kanban')); $wiparray[] = $mform->createElement('text', 'wiplimit', get_string('wiplimit', 'kanban'), ['size' => '5']); $mform->addGroup($wiparray, 'wipgroup', '', '', false); - + $mform->setType('wiplimit', PARAM_INT); $mform->setType('wiplimitenable', PARAM_BOOL); - + $mform->disabledIf('wiplimit', 'wiplimitenable', 'notchecked'); } From 8fb24c2fa1fb625811805f22018f64ecc9e715fe Mon Sep 17 00:00:00 2001 From: Stefan Hanauska Date: Tue, 14 Jan 2025 10:41:48 +0100 Subject: [PATCH 3/4] Remove unused class --- classes/boardmanager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/classes/boardmanager.php b/classes/boardmanager.php index 227bad68..956a9dd1 100644 --- a/classes/boardmanager.php +++ b/classes/boardmanager.php @@ -28,7 +28,6 @@ use cm_info; use context_module; use context_system; -use core_h5p\core; use core_user; use moodle_exception; use stdClass; From 0730d77cb72d42a4bfdc0b8c6f9f4621a24216bc Mon Sep 17 00:00:00 2001 From: Philipp Memmel Date: Tue, 14 Jan 2025 12:29:23 +0100 Subject: [PATCH 4/4] MBS-8970: Fix error handling --- amd/build/mutations.min.js | 2 +- amd/build/mutations.min.js.map | 2 +- amd/src/mutations.js | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/amd/build/mutations.min.js b/amd/build/mutations.min.js index 147bb5ba..987f2204 100644 --- a/amd/build/mutations.min.js +++ b/amd/build/mutations.min.js @@ -1,3 +1,3 @@ -define("mod_kanban/mutations",["exports","core/ajax","core/notification","core/str","core/log"],(function(_exports,_ajax,_notification,_str,_log){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_log=_interopRequireDefault(_log);return _exports.default=class{constructor(){var obj,key,value;value=0,(key="updateFails")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}async saveAsTemplate(stateManager){await this._sendChange("save_as_template",stateManager)}async deleteCard(stateManager,cardId){await this._sendChange("delete_card",stateManager,{cardid:cardId})}async deleteBoard(stateManager){await this._sendChange("delete_board",stateManager)}async addCard(stateManager,columnId,afterCard){await this._sendChange("add_card",stateManager,{columnid:columnId,aftercard:afterCard})}async moveCard(stateManager,cardId,columnId,afterCard){await this._sendChange("move_card",stateManager,{cardid:cardId,columnid:columnId,aftercard:afterCard})}async deleteColumn(stateManager,columnId){await this._sendChange("delete_column",stateManager,{columnid:columnId})}async addColumn(stateManager,afterColumn){await this._sendChange("add_column",stateManager,{aftercol:afterColumn})}async moveColumn(stateManager,columnId,afterColumn){await this._sendChange("move_column",stateManager,{columnid:columnId,aftercol:afterColumn})}async assignUser(stateManager,cardId){let userId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;await this._sendChange("assign_user",stateManager,{cardid:cardId,userid:userId})}async completeCard(stateManager,cardId){await this._sendChange("set_card_complete",stateManager,{cardid:cardId,state:1})}async uncompleteCard(stateManager,cardId){await this._sendChange("set_card_complete",stateManager,{cardid:cardId,state:0})}async unassignUser(stateManager,cardId){let userId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;await this._sendChange("unassign_user",stateManager,{cardid:cardId,userid:userId})}async lockColumn(stateManager,columnId){await this._sendChange("set_column_locked",stateManager,{columnid:columnId,state:1})}async unlockColumn(stateManager,columnId){await this._sendChange("set_column_locked",stateManager,{columnid:columnId,state:0})}async lockColumns(stateManager){await this._sendChange("set_board_columns_locked",stateManager,{state:1})}async unlockColumns(stateManager){await this._sendChange("set_board_columns_locked",stateManager,{state:0})}async sendDiscussionMessage(stateManager,cardId,message){await this._sendChange("add_discussion_message",stateManager,{cardid:cardId,message:message})}async deleteMessage(stateManager,messageId){await this._sendChange("delete_discussion_message",stateManager,{messageid:messageId})}async pushCard(stateManager,cardId){await this._sendChange("push_card_copy",stateManager,{cardid:cardId})}async _sendChange(method,stateManager,data){const state=stateManager.state,request={methodname:"mod_kanban_"+method,args:{cmid:state.common.id,boardid:state.board.id,data:data},fail:this.displayError};let result=null;try{result=await _ajax.default.call([request])[0]}catch(e){return _log.default.warn("Sending a change request to the kanban backend failed, probably due to connection loss."),void this.processUpdateFail(stateManager)}this.processUpdates(stateManager,result)}async getUpdates(stateManager){const state=stateManager.state;if(void 0===state.board)stateManager.setReadOnly(!1),stateManager.eventsToPublish.push({eventName:"board:deleted",eventData:{},action:"deleted"}),stateManager.setReadOnly(!0);else{let result=null;try{result=await _ajax.default.call([{methodname:"mod_kanban_get_kanban_content_update",args:{cmid:state.common.id,boardid:state.board.id,timestamp:state.common.timestamp},fail:()=>{this.processUpdateFail(stateManager)}}])[0]}catch(e){return _log.default.warn("Retrieving the updated state of the kanban board failed, probably due to connection loss."),void this.processUpdateFail(stateManager)}this.processUpdates(stateManager,result)}}processUpdateFail(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.common.updatefails++,stateManager.setReadOnly(!0)}async displayError(data){data.message&&await _notification.default.alert((0,_str.get_string)("error"),data.message,(0,_str.get_string)("cancel"))}async getDiscussionUpdates(stateManager,cardId){const state=stateManager.state;let timestamp=0;state.discussions.forEach((c=>{c.kanban_card==cardId&&c.timestamp>timestamp&&(timestamp=c.timestamp)}));const result=await _ajax.default.call([{methodname:"mod_kanban_get_discussion_update",args:{cmid:state.common.id,boardid:state.board.id,cardid:cardId,timestamp:timestamp},fail:()=>{this.processUpdateFail(stateManager)}}])[0];this.processUpdates(stateManager,result)}async getHistoryUpdates(stateManager,cardId){const state=stateManager.state;let timestamp=0;state.history.forEach((c=>{c.kanban_card==cardId&&c.timestamp>timestamp&&(timestamp=c.timestamp)}));const result=await _ajax.default.call([{methodname:"mod_kanban_get_history_update",args:{cmid:state.common.id,boardid:state.board.id,cardid:cardId,timestamp:timestamp},fail:()=>{this.processUpdateFail(stateManager)}}])[0];this.processUpdates(stateManager,result)}async processUpdates(stateManager,result){let updates=JSON.parse(result.update);stateManager.processUpdates(updates)}},_exports.default})); +define("mod_kanban/mutations",["exports","core/ajax","core/notification","core/str","core/log"],(function(_exports,_ajax,_notification,_str,_log){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_log=_interopRequireDefault(_log);return _exports.default=class{constructor(){var obj,key,value;value=0,(key="updateFails")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}async saveAsTemplate(stateManager){await this._sendChange("save_as_template",stateManager)}async deleteCard(stateManager,cardId){await this._sendChange("delete_card",stateManager,{cardid:cardId})}async deleteBoard(stateManager){await this._sendChange("delete_board",stateManager)}async addCard(stateManager,columnId,afterCard){await this._sendChange("add_card",stateManager,{columnid:columnId,aftercard:afterCard})}async moveCard(stateManager,cardId,columnId,afterCard){await this._sendChange("move_card",stateManager,{cardid:cardId,columnid:columnId,aftercard:afterCard})}async deleteColumn(stateManager,columnId){await this._sendChange("delete_column",stateManager,{columnid:columnId})}async addColumn(stateManager,afterColumn){await this._sendChange("add_column",stateManager,{aftercol:afterColumn})}async moveColumn(stateManager,columnId,afterColumn){await this._sendChange("move_column",stateManager,{columnid:columnId,aftercol:afterColumn})}async assignUser(stateManager,cardId){let userId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;await this._sendChange("assign_user",stateManager,{cardid:cardId,userid:userId})}async completeCard(stateManager,cardId){await this._sendChange("set_card_complete",stateManager,{cardid:cardId,state:1})}async uncompleteCard(stateManager,cardId){await this._sendChange("set_card_complete",stateManager,{cardid:cardId,state:0})}async unassignUser(stateManager,cardId){let userId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;await this._sendChange("unassign_user",stateManager,{cardid:cardId,userid:userId})}async lockColumn(stateManager,columnId){await this._sendChange("set_column_locked",stateManager,{columnid:columnId,state:1})}async unlockColumn(stateManager,columnId){await this._sendChange("set_column_locked",stateManager,{columnid:columnId,state:0})}async lockColumns(stateManager){await this._sendChange("set_board_columns_locked",stateManager,{state:1})}async unlockColumns(stateManager){await this._sendChange("set_board_columns_locked",stateManager,{state:0})}async sendDiscussionMessage(stateManager,cardId,message){await this._sendChange("add_discussion_message",stateManager,{cardid:cardId,message:message})}async deleteMessage(stateManager,messageId){await this._sendChange("delete_discussion_message",stateManager,{messageid:messageId})}async pushCard(stateManager,cardId){await this._sendChange("push_card_copy",stateManager,{cardid:cardId})}async _sendChange(method,stateManager,data){const state=stateManager.state,request={methodname:"mod_kanban_"+method,args:{cmid:state.common.id,boardid:state.board.id,data:data},fail:this.displayError};let result=null;try{result=await _ajax.default.call([request])[0]}catch(e){return void(e instanceof Object||(_log.default.warn("Sending a change request to the kanban backend failed, probably due to connection loss."),this.processUpdateFail(stateManager)))}this.processUpdates(stateManager,result)}async getUpdates(stateManager){const state=stateManager.state;if(void 0===state.board)stateManager.setReadOnly(!1),stateManager.eventsToPublish.push({eventName:"board:deleted",eventData:{},action:"deleted"}),stateManager.setReadOnly(!0);else{let result=null;try{result=await _ajax.default.call([{methodname:"mod_kanban_get_kanban_content_update",args:{cmid:state.common.id,boardid:state.board.id,timestamp:state.common.timestamp},fail:()=>{this.processUpdateFail(stateManager)}}])[0]}catch(e){return void(e instanceof Object||(_log.default.warn("Sending a change request to the kanban backend failed, probably due to connection loss."),this.processUpdateFail(stateManager)))}this.processUpdates(stateManager,result)}}processUpdateFail(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.common.updatefails++,stateManager.setReadOnly(!0)}async displayError(data){data.message&&await _notification.default.alert((0,_str.get_string)("error"),data.message,(0,_str.get_string)("cancel"))}async getDiscussionUpdates(stateManager,cardId){const state=stateManager.state;let timestamp=0;state.discussions.forEach((c=>{c.kanban_card==cardId&&c.timestamp>timestamp&&(timestamp=c.timestamp)}));const result=await _ajax.default.call([{methodname:"mod_kanban_get_discussion_update",args:{cmid:state.common.id,boardid:state.board.id,cardid:cardId,timestamp:timestamp},fail:()=>{this.processUpdateFail(stateManager)}}])[0];this.processUpdates(stateManager,result)}async getHistoryUpdates(stateManager,cardId){const state=stateManager.state;let timestamp=0;state.history.forEach((c=>{c.kanban_card==cardId&&c.timestamp>timestamp&&(timestamp=c.timestamp)}));const result=await _ajax.default.call([{methodname:"mod_kanban_get_history_update",args:{cmid:state.common.id,boardid:state.board.id,cardid:cardId,timestamp:timestamp},fail:()=>{this.processUpdateFail(stateManager)}}])[0];this.processUpdates(stateManager,result)}async processUpdates(stateManager,result){let updates=JSON.parse(result.update);stateManager.processUpdates(updates)}},_exports.default})); //# sourceMappingURL=mutations.min.js.map \ No newline at end of file diff --git a/amd/build/mutations.min.js.map b/amd/build/mutations.min.js.map index 24c5610c..6d212c58 100644 --- a/amd/build/mutations.min.js.map +++ b/amd/build/mutations.min.js.map @@ -1 +1 @@ -{"version":3,"file":"mutations.min.js","sources":["../src/mutations.js"],"sourcesContent":["import Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport Log from 'core/log';\n\n/**\n * Mutations library for mod_kanban.\n * The functions are just used to forward data to the webservice.\n */\nexport default class {\n // Attribute for counting update fails.\n updateFails = 0;\n\n async saveAsTemplate(stateManager) {\n await this._sendChange('save_as_template', stateManager);\n }\n\n /**\n * Delete a card.\n * @param {*} stateManager StateManager instance\n * @param {number} cardId Id of the card to be deleted\n */\n async deleteCard(stateManager, cardId) {\n await this._sendChange('delete_card', stateManager, {cardid: cardId});\n }\n\n /**\n * Delete the board.\n * @param {*} stateManager StateManager instance\n */\n async deleteBoard(stateManager) {\n await this._sendChange('delete_board', stateManager);\n }\n\n /**\n * Add a card after an existing one.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column\n * @param {number} afterCard Id of the card before (0 means to insert at the top of the column)\n */\n async addCard(stateManager, columnId, afterCard) {\n await this._sendChange('add_card', stateManager, {columnid: columnId, aftercard: afterCard});\n }\n\n /**\n * Move a card to another column.\n * @param {*} stateManager StateManager instance\n * @param {number} cardId Id of the card to be deleted\n * @param {number} columnId Id of the new column\n * @param {number} afterCard Id of the card before (0 means to move at the top of the column)\n */\n async moveCard(stateManager, cardId, columnId, afterCard) {\n await this._sendChange('move_card', stateManager, {cardid: cardId, columnid: columnId, aftercard: afterCard});\n }\n\n /**\n * Deletes a column and all cards within.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to delete\n */\n async deleteColumn(stateManager, columnId) {\n await this._sendChange('delete_column', stateManager, {columnid: columnId});\n }\n\n /**\n * Adds a new column.\n * @param {*} stateManager StateManager instance\n * @param {number} afterColumn Id of the column before (0 means to insert at the left of the board)\n */\n async addColumn(stateManager, afterColumn) {\n await this._sendChange('add_column', stateManager, {aftercol: afterColumn});\n }\n\n /**\n * Moves a column to a new place.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to move\n * @param {number} afterColumn Id of the column before (0 means to insert at the left of the board)\n */\n async moveColumn(stateManager, columnId, afterColumn) {\n await this._sendChange('move_column', stateManager, {columnid: columnId, aftercol: afterColumn});\n }\n\n /**\n * Assign a user to a card.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n * @param {number} userId Id of the user to assign (0 means to assign the current user)\n */\n async assignUser(stateManager, cardId, userId = 0) {\n await this._sendChange('assign_user', stateManager, {cardid: cardId, userid: userId});\n }\n\n /**\n * Mark a card as completed.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n */\n async completeCard(stateManager, cardId) {\n await this._sendChange('set_card_complete', stateManager, {cardid: cardId, state: 1});\n }\n\n /**\n * Mark a card as not completed.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n */\n async uncompleteCard(stateManager, cardId) {\n await this._sendChange('set_card_complete', stateManager, {cardid: cardId, state: 0});\n }\n\n /**\n * Remove assignment for a user to a card.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n * @param {number} userId Id of the user to unassign, defaults to 0 (current user)\n */\n async unassignUser(stateManager, cardId, userId = 0) {\n await this._sendChange('unassign_user', stateManager, {cardid: cardId, userid: userId});\n }\n\n /**\n * Locks a column.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to lock\n */\n async lockColumn(stateManager, columnId) {\n await this._sendChange('set_column_locked', stateManager, {columnid: columnId, state: 1});\n }\n\n /**\n * Unlocks a column.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to unlock\n */\n async unlockColumn(stateManager, columnId) {\n await this._sendChange('set_column_locked', stateManager, {columnid: columnId, state: 0});\n }\n\n /**\n * Locks all columns of the board.\n * @param {*} stateManager StateManager instance\n */\n async lockColumns(stateManager) {\n await this._sendChange('set_board_columns_locked', stateManager, {state: 1});\n }\n\n /**\n * Unlocks all columns of the board.\n * @param {*} stateManager StateManager instance\n */\n async unlockColumns(stateManager) {\n await this._sendChange('set_board_columns_locked', stateManager, {state: 0});\n }\n\n /**\n * Adds a message to discussion.\n * @param {*} stateManager\n * @param {*} cardId\n * @param {*} message\n */\n async sendDiscussionMessage(stateManager, cardId, message) {\n await this._sendChange('add_discussion_message', stateManager, {cardid: cardId, message: message});\n }\n\n /**\n * Delete a message from a discussion.\n * @param {*} stateManager StateManager instance\n * @param {number} messageId Id of the message to be deleted\n */\n async deleteMessage(stateManager, messageId) {\n await this._sendChange('delete_discussion_message', stateManager, {messageid: messageId});\n }\n\n /**\n * Push a copy of a card to all boards.\n * @param {*} stateManager StateManager instance\n * @param {number} cardId Id of the card to be pushed\n */\n async pushCard(stateManager, cardId) {\n await this._sendChange('push_card_copy', stateManager, {cardid: cardId});\n }\n\n /**\n * Send change request to webservice\n * @param {string} method Name of the method\n * @param {*} stateManager StateManager instance\n * @param {object} data Data to send\n */\n async _sendChange(method, stateManager, data) {\n const state = stateManager.state;\n const request = {\n methodname: 'mod_kanban_' + method,\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n data: data\n },\n fail: this.displayError,\n };\n\n let result = null;\n try {\n result = await Ajax.call([request])[0];\n } catch (e) {\n // If the request cannot be performed (connection loss for example) we need to catch this error here.\n Log.warn('Sending a change request to the kanban backend failed, probably due to connection loss.');\n this.processUpdateFail(stateManager);\n return;\n }\n\n this.processUpdates(stateManager, result);\n }\n\n /**\n * Update state.\n * @param {*} stateManager\n */\n async getUpdates(stateManager) {\n const state = stateManager.state;\n if (state.board === undefined) {\n stateManager.setReadOnly(false);\n stateManager.eventsToPublish.push({\n eventName: `board:deleted`,\n eventData: {},\n action: `deleted`,\n });\n stateManager.setReadOnly(true);\n } else {\n let result = null;\n try {\n result = await Ajax.call([{\n methodname: 'mod_kanban_get_kanban_content_update',\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n timestamp: state.common.timestamp,\n },\n fail: () => {\n this.processUpdateFail(stateManager);\n },\n }])[0];\n } catch (e) {\n // If the request cannot be performed (connection loss for example) we need to catch this error here.\n Log.warn('Retrieving the updated state of the kanban board failed, probably due to connection loss.');\n this.processUpdateFail(stateManager);\n return;\n }\n\n this.processUpdates(stateManager, result);\n }\n }\n\n /**\n * Count update fails.\n * @param {*} stateManager\n */\n processUpdateFail(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.common.updatefails++;\n stateManager.setReadOnly(true);\n }\n\n /**\n * Show a modal to display an error message\n * @param {*} data\n */\n async displayError(data) {\n if (data.message) {\n // Can switch to direct call of getString when dropping support for Moodle 4.1.\n await Notification.alert(getString('error'), data.message, getString('cancel'));\n }\n }\n\n /**\n * Update discussions for a card.\n * @param {*} stateManager\n * @param {number} cardId\n */\n async getDiscussionUpdates(stateManager, cardId) {\n const state = stateManager.state;\n let timestamp = 0;\n state.discussions.forEach((c) => {\n if (c.kanban_card == cardId) {\n if (c.timestamp > timestamp) {\n timestamp = c.timestamp;\n }\n }\n });\n\n const result = await Ajax.call([{\n methodname: 'mod_kanban_get_discussion_update',\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n cardid: cardId,\n timestamp: timestamp,\n },\n fail: () => {\n this.processUpdateFail(stateManager);\n },\n }])[0];\n\n this.processUpdates(stateManager, result);\n }\n\n /**\n * Update history for a card.\n * @param {*} stateManager\n * @param {number} cardId\n */\n async getHistoryUpdates(stateManager, cardId) {\n const state = stateManager.state;\n let timestamp = 0;\n state.history.forEach((c) => {\n if (c.kanban_card == cardId) {\n if (c.timestamp > timestamp) {\n timestamp = c.timestamp;\n }\n }\n });\n\n const result = await Ajax.call([{\n methodname: 'mod_kanban_get_history_update',\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n cardid: cardId,\n timestamp: timestamp,\n },\n fail: () => {\n this.processUpdateFail(stateManager);\n },\n }])[0];\n\n this.processUpdates(stateManager, result);\n }\n\n /**\n * Process updates.\n *\n * @param {*} stateManager\n * @param {*} result\n */\n async processUpdates(stateManager, result) {\n let updates = JSON.parse(result.update);\n stateManager.processUpdates(updates);\n }\n}\n"],"names":["stateManager","this","_sendChange","cardId","cardid","columnId","afterCard","columnid","aftercard","afterColumn","aftercol","userId","userid","state","message","messageId","messageid","method","data","request","methodname","args","cmid","common","id","boardid","board","fail","displayError","result","Ajax","call","e","warn","processUpdateFail","processUpdates","undefined","setReadOnly","eventsToPublish","push","eventName","eventData","action","timestamp","updatefails","Notification","alert","discussions","forEach","c","kanban_card","history","updates","JSON","parse","update"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"mutations.min.js","sources":["../src/mutations.js"],"sourcesContent":["import Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport Log from 'core/log';\n\n/**\n * Mutations library for mod_kanban.\n * The functions are just used to forward data to the webservice.\n */\nexport default class {\n // Attribute for counting update fails.\n updateFails = 0;\n\n async saveAsTemplate(stateManager) {\n await this._sendChange('save_as_template', stateManager);\n }\n\n /**\n * Delete a card.\n * @param {*} stateManager StateManager instance\n * @param {number} cardId Id of the card to be deleted\n */\n async deleteCard(stateManager, cardId) {\n await this._sendChange('delete_card', stateManager, {cardid: cardId});\n }\n\n /**\n * Delete the board.\n * @param {*} stateManager StateManager instance\n */\n async deleteBoard(stateManager) {\n await this._sendChange('delete_board', stateManager);\n }\n\n /**\n * Add a card after an existing one.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column\n * @param {number} afterCard Id of the card before (0 means to insert at the top of the column)\n */\n async addCard(stateManager, columnId, afterCard) {\n await this._sendChange('add_card', stateManager, {columnid: columnId, aftercard: afterCard});\n }\n\n /**\n * Move a card to another column.\n * @param {*} stateManager StateManager instance\n * @param {number} cardId Id of the card to be deleted\n * @param {number} columnId Id of the new column\n * @param {number} afterCard Id of the card before (0 means to move at the top of the column)\n */\n async moveCard(stateManager, cardId, columnId, afterCard) {\n await this._sendChange('move_card', stateManager, {cardid: cardId, columnid: columnId, aftercard: afterCard});\n }\n\n /**\n * Deletes a column and all cards within.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to delete\n */\n async deleteColumn(stateManager, columnId) {\n await this._sendChange('delete_column', stateManager, {columnid: columnId});\n }\n\n /**\n * Adds a new column.\n * @param {*} stateManager StateManager instance\n * @param {number} afterColumn Id of the column before (0 means to insert at the left of the board)\n */\n async addColumn(stateManager, afterColumn) {\n await this._sendChange('add_column', stateManager, {aftercol: afterColumn});\n }\n\n /**\n * Moves a column to a new place.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to move\n * @param {number} afterColumn Id of the column before (0 means to insert at the left of the board)\n */\n async moveColumn(stateManager, columnId, afterColumn) {\n await this._sendChange('move_column', stateManager, {columnid: columnId, aftercol: afterColumn});\n }\n\n /**\n * Assign a user to a card.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n * @param {number} userId Id of the user to assign (0 means to assign the current user)\n */\n async assignUser(stateManager, cardId, userId = 0) {\n await this._sendChange('assign_user', stateManager, {cardid: cardId, userid: userId});\n }\n\n /**\n * Mark a card as completed.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n */\n async completeCard(stateManager, cardId) {\n await this._sendChange('set_card_complete', stateManager, {cardid: cardId, state: 1});\n }\n\n /**\n * Mark a card as not completed.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n */\n async uncompleteCard(stateManager, cardId) {\n await this._sendChange('set_card_complete', stateManager, {cardid: cardId, state: 0});\n }\n\n /**\n * Remove assignment for a user to a card.\n * @param {*} stateManager StateManager instance.\n * @param {number} cardId Id of the card\n * @param {number} userId Id of the user to unassign, defaults to 0 (current user)\n */\n async unassignUser(stateManager, cardId, userId = 0) {\n await this._sendChange('unassign_user', stateManager, {cardid: cardId, userid: userId});\n }\n\n /**\n * Locks a column.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to lock\n */\n async lockColumn(stateManager, columnId) {\n await this._sendChange('set_column_locked', stateManager, {columnid: columnId, state: 1});\n }\n\n /**\n * Unlocks a column.\n * @param {*} stateManager StateManager instance\n * @param {number} columnId Id of the column to unlock\n */\n async unlockColumn(stateManager, columnId) {\n await this._sendChange('set_column_locked', stateManager, {columnid: columnId, state: 0});\n }\n\n /**\n * Locks all columns of the board.\n * @param {*} stateManager StateManager instance\n */\n async lockColumns(stateManager) {\n await this._sendChange('set_board_columns_locked', stateManager, {state: 1});\n }\n\n /**\n * Unlocks all columns of the board.\n * @param {*} stateManager StateManager instance\n */\n async unlockColumns(stateManager) {\n await this._sendChange('set_board_columns_locked', stateManager, {state: 0});\n }\n\n /**\n * Adds a message to discussion.\n * @param {*} stateManager\n * @param {*} cardId\n * @param {*} message\n */\n async sendDiscussionMessage(stateManager, cardId, message) {\n await this._sendChange('add_discussion_message', stateManager, {cardid: cardId, message: message});\n }\n\n /**\n * Delete a message from a discussion.\n * @param {*} stateManager StateManager instance\n * @param {number} messageId Id of the message to be deleted\n */\n async deleteMessage(stateManager, messageId) {\n await this._sendChange('delete_discussion_message', stateManager, {messageid: messageId});\n }\n\n /**\n * Push a copy of a card to all boards.\n * @param {*} stateManager StateManager instance\n * @param {number} cardId Id of the card to be pushed\n */\n async pushCard(stateManager, cardId) {\n await this._sendChange('push_card_copy', stateManager, {cardid: cardId});\n }\n\n /**\n * Send change request to webservice\n * @param {string} method Name of the method\n * @param {*} stateManager StateManager instance\n * @param {object} data Data to send\n */\n async _sendChange(method, stateManager, data) {\n const state = stateManager.state;\n const request = {\n methodname: 'mod_kanban_' + method,\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n data: data\n },\n fail: this.displayError,\n };\n\n let result = null;\n try {\n result = await Ajax.call([request])[0];\n } catch (e) {\n if (!(e instanceof Object)) {\n // If we do not receive a proper object, there has an issue with requesting the webservice.\n // This for example could be due to a connection loss. We therefore need to catch this error separately.\n Log.warn('Sending a change request to the kanban backend failed, probably due to connection loss.');\n this.processUpdateFail(stateManager);\n }\n return;\n }\n\n this.processUpdates(stateManager, result);\n }\n\n /**\n * Update state.\n * @param {*} stateManager\n */\n async getUpdates(stateManager) {\n const state = stateManager.state;\n if (state.board === undefined) {\n stateManager.setReadOnly(false);\n stateManager.eventsToPublish.push({\n eventName: `board:deleted`,\n eventData: {},\n action: `deleted`,\n });\n stateManager.setReadOnly(true);\n } else {\n let result = null;\n try {\n result = await Ajax.call([{\n methodname: 'mod_kanban_get_kanban_content_update',\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n timestamp: state.common.timestamp,\n },\n fail: () => {\n this.processUpdateFail(stateManager);\n },\n }])[0];\n } catch (e) {\n if (!(e instanceof Object)) {\n // If we do not receive a proper object, there has an issue with requesting the webservice.\n // This for example could be due to a connection loss. We therefore need to catch this error separately.\n Log.warn('Sending a change request to the kanban backend failed, probably due to connection loss.');\n this.processUpdateFail(stateManager);\n }\n return;\n }\n\n this.processUpdates(stateManager, result);\n }\n }\n\n /**\n * Count update fails.\n * @param {*} stateManager\n */\n processUpdateFail(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.common.updatefails++;\n stateManager.setReadOnly(true);\n }\n\n /**\n * Show a modal to display an error message\n * @param {*} data\n */\n async displayError(data) {\n if (data.message) {\n // Can switch to direct call of getString when dropping support for Moodle 4.1.\n await Notification.alert(getString('error'), data.message, getString('cancel'));\n }\n }\n\n /**\n * Update discussions for a card.\n * @param {*} stateManager\n * @param {number} cardId\n */\n async getDiscussionUpdates(stateManager, cardId) {\n const state = stateManager.state;\n let timestamp = 0;\n state.discussions.forEach((c) => {\n if (c.kanban_card == cardId) {\n if (c.timestamp > timestamp) {\n timestamp = c.timestamp;\n }\n }\n });\n\n const result = await Ajax.call([{\n methodname: 'mod_kanban_get_discussion_update',\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n cardid: cardId,\n timestamp: timestamp,\n },\n fail: () => {\n this.processUpdateFail(stateManager);\n },\n }])[0];\n\n this.processUpdates(stateManager, result);\n }\n\n /**\n * Update history for a card.\n * @param {*} stateManager\n * @param {number} cardId\n */\n async getHistoryUpdates(stateManager, cardId) {\n const state = stateManager.state;\n let timestamp = 0;\n state.history.forEach((c) => {\n if (c.kanban_card == cardId) {\n if (c.timestamp > timestamp) {\n timestamp = c.timestamp;\n }\n }\n });\n\n const result = await Ajax.call([{\n methodname: 'mod_kanban_get_history_update',\n args: {\n cmid: state.common.id,\n boardid: state.board.id,\n cardid: cardId,\n timestamp: timestamp,\n },\n fail: () => {\n this.processUpdateFail(stateManager);\n },\n }])[0];\n\n this.processUpdates(stateManager, result);\n }\n\n /**\n * Process updates.\n *\n * @param {*} stateManager\n * @param {*} result\n */\n async processUpdates(stateManager, result) {\n let updates = JSON.parse(result.update);\n stateManager.processUpdates(updates);\n }\n}\n"],"names":["stateManager","this","_sendChange","cardId","cardid","columnId","afterCard","columnid","aftercard","afterColumn","aftercol","userId","userid","state","message","messageId","messageid","method","data","request","methodname","args","cmid","common","id","boardid","board","fail","displayError","result","Ajax","call","e","Object","warn","processUpdateFail","processUpdates","undefined","setReadOnly","eventsToPublish","push","eventName","eventData","action","timestamp","updatefails","Notification","alert","discussions","forEach","c","kanban_card","history","updates","JSON","parse","update"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/mutations.js b/amd/src/mutations.js index 899a93a6..5247ca91 100644 --- a/amd/src/mutations.js +++ b/amd/src/mutations.js @@ -203,9 +203,12 @@ export default class { try { result = await Ajax.call([request])[0]; } catch (e) { - // If the request cannot be performed (connection loss for example) we need to catch this error here. - Log.warn('Sending a change request to the kanban backend failed, probably due to connection loss.'); - this.processUpdateFail(stateManager); + if (!(e instanceof Object)) { + // If we do not receive a proper object, there has an issue with requesting the webservice. + // This for example could be due to a connection loss. We therefore need to catch this error separately. + Log.warn('Sending a change request to the kanban backend failed, probably due to connection loss.'); + this.processUpdateFail(stateManager); + } return; } @@ -241,9 +244,12 @@ export default class { }, }])[0]; } catch (e) { - // If the request cannot be performed (connection loss for example) we need to catch this error here. - Log.warn('Retrieving the updated state of the kanban board failed, probably due to connection loss.'); - this.processUpdateFail(stateManager); + if (!(e instanceof Object)) { + // If we do not receive a proper object, there has an issue with requesting the webservice. + // This for example could be due to a connection loss. We therefore need to catch this error separately. + Log.warn('Sending a change request to the kanban backend failed, probably due to connection loss.'); + this.processUpdateFail(stateManager); + } return; }