diff --git a/CHANGES.md b/CHANGES.md
index 5960cf2..74953f9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+Changes in v5.2-r1
+----------------------------------
+
+- support for Moodle 5.2
+- add CHANGES.md for user-visible changes
+
Changes in v5.0-r1 (since v4.5-r1)
----------------------------------
diff --git a/amd/build/blocks_groups_visibility.min.js b/amd/build/blocks_groups_visibility.min.js
index b38aca0..61e53d3 100644
--- a/amd/build/blocks_groups_visibility.min.js
+++ b/amd/build/blocks_groups_visibility.min.js
@@ -5,6 +5,6 @@
* @copyright 2016/17 N Herrmann
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define("block_groups/blocks_groups_visibility",["jquery","core/ajax","core/url","core/notification","core/str"],(function($,ajax,url,notification,str){var removeWarning=function(identifier){$(".block_groups").find(".warning"+identifier).remove()},removeSpinner=function(groupid){var divgroup=$(".block_groups");divgroup.find(".spinner"+groupid).remove(),divgroup.find(".imggroup-"+groupid).show()},removeSpinners=function(){var divgroups=$(".block_groups");divgroups.find(".spinner-all").remove(),divgroups.find(".imggroup").show()},reloadPage=function(){location.reload(!0)},createWarningMessage=function(){str.get_strings([{key:"errortitle",component:"block_groups"},{key:"nochangeindatabasepossiblereload",component:"block_groups"},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],s[1],s[2],s[3],reloadPage)})).fail(notification.exception)},addWarning=function(identifier){var divgroups=$(".block_groups");if(divgroups.find(".warning"+identifier).length)return removeSpinner(identifier),!1;"all"===identifier?removeSpinners():removeSpinner(identifier);var imgurl=url.imageUrl("i/warning","moodle"),warning=document.createElement("img");return warning.className="warning"+identifier+" block-groups-warning",warning.src=imgurl,createWarningMessage(),divgroups.find(".imggroup-"+identifier).before(warning),divgroups.find(".imggroup-"+identifier).on("click",createWarningMessage),divgroups.find(".warning"+identifier).on("click",createWarningMessage),!1},changevisibility=function(event){var divgroups=$(".block_groups"),groupid=$(this).data("groupid");if(divgroups.find(".spinner"+$(this).data("groupid")).length>0||divgroups.find(".spinner-all").length>0)return!1;!function(groupid){var divgroups=$(".block_groups");divgroups.find(".warning"+groupid).length>0&&removeWarning(groupid);var imgurl=url.imageUrl("i/loading_small","moodle"),spinner=document.createElement("img");spinner.className="spinner"+groupid+" spinner block-groups-spinner",spinner.src=imgurl,spinner.hidden=!1,divgroups.find(".imggroup-"+groupid).before(spinner),divgroups.find(".imggroup-"+groupid).hide()}($(this).data("groupid"));var promises=ajax.call([{methodname:"block_groups_create_output",args:{groups:{id:$(this).data("groupid"),courseid:event.data.courseid}}}]);return $(document).ajaxError((function(){return addWarning(groupid),!1})),promises[0].then((function(response){return null===response||!0===response.error?(addWarning(groupid),!1):(divgroups.find(".group-"+response.id).replaceWith(response.newelement),0===response.visibility&&divgroups.find(".membergroup-"+response.id).removeClass("hiddengroups"),1===response.visibility&&divgroups.find(".membergroup-"+response.id).addClass("hiddengroups"),divgroups.find(".group-"+response.id+" .block_groups_toggle").on("click",{courseid:event.data.courseid},changevisibility),removeSpinner(response.id),!1)})).fail((function(){return addWarning(groupid),!1})),!1},addNotification=function(type,text){notification.addNotification({message:text,type:type})},changeVisibilityAll=function(event){if($(".block_groups").find(".spinner").length>0)return!1;var warningexist=$(".block_groups").find(".block-groups-warning");if(void 0!==warningexist&&warningexist.length>0)return addWarning("all"),!1;!function(){var divgroups=$(".block_groups");divgroups.find(".warningall").length>0&&removeWarning("all");var imgurl=url.imageUrl("i/loading_small","moodle"),spinner=document.createElement("img");spinner.className="spinner-all spinner block-groups-spinner",spinner.src=imgurl,spinner.hidden=!1,divgroups.find(".imggroup").before(spinner),divgroups.find(".imggroup").hide()}();var promises=ajax.call([{methodname:"block_groups_create_allgroups_output",args:{groups:{action:$(this).data("action"),courseid:event.data.courseid}}}]);return $(document).ajaxError((function(){return addWarning("all"),!1})),promises[0].then((function(response){var divgroups=$(".block_groups");return null===response||!0===response.error?(addWarning("all"),!1):0===response.visibility?(divgroups.find(".content").replaceWith(response.newelement),str.get_strings([{key:"nogroups",component:"block_groups"}]).done((function(s){addNotification("error",s[0])})).fail(notification.exception),!1):(divgroups.find(".wrapperlistgroup").replaceWith(response.newelement),divgroups.find(".block_groups_toggle").on("click",{courseid:event.data.courseid},changevisibility),function(response){var membergroup=$(".block-groups-membergroup"),visibility=response.visibility;2===visibility&&membergroup.removeClass("hiddengroups"),1===visibility&&membergroup.addClass("hiddengroups")}(response),str.get_strings([{key:"groupschangedhidden",component:"block_groups"},{key:"groupschangedvisible",component:"block_groups"},{key:"allgroupsinstatehidden",component:"block_groups"},{key:"allgroupsinstatevisible",component:"block_groups"}]).done((function(s){switch(response.visibility){case 1:addNotification("success",s[0]);break;case 2:addNotification("success",s[1]);break;case 3:addNotification("warning",s[2]);break;case 4:addNotification("warning",s[3])}})).fail(notification.exception),!1)})).fail((function(){return addWarning("all"),!1})),removeSpinners(),!1};return{initialise:function(courseid){$(".block_groups_toggle").on("click",{courseid:courseid},changevisibility),$(".block_groups_all_toggle").on("click",{courseid:courseid},changeVisibilityAll)}}}));
+define("block_groups/blocks_groups_visibility",["jquery","core/ajax","core/url","core/notification","core/str"],(function($,ajax,url,notification,str){var removeWarning=function(identifier){$(".block_groups").find(".warning"+identifier).remove()},removeSpinner=function(groupid){var divgroup=$(".block_groups");divgroup.find(".spinner"+groupid).remove(),divgroup.find(".imggroup-"+groupid).show()},removeSpinners=function(){var divgroups=$(".block_groups");divgroups.find(".spinner-all").remove(),divgroups.find(".imggroup").show()},reloadPage=function(){location.reload(!0)},createWarningMessage=function(){str.get_strings([{key:"errortitle",component:"block_groups"},{key:"nochangeindatabasepossiblereload",component:"block_groups"},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],s[1],s[2],s[3],reloadPage)})).fail(notification.exception)},addWarning=function(identifier){var divgroups=$(".block_groups");if(divgroups.find(".warning"+identifier).length)return removeSpinner(identifier),!1;"all"===identifier?removeSpinners():removeSpinner(identifier);var imgurl=url.imageUrl("i/warning","moodle"),warning=document.createElement("img");return warning.className="warning"+identifier+" block-groups-warning",warning.src=imgurl,createWarningMessage(),divgroups.find(".imggroup-"+identifier).before(warning),divgroups.find(".imggroup-"+identifier).on("click",createWarningMessage),divgroups.find(".warning"+identifier).on("click",createWarningMessage),!1},changevisibility=function(event){var divgroups=$(".block_groups"),groupid=$(this).data("groupid");if(divgroups.find(".spinner"+$(this).data("groupid")).length>0||divgroups.find(".spinner-all").length>0)return!1;!function(groupid){var divgroups=$(".block_groups");divgroups.find(".warning"+groupid).length>0&&removeWarning(groupid);var imgurl=url.imageUrl("i/loading_small","moodle"),spinner=document.createElement("img");spinner.className="spinner"+groupid+" spinner block-groups-spinner",spinner.src=imgurl,spinner.hidden=!1,divgroups.find(".imggroup-"+groupid).before(spinner),divgroups.find(".imggroup-"+groupid).hide()}($(this).data("groupid"));var promises=ajax.call([{methodname:"block_groups_create_output",args:{groups:{id:$(this).data("groupid"),courseid:event.data.courseid}}}]);return $(document).ajaxError((function(){return addWarning(groupid),!1})),promises[0].then((function(response){return null===response||!0===response.error?(addWarning(groupid),!1):(divgroups.find(".group-"+response.id).replaceWith(response.newelement),0===response.visibility&&divgroups.find(".membergroup-"+response.id).removeClass("hiddengroups"),1===response.visibility&&divgroups.find(".membergroup-"+response.id).addClass("hiddengroups"),divgroups.find(".group-"+response.id+" .block_groups_toggle").on("click",{courseid:event.data.courseid},changevisibility),removeSpinner(response.id),!1)})).fail((function(){return addWarning(groupid),!1})),!1},addNotification=function(type,text){notification.addNotification({message:text,type:type})},changeVisibilityAll=function(event){if($(".block_groups").find(".spinner").length>0)return!1;var warningexist=$(".block_groups").find(".block-groups-warning");if(void 0!==warningexist&&warningexist.length>0)return addWarning("all"),!1;!function(){var divgroups=$(".block_groups");divgroups.find(".warningall").length>0&&removeWarning("all");var imgurl=url.imageUrl("i/loading_small","moodle"),spinner=document.createElement("img");spinner.className="spinner-all spinner block-groups-spinner",spinner.src=imgurl,spinner.hidden=!1,divgroups.find(".imggroup").before(spinner),divgroups.find(".imggroup").hide()}();var promises=ajax.call([{methodname:"block_groups_create_allgroups_output",args:{groups:{action:$(this).data("action"),courseid:event.data.courseid}}}]);return $(document).ajaxError((function(){return addWarning("all"),!1})),promises[0].then((function(response){var divgroups=$(".block_groups");return null===response||!0===response.error?(addWarning("all"),!1):0===response.visibility?(divgroups.find(".content").replaceWith(response.newelement),str.get_strings([{key:"nogroups",component:"block_groups"}]).done((function(s){addNotification("error",s[0])})).fail(notification.exception),!1):(divgroups.find(".wrapperlistgroup").replaceWith(response.newelement),divgroups.find(".block_groups_toggle").on("click",{courseid:event.data.courseid},changevisibility),function(response){var membergroup=$(".block-groups-membergroup"),visibility=response.visibility;2===visibility&&membergroup.removeClass("hiddengroups"),1===visibility&&membergroup.addClass("hiddengroups")}(response),str.get_strings([{key:"groupschangedhidden",component:"block_groups"},{key:"groupschangedvisible",component:"block_groups"},{key:"allgroupsinstatehidden",component:"block_groups"},{key:"allgroupsinstatevisible",component:"block_groups"}]).done((function(s){switch(response.visibility){case 1:addNotification("success",s[0]);break;case 2:addNotification("success",s[1]);break;case 3:addNotification("warning",s[2]);break;case 4:addNotification("warning",s[3])}})).fail(notification.exception),!1)})).fail((function(){return addWarning("all"),!1})),removeSpinners(),!1},changeVisibilityGrouping=function(event){var divgroups=$(".block_groups"),groupingid=$(this).data("groupingid"),action=$(this).data("action");if(divgroups.find(".spinner").length>0)return!1;var warningexist=divgroups.find(".block-groups-warning");if(void 0!==warningexist&&warningexist.length>0)return addWarning(groupingid),!1;!function(groupingid){var divgroups=$(".block_groups"),imgurl=url.imageUrl("i/loading_small","moodle");divgroups.find(".imggroup-grouping-"+groupingid).each((function(){var spinner=document.createElement("img");spinner.className="spinner-grouping-"+groupingid+" spinner block-groups-spinner",spinner.src=imgurl,spinner.hidden=!1,$(this).before(spinner),$(this).hide()}))}(groupingid);var promises=ajax.call([{methodname:"block_groups_create_grouping_output",args:{grouping:{groupingid:groupingid,courseid:event.data.courseid,action:action}}}]);return $(document).ajaxError((function(){return addWarning(groupingid),!1})),promises[0].then((function(response){return null===response||!0===response.error?(addWarning(groupingid),!1):(divgroups.find(".wrapperlistgroup").replaceWith(response.newelement),divgroups.find(".block_groups_toggle").on("click",{courseid:event.data.courseid},changevisibility),divgroups.find(".block_groups_grouping_toggle").on("click",{courseid:event.data.courseid},changeVisibilityGrouping),checkGroupingMembers(response),str.get_strings([{key:"groupingchangedhidden",component:"block_groups"},{key:"groupingchangedvisible",component:"block_groups"},{key:"groupinginstatehidden",component:"block_groups"},{key:"groupinginstatevisible",component:"block_groups"},{key:"nogroups",component:"block_groups"}]).done((function(s){switch(response.visibility){case 0:addNotification("warning",s[4]);break;case 1:addNotification("success",s[0]);break;case 2:addNotification("success",s[1]);break;case 3:addNotification("warning",s[2]);break;case 4:addNotification("warning",s[3])}})).fail(notification.exception),function(groupingid){var divgroups=$(".block_groups");divgroups.find(".spinner-grouping-"+groupingid).remove(),divgroups.find(".imggroup-grouping-"+groupingid).show()}(groupingid),!1)})).fail((function(){return addWarning(groupingid),!1})),!1},checkGroupingMembers=function(response){response.changedgroups.forEach((function(group){var membergroup=$(".membergroup-"+group.groupid);2===response.visibility&&membergroup.removeClass("hiddengroups"),1===response.visibility&&membergroup.addClass("hiddengroups")}))};return{initialise:function(courseid){$(".block_groups_toggle").on("click",{courseid:courseid},changevisibility),$(".block_groups_all_toggle").on("click",{courseid:courseid},changeVisibilityAll),$(".block_groups_grouping_toggle").on("click",{courseid:courseid},changeVisibilityGrouping)}}}));
//# sourceMappingURL=blocks_groups_visibility.min.js.map
\ No newline at end of file
diff --git a/amd/build/blocks_groups_visibility.min.js.map b/amd/build/blocks_groups_visibility.min.js.map
index 79bf1d2..a179b05 100644
--- a/amd/build/blocks_groups_visibility.min.js.map
+++ b/amd/build/blocks_groups_visibility.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"blocks_groups_visibility.min.js","sources":["../src/blocks_groups_visibility.js"],"sourcesContent":["// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n\n/**\n * Javascript module for block_groups\n *\n * @module block_groups\n * @copyright 2016/17 N Herrmann\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/url', 'core/notification', 'core/str'], function($, ajax, url, notification, str) {\n /**\n * Methode to remove warnings\n * @param {(Number|String)} identifier\n */\n var removeWarning = function(identifier) {\n $('.block_groups').find('.warning' + identifier).remove();\n };\n /**\n * Removes the Spinner Class of a single group.\n * @param {Number} groupid that identifies to which group the spinner belongs to.\n */\n var removeSpinner = function(groupid) {\n var divgroup = $('.block_groups');\n divgroup.find('.spinner' + groupid).remove();\n divgroup.find('.imggroup-' + groupid).show();\n };\n /**\n * Removes all spinners.\n */\n var removeSpinners = function() {\n var divgroups = $('.block_groups');\n divgroups.find('.spinner-all').remove();\n divgroups.find('.imggroup').show();\n };\n /**\n * Reloads the current page.\n */\n var reloadPage = function() {\n location.reload(true);\n };\n /**\n * Creates a warning message.\n */\n var createWarningMessage = function() {\n\n str.get_strings([\n {'key': 'errortitle', component: 'block_groups'},\n {'key': 'nochangeindatabasepossiblereload', component: 'block_groups'},\n {'key': 'yes'},\n {'key': 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], reloadPage);\n }).fail(notification.exception);\n };\n /**\n * Initialises Spinner for a single group.\n * @param {Number} groupid\n */\n var addSpinner = function(groupid) {\n var divgroups = $('.block_groups');\n if (divgroups.find('.warning' + groupid).length > 0) {\n\n removeWarning(groupid);\n }\n var imgurl = url.imageUrl(\"i/loading_small\", 'moodle');\n var spinner = document.createElement(\"img\");\n spinner.className = 'spinner' + groupid + ' spinner block-groups-spinner';\n spinner.src = imgurl;\n spinner.hidden = false;\n\n divgroups.find('.imggroup-' + groupid).before(spinner);\n divgroups.find('.imggroup-' + groupid).hide();\n };\n /**\n * Adds a warning(triangle with exclamation mark) in case the response is empty or the response throws an error.\n * @param {(Number|String)} identifier\n * @returns {Boolean} false\n */\n var addWarning = function(identifier) {\n var divgroups = $('.block_groups');\n var warningexist = divgroups.find('.warning' + identifier);\n if (warningexist.length) {\n removeSpinner(identifier);\n return false;\n } else {\n if (identifier === 'all') {\n removeSpinners();\n } else {\n removeSpinner(identifier);\n }\n var imgurl = url.imageUrl(\"i/warning\", 'moodle');\n var warning = document.createElement(\"img\");\n warning.className = 'warning' + identifier + ' block-groups-warning';\n warning.src = imgurl;\n createWarningMessage();\n (divgroups.find('.imggroup-' + identifier).before(warning));\n divgroups.find('.imggroup-' + identifier).on('click', createWarningMessage);\n divgroups.find('.warning' + identifier).on('click', createWarningMessage);\n }\n return false;\n };\n /**\n * Method that calls for an ajax script and replaces and/or changes the output components for a single group.\n * @param {*} event\n * @return {Boolean}\n */\n var changevisibility = function(event) {\n var divgroups = $('.block_groups');\n\n var groupid = $(this).data('groupid');\n if (divgroups.find('.spinner' + $(this).data('groupid')).length > 0 ||\n divgroups.find('.spinner-all').length > 0) {\n return false;\n }\n addSpinner($(this).data('groupid'));\n var promises = ajax.call([\n\n {\n methodname: 'block_groups_create_output', args: {\n groups: {\n id: $(this).data('groupid'),\n courseid: event.data.courseid\n }\n\n }\n }\n ]);\n $(document).ajaxError(function() {\n addWarning(groupid);\n return false;\n });\n\n promises[0].then(function(response) {\n if (response === null || response.error === true) {\n addWarning(groupid);\n return false;\n }\n divgroups.find('.group-' + response.id).replaceWith(response.newelement);\n // Replaces the used element, therefore removes the spinner.\n if (response.visibility === 0) {\n divgroups.find('.membergroup-' + response.id).removeClass('hiddengroups');\n }\n if (response.visibility === 1) {\n divgroups.find('.membergroup-' + response.id).addClass('hiddengroups');\n }\n divgroups.find('.group-' + response.id + ' .block_groups_toggle').on('click',\n {courseid: event.data.courseid}, changevisibility);\n removeSpinner(response.id);\n return false;\n }).fail(function() {\n addWarning(groupid);\n return false;\n });\n return false;\n };\n var checkMember = function(response) {\n var membergroup = $('.block-groups-membergroup');\n var visibility = response.visibility;\n if (visibility === 2) {\n membergroup.removeClass('hiddengroups');\n }\n if (visibility === 1) {\n membergroup.addClass('hiddengroups');\n }\n };\n /**\n * Initialises spinners for all groups.\n */\n var addSpinners = function() {\n var divgroups = $('.block_groups');\n if (divgroups.find('.warningall').length > 0) {\n removeWarning('all');\n }\n var imgurl = url.imageUrl(\"i/loading_small\", 'moodle');\n var spinner = document.createElement(\"img\");\n spinner.className = 'spinner-all spinner block-groups-spinner';\n spinner.src = imgurl;\n spinner.hidden = false;\n divgroups.find('.imggroup').before(spinner);\n divgroups.find('.imggroup').hide();\n };\n\n var addNotification = function(type, text) {\n notification.addNotification({\n message: text,\n type: type\n });\n };\n /**\n * Method that calls for an ajax script and replaces and/or changes the output components for all groups.\n * @param {Number} event\n * @return {Boolean}\n */\n var changeVisibilityAll = function(event) {\n if ($('.block_groups').find('.spinner').length > 0) {\n return false;\n }\n var warningexist = $('.block_groups').find('.block-groups-warning');\n if (typeof warningexist !== 'undefined' && warningexist.length > 0) {\n addWarning('all');\n return false;\n }\n addSpinners();\n // Calls for the externallib.\n var promises = ajax.call([\n {\n methodname: 'block_groups_create_allgroups_output', args: {\n groups: {\n action: $(this).data('action'),\n courseid: event.data.courseid\n }\n }\n }\n ]);\n $(document).ajaxError(function() {\n addWarning('all');\n return false;\n });\n // Response is processed.\n promises[0].then(function(response) {\n var divgroups = $('.block_groups');\n // Catch misleading responses.\n if (response === null || response.error === true) {\n addWarning('all');\n return false;\n }\n if (response.visibility === 0) {\n // Is nearly impossible since group has to be deleted during the request, however it is\n // possible, therefore a special message is thrown.\n divgroups.find('.content').replaceWith(response.newelement);\n str.get_strings([\n {key: 'nogroups', component: 'block_groups'}\n ]).done(function(s) {\n addNotification('error', s[0]);\n }).fail(notification.exception);\n return false;\n }\n\n // Old Elements are replaced and on click event added.\n divgroups.find('.wrapperlistgroup').replaceWith(response.newelement);\n divgroups.find('.block_groups_toggle').on('click', {courseid: event.data.courseid}, changevisibility);\n checkMember(response);\n // Outputvisibility 0->nogroups 1 -> hidden 2->visible 3-> all are hidden 4-> all are visible.\n str.get_strings([\n {key: 'groupschangedhidden', component: 'block_groups'},\n {key: 'groupschangedvisible', component: 'block_groups'},\n {key: 'allgroupsinstatehidden', component: 'block_groups'},\n {key: 'allgroupsinstatevisible', component: 'block_groups'}\n ]).done(function(s) {\n switch (response.visibility) {\n case 1:\n addNotification('success', s[0]);\n break;\n case 2:\n addNotification('success', s[1]);\n break;\n case 3:\n addNotification('warning', s[2]);\n break;\n case 4:\n addNotification('warning', s[3]);\n break;\n default:\n break;\n }\n }).fail(notification.exception);\n return false;\n }).fail(function() {\n addWarning('all');\n return false;\n });\n removeSpinners();\n return false;\n };\n\n /**\n * Calls for the main method. Either single groups are changed with block_groups_toggle or all groups with\n * block_groups_all_toggle.\n * @param {Number} courseid\n */\n return {\n initialise: function(courseid) {\n $('.block_groups_toggle').on('click', {courseid: courseid}, changevisibility);\n $('.block_groups_all_toggle').on('click', {courseid: courseid}, changeVisibilityAll);\n\n }\n };\n});\n"],"names":["define","$","ajax","url","notification","str","removeWarning","identifier","find","remove","removeSpinner","groupid","divgroup","show","removeSpinners","divgroups","reloadPage","location","reload","createWarningMessage","get_strings","component","done","s","confirm","fail","exception","addWarning","length","imgurl","imageUrl","warning","document","createElement","className","src","before","on","changevisibility","event","this","data","spinner","hidden","hide","addSpinner","promises","call","methodname","args","groups","id","courseid","ajaxError","then","response","error","replaceWith","newelement","visibility","removeClass","addClass","addNotification","type","text","message","changeVisibilityAll","warningexist","addSpinners","action","key","membergroup","checkMember","initialise"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"blocks_groups_visibility.min.js","sources":["../src/blocks_groups_visibility.js"],"sourcesContent":["// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n\n/**\n * Javascript module for block_groups\n *\n * @module block_groups\n * @copyright 2016/17 N Herrmann\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/url', 'core/notification', 'core/str'], function($, ajax, url, notification, str) {\n /**\n * Methode to remove warnings\n * @param {(Number|String)} identifier\n */\n var removeWarning = function(identifier) {\n $('.block_groups').find('.warning' + identifier).remove();\n };\n /**\n * Removes the Spinner Class of a single group.\n * @param {Number} groupid that identifies to which group the spinner belongs to.\n */\n var removeSpinner = function(groupid) {\n var divgroup = $('.block_groups');\n divgroup.find('.spinner' + groupid).remove();\n divgroup.find('.imggroup-' + groupid).show();\n };\n /**\n * Removes all spinners.\n */\n var removeSpinners = function() {\n var divgroups = $('.block_groups');\n divgroups.find('.spinner-all').remove();\n divgroups.find('.imggroup').show();\n };\n /**\n * Reloads the current page.\n */\n var reloadPage = function() {\n location.reload(true);\n };\n /**\n * Creates a warning message.\n */\n var createWarningMessage = function() {\n\n str.get_strings([\n {'key': 'errortitle', component: 'block_groups'},\n {'key': 'nochangeindatabasepossiblereload', component: 'block_groups'},\n {'key': 'yes'},\n {'key': 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], reloadPage);\n }).fail(notification.exception);\n };\n /**\n * Initialises Spinner for a single group.\n * @param {Number} groupid\n */\n var addSpinner = function(groupid) {\n var divgroups = $('.block_groups');\n if (divgroups.find('.warning' + groupid).length > 0) {\n\n removeWarning(groupid);\n }\n var imgurl = url.imageUrl(\"i/loading_small\", 'moodle');\n var spinner = document.createElement(\"img\");\n spinner.className = 'spinner' + groupid + ' spinner block-groups-spinner';\n spinner.src = imgurl;\n spinner.hidden = false;\n\n divgroups.find('.imggroup-' + groupid).before(spinner);\n divgroups.find('.imggroup-' + groupid).hide();\n };\n /**\n * Adds spinners for both visibility icons of one grouping.\n *\n * @param {Number} groupingid\n */\n var addGroupingSpinners = function(groupingid) {\n var divgroups = $('.block_groups');\n var imgurl = url.imageUrl(\"i/loading_small\", 'moodle');\n\n divgroups.find('.imggroup-grouping-' + groupingid).each(function() {\n var spinner = document.createElement(\"img\");\n spinner.className = 'spinner-grouping-' + groupingid + ' spinner block-groups-spinner';\n spinner.src = imgurl;\n spinner.hidden = false;\n $(this).before(spinner);\n $(this).hide();\n });\n };\n /**\n * Removes spinners for both visibility icons of one grouping.\n *\n * @param {Number} groupingid\n */\n var removeGroupingSpinners = function(groupingid) {\n var divgroups = $('.block_groups');\n\n divgroups.find('.spinner-grouping-' + groupingid).remove();\n divgroups.find('.imggroup-grouping-' + groupingid).show();\n };\n /**\n * Adds a warning(triangle with exclamation mark) in case the response is empty or the response throws an error.\n * @param {(Number|String)} identifier\n * @returns {Boolean} false\n */\n var addWarning = function(identifier) {\n var divgroups = $('.block_groups');\n var warningexist = divgroups.find('.warning' + identifier);\n if (warningexist.length) {\n removeSpinner(identifier);\n return false;\n } else {\n if (identifier === 'all') {\n removeSpinners();\n } else {\n removeSpinner(identifier);\n }\n var imgurl = url.imageUrl(\"i/warning\", 'moodle');\n var warning = document.createElement(\"img\");\n warning.className = 'warning' + identifier + ' block-groups-warning';\n warning.src = imgurl;\n createWarningMessage();\n (divgroups.find('.imggroup-' + identifier).before(warning));\n divgroups.find('.imggroup-' + identifier).on('click', createWarningMessage);\n divgroups.find('.warning' + identifier).on('click', createWarningMessage);\n }\n return false;\n };\n /**\n * Method that calls for an ajax script and replaces and/or changes the output components for a single group.\n * @param {*} event\n * @return {Boolean}\n */\n var changevisibility = function(event) {\n var divgroups = $('.block_groups');\n\n var groupid = $(this).data('groupid');\n if (divgroups.find('.spinner' + $(this).data('groupid')).length > 0 ||\n divgroups.find('.spinner-all').length > 0) {\n return false;\n }\n addSpinner($(this).data('groupid'));\n var promises = ajax.call([\n\n {\n methodname: 'block_groups_create_output', args: {\n groups: {\n id: $(this).data('groupid'),\n courseid: event.data.courseid\n }\n\n }\n }\n ]);\n $(document).ajaxError(function() {\n addWarning(groupid);\n return false;\n });\n\n promises[0].then(function(response) {\n if (response === null || response.error === true) {\n addWarning(groupid);\n return false;\n }\n divgroups.find('.group-' + response.id).replaceWith(response.newelement);\n // Replaces the used element, therefore removes the spinner.\n if (response.visibility === 0) {\n divgroups.find('.membergroup-' + response.id).removeClass('hiddengroups');\n }\n if (response.visibility === 1) {\n divgroups.find('.membergroup-' + response.id).addClass('hiddengroups');\n }\n divgroups.find('.group-' + response.id + ' .block_groups_toggle').on('click',\n {courseid: event.data.courseid}, changevisibility);\n removeSpinner(response.id);\n return false;\n }).fail(function() {\n addWarning(groupid);\n return false;\n });\n return false;\n };\n var checkMember = function(response) {\n var membergroup = $('.block-groups-membergroup');\n var visibility = response.visibility;\n if (visibility === 2) {\n membergroup.removeClass('hiddengroups');\n }\n if (visibility === 1) {\n membergroup.addClass('hiddengroups');\n }\n };\n /**\n * Initialises spinners for all groups.\n */\n var addSpinners = function() {\n var divgroups = $('.block_groups');\n if (divgroups.find('.warningall').length > 0) {\n removeWarning('all');\n }\n var imgurl = url.imageUrl(\"i/loading_small\", 'moodle');\n var spinner = document.createElement(\"img\");\n spinner.className = 'spinner-all spinner block-groups-spinner';\n spinner.src = imgurl;\n spinner.hidden = false;\n divgroups.find('.imggroup').before(spinner);\n divgroups.find('.imggroup').hide();\n };\n\n var addNotification = function(type, text) {\n notification.addNotification({\n message: text,\n type: type\n });\n };\n /**\n * Method that calls for an ajax script and replaces and/or changes the output components for all groups.\n * @param {Number} event\n * @return {Boolean}\n */\n var changeVisibilityAll = function(event) {\n if ($('.block_groups').find('.spinner').length > 0) {\n return false;\n }\n var warningexist = $('.block_groups').find('.block-groups-warning');\n if (typeof warningexist !== 'undefined' && warningexist.length > 0) {\n addWarning('all');\n return false;\n }\n addSpinners();\n // Calls for the externallib.\n var promises = ajax.call([\n {\n methodname: 'block_groups_create_allgroups_output', args: {\n groups: {\n action: $(this).data('action'),\n courseid: event.data.courseid\n }\n }\n }\n ]);\n $(document).ajaxError(function() {\n addWarning('all');\n return false;\n });\n // Response is processed.\n promises[0].then(function(response) {\n var divgroups = $('.block_groups');\n // Catch misleading responses.\n if (response === null || response.error === true) {\n addWarning('all');\n return false;\n }\n if (response.visibility === 0) {\n // Is nearly impossible since group has to be deleted during the request, however it is\n // possible, therefore a special message is thrown.\n divgroups.find('.content').replaceWith(response.newelement);\n str.get_strings([\n {key: 'nogroups', component: 'block_groups'}\n ]).done(function(s) {\n addNotification('error', s[0]);\n }).fail(notification.exception);\n return false;\n }\n\n // Old Elements are replaced and on click event added.\n divgroups.find('.wrapperlistgroup').replaceWith(response.newelement);\n divgroups.find('.block_groups_toggle').on('click', {courseid: event.data.courseid}, changevisibility);\n checkMember(response);\n // Outputvisibility 0->nogroups 1 -> hidden 2->visible 3-> all are hidden 4-> all are visible.\n str.get_strings([\n {key: 'groupschangedhidden', component: 'block_groups'},\n {key: 'groupschangedvisible', component: 'block_groups'},\n {key: 'allgroupsinstatehidden', component: 'block_groups'},\n {key: 'allgroupsinstatevisible', component: 'block_groups'}\n ]).done(function(s) {\n switch (response.visibility) {\n case 1:\n addNotification('success', s[0]);\n break;\n case 2:\n addNotification('success', s[1]);\n break;\n case 3:\n addNotification('warning', s[2]);\n break;\n case 4:\n addNotification('warning', s[3]);\n break;\n default:\n break;\n }\n }).fail(notification.exception);\n return false;\n }).fail(function() {\n addWarning('all');\n return false;\n });\n removeSpinners();\n return false;\n };\n\n /**\n * Method that calls for an ajax script and changes the visibility of all groups in one grouping.\n *\n * @param {*} event\n * @return {Boolean}\n */\n var changeVisibilityGrouping = function(event) {\n var divgroups = $('.block_groups');\n var groupingid = $(this).data('groupingid');\n var action = $(this).data('action');\n\n if (divgroups.find('.spinner').length > 0) {\n return false;\n }\n\n var warningexist = divgroups.find('.block-groups-warning');\n if (typeof warningexist !== 'undefined' && warningexist.length > 0) {\n addWarning(groupingid);\n return false;\n }\n\n addGroupingSpinners(groupingid);\n\n var promises = ajax.call([\n {\n methodname: \"block_groups_create_grouping_output\",\n args: {\n grouping: {\n groupingid: groupingid,\n courseid:event.data.courseid,\n action: action\n }\n }\n }\n ]);\n\n $(document).ajaxError(function() {\n addWarning(groupingid);\n return false;\n });\n\n promises[0].then(function(response) {\n if (response === null || response.error === true) {\n addWarning(groupingid);\n return false;\n }\n\n divgroups.find('.wrapperlistgroup').replaceWith(response.newelement);\n divgroups.find('.block_groups_toggle').on(\n 'click',\n {courseid: event.data.courseid}, changevisibility);\n divgroups.find('.block_groups_grouping_toggle').on(\n 'click',\n {courseid: event.data.courseid},\n changeVisibilityGrouping\n );\n checkGroupingMembers(response);\n\n str.get_strings([\n {key: 'groupingchangedhidden', component: 'block_groups'},\n {key: 'groupingchangedvisible', component: 'block_groups'},\n {key: 'groupinginstatehidden', component: 'block_groups'},\n {key: 'groupinginstatevisible', component: 'block_groups'},\n {key: 'nogroups', component: 'block_groups'}\n ]).done(function(s) {\n switch (response.visibility) {\n case 0:\n addNotification('warning', s[4]);\n break;\n case 1:\n addNotification('success', s[0]);\n break;\n case 2:\n addNotification('success', s[1]);\n break;\n case 3:\n addNotification('warning', s[2]);\n break;\n case 4:\n addNotification('warning', s[3]);\n break;\n default:\n break;\n }\n }).fail(notification.exception);\n\n removeGroupingSpinners(groupingid);\n return false;\n }).fail(function() {\n addWarning(groupingid);\n return false;\n });\n return false;\n };\n\n /**\n * Updates member group visibility only for groups changed by grouping action.\n *\n * @param {Object} response\n */\n var checkGroupingMembers = function(response) {\n response.changedgroups.forEach(function(group) {\n var membergroup = $('.membergroup-' + group.groupid);\n\n if (response.visibility === 2) {\n membergroup.removeClass('hiddengroups');\n }\n\n if (response.visibility === 1) {\n membergroup.addClass('hiddengroups');\n }\n });\n };\n\n /**\n * Calls for the main method. Either single groups are changed with block_groups_toggle or all groups with\n * block_groups_all_toggle.\n * @param {Number} courseid\n */\n return {\n initialise: function(courseid) {\n $('.block_groups_toggle').on('click', {courseid: courseid}, changevisibility);\n $('.block_groups_all_toggle').on('click', {courseid: courseid}, changeVisibilityAll);\n $('.block_groups_grouping_toggle').on('click', {courseid: courseid}, changeVisibilityGrouping);\n }\n };\n});\n"],"names":["define","$","ajax","url","notification","str","removeWarning","identifier","find","remove","removeSpinner","groupid","divgroup","show","removeSpinners","divgroups","reloadPage","location","reload","createWarningMessage","get_strings","component","done","s","confirm","fail","exception","addWarning","length","imgurl","imageUrl","warning","document","createElement","className","src","before","on","changevisibility","event","this","data","spinner","hidden","hide","addSpinner","promises","call","methodname","args","groups","id","courseid","ajaxError","then","response","error","replaceWith","newelement","visibility","removeClass","addClass","addNotification","type","text","message","changeVisibilityAll","warningexist","addSpinners","action","key","membergroup","checkMember","changeVisibilityGrouping","groupingid","each","addGroupingSpinners","grouping","checkGroupingMembers","removeGroupingSpinners","changedgroups","forEach","group","initialise"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/src/blocks_groups_visibility.js b/amd/src/blocks_groups_visibility.js
index 420bc2e..42ee67b 100644
--- a/amd/src/blocks_groups_visibility.js
+++ b/amd/src/blocks_groups_visibility.js
@@ -90,6 +90,35 @@ define(['jquery', 'core/ajax', 'core/url', 'core/notification', 'core/str'], fun
divgroups.find('.imggroup-' + groupid).before(spinner);
divgroups.find('.imggroup-' + groupid).hide();
};
+ /**
+ * Adds spinners for both visibility icons of one grouping.
+ *
+ * @param {Number} groupingid
+ */
+ var addGroupingSpinners = function(groupingid) {
+ var divgroups = $('.block_groups');
+ var imgurl = url.imageUrl("i/loading_small", 'moodle');
+
+ divgroups.find('.imggroup-grouping-' + groupingid).each(function() {
+ var spinner = document.createElement("img");
+ spinner.className = 'spinner-grouping-' + groupingid + ' spinner block-groups-spinner';
+ spinner.src = imgurl;
+ spinner.hidden = false;
+ $(this).before(spinner);
+ $(this).hide();
+ });
+ };
+ /**
+ * Removes spinners for both visibility icons of one grouping.
+ *
+ * @param {Number} groupingid
+ */
+ var removeGroupingSpinners = function(groupingid) {
+ var divgroups = $('.block_groups');
+
+ divgroups.find('.spinner-grouping-' + groupingid).remove();
+ divgroups.find('.imggroup-grouping-' + groupingid).show();
+ };
/**
* Adds a warning(triangle with exclamation mark) in case the response is empty or the response throws an error.
* @param {(Number|String)} identifier
@@ -292,6 +321,120 @@ define(['jquery', 'core/ajax', 'core/url', 'core/notification', 'core/str'], fun
return false;
};
+ /**
+ * Method that calls for an ajax script and changes the visibility of all groups in one grouping.
+ *
+ * @param {*} event
+ * @return {Boolean}
+ */
+ var changeVisibilityGrouping = function(event) {
+ var divgroups = $('.block_groups');
+ var groupingid = $(this).data('groupingid');
+ var action = $(this).data('action');
+
+ if (divgroups.find('.spinner').length > 0) {
+ return false;
+ }
+
+ var warningexist = divgroups.find('.block-groups-warning');
+ if (typeof warningexist !== 'undefined' && warningexist.length > 0) {
+ addWarning(groupingid);
+ return false;
+ }
+
+ addGroupingSpinners(groupingid);
+
+ var promises = ajax.call([
+ {
+ methodname: "block_groups_create_grouping_output",
+ args: {
+ grouping: {
+ groupingid: groupingid,
+ courseid:event.data.courseid,
+ action: action
+ }
+ }
+ }
+ ]);
+
+ $(document).ajaxError(function() {
+ addWarning(groupingid);
+ return false;
+ });
+
+ promises[0].then(function(response) {
+ if (response === null || response.error === true) {
+ addWarning(groupingid);
+ return false;
+ }
+
+ divgroups.find('.wrapperlistgroup').replaceWith(response.newelement);
+ divgroups.find('.block_groups_toggle').on(
+ 'click',
+ {courseid: event.data.courseid}, changevisibility);
+ divgroups.find('.block_groups_grouping_toggle').on(
+ 'click',
+ {courseid: event.data.courseid},
+ changeVisibilityGrouping
+ );
+ checkGroupingMembers(response);
+
+ str.get_strings([
+ {key: 'groupingchangedhidden', component: 'block_groups'},
+ {key: 'groupingchangedvisible', component: 'block_groups'},
+ {key: 'groupinginstatehidden', component: 'block_groups'},
+ {key: 'groupinginstatevisible', component: 'block_groups'},
+ {key: 'nogroups', component: 'block_groups'}
+ ]).done(function(s) {
+ switch (response.visibility) {
+ case 0:
+ addNotification('warning', s[4]);
+ break;
+ case 1:
+ addNotification('success', s[0]);
+ break;
+ case 2:
+ addNotification('success', s[1]);
+ break;
+ case 3:
+ addNotification('warning', s[2]);
+ break;
+ case 4:
+ addNotification('warning', s[3]);
+ break;
+ default:
+ break;
+ }
+ }).fail(notification.exception);
+
+ removeGroupingSpinners(groupingid);
+ return false;
+ }).fail(function() {
+ addWarning(groupingid);
+ return false;
+ });
+ return false;
+ };
+
+ /**
+ * Updates member group visibility only for groups changed by grouping action.
+ *
+ * @param {Object} response
+ */
+ var checkGroupingMembers = function(response) {
+ response.changedgroups.forEach(function(group) {
+ var membergroup = $('.membergroup-' + group.groupid);
+
+ if (response.visibility === 2) {
+ membergroup.removeClass('hiddengroups');
+ }
+
+ if (response.visibility === 1) {
+ membergroup.addClass('hiddengroups');
+ }
+ });
+ };
+
/**
* Calls for the main method. Either single groups are changed with block_groups_toggle or all groups with
* block_groups_all_toggle.
@@ -301,7 +444,7 @@ define(['jquery', 'core/ajax', 'core/url', 'core/notification', 'core/str'], fun
initialise: function(courseid) {
$('.block_groups_toggle').on('click', {courseid: courseid}, changevisibility);
$('.block_groups_all_toggle').on('click', {courseid: courseid}, changeVisibilityAll);
-
+ $('.block_groups_grouping_toggle').on('click', {courseid: courseid}, changeVisibilityGrouping);
}
};
});
diff --git a/block_groups.php b/block_groups.php
index e59289f..f09b046 100644
--- a/block_groups.php
+++ b/block_groups.php
@@ -181,8 +181,13 @@ public function build_grouping_array($allgroupings, $courseid) {
foreach ($allgroupings as $g => $value) {
if (is_object($value) && property_exists($value, 'name')) {
// Necessary DB query to prohibit multiple ids of grouping members.
- $countgroupingmember = $arrayofmembers[$value->id]->number;
- $groupingsarray[$g] = $renderer->get_grouping($value->name, $countgroupingmember);
+ $countgroupingmember = 0;
+
+ if (isset($arrayofmembers[$value->id])) {
+ $countgroupingmember = $arrayofmembers[$value->id]->number;
+ }
+
+ $groupingsarray[$g] = $renderer->get_grouping($value, $countgroupingmember);
}
}
return $groupingsarray;
diff --git a/classes/external/create_grouping_output.php b/classes/external/create_grouping_output.php
new file mode 100644
index 0000000..79714ea
--- /dev/null
+++ b/classes/external/create_grouping_output.php
@@ -0,0 +1,229 @@
+.
+
+namespace block_groups\external;
+
+use context_course;
+use core_external\external_api;
+use core_external\external_function_parameters;
+use core_external\external_multiple_structure;
+use core_external\external_single_structure;
+use core_external\external_value;
+use html_writer;
+use moodle_url;
+
+/**
+ * create_grouping_output external function
+ *
+ * @package block_groups
+ * @copyright 2026 Lena Herfeldt
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class create_grouping_output extends external_api {
+ /**
+ * Specifies the input parameters.
+ * @return external_function_parameters
+ */
+ public static function execute_parameters() {
+ return new external_function_parameters(
+ [
+ 'grouping' => new external_single_structure(
+ [
+ 'groupingid' => new external_value(PARAM_INT, 'id of grouping'),
+ 'courseid' => new external_value(PARAM_INT, 'id of course'),
+ 'action' => new external_value(PARAM_ALPHA, 'show or hide'),
+ ]
+ ),
+ ]
+ );
+ }
+
+ /**
+ * Specifies the output parameters.
+ * @return external_single_structure
+ */
+ public static function execute_returns() {
+ return new external_single_structure(
+ [
+ 'courseid' => new external_value(PARAM_INT, 'id of course'),
+ 'groupingid' => new external_value(PARAM_INT, 'id of grouping'),
+ 'newelement' => new external_value(PARAM_RAW, 'replace html-element'),
+ 'visibility' => new external_value(PARAM_INT, 'returns the visibility value'),
+ 'changedgroups' => new external_multiple_structure(
+ new external_single_structure(
+ [
+ 'groupid' => new external_value(PARAM_INT, 'group-id'),
+ ]
+ )
+ ),
+ ]
+ );
+ }
+
+ /**
+ * Changed the Database and returns the updated html content.
+ * create_grouping_output
+ * @param array $grouping
+ * @return array
+ */
+ public static function execute($grouping) {
+ global $CFG, $PAGE, $DB;
+ $params = self::validate_parameters(self::execute_parameters(), ['grouping' => $grouping]);
+ $courseid = $params['grouping']['courseid'];
+ $groupingid = $params['grouping']['groupingid'];
+ $action = $params['grouping']['action'];
+ $context = context_course::instance($courseid);
+
+ $PAGE->set_context($context);
+ self::validate_context($context);
+ require_capability('moodle/course:managegroups', $context);
+
+ require_once($CFG->dirroot . '/blocks/groups/locallib.php');
+ require_once($CFG->dirroot . '/group/lib.php');
+
+ $groupingexists = $DB->get_record('groupings', [
+ 'id' => $groupingid,
+ 'courseid' => $courseid,
+ ]);
+
+ if (!$groupingexists) {
+ throw new \invalid_parameter_exception('Grouping does not exist or does not belong to course');
+ }
+
+ $groupids = self::get_group_ids_for_grouping($groupingid, $courseid);
+
+ if (empty($groupids)) {
+ return [
+ 'courseid' => $courseid,
+ 'groupingid' => $groupingid,
+ 'newelement' => self::generate_groups_html($courseid),
+ 'visibility' => 0,
+ 'changedgroups' => [],
+ ];
+ }
+
+ $changedgroups = self::update_group_visibility($groupids, $courseid, $action);
+ $statuscode = self::get_status_code($action, $changedgroups);
+
+ return [
+ 'courseid' => $courseid,
+ 'groupingid' => $groupingid,
+ 'newelement' => self::generate_groups_html($courseid),
+ 'visibility' => $statuscode,
+ 'changedgroups' => $changedgroups,
+ ];
+ }
+
+ /**
+ * Returns all group ids belonging to a grouping.
+ *
+ * @param int $groupingid
+ * @param int $courseid
+ * @return array
+ */
+ private static function get_group_ids_for_grouping($groupingid, $courseid) {
+ global $DB;
+
+ $sql = "SELECT g.id
+ FROM {groups} g
+ JOIN {groupings_groups} gg ON gg.groupid = g.id
+ WHERE gg.groupingid = :groupingid AND g.courseid = :courseid
+ ORDER BY g.id ASC";
+
+ $records = $DB->get_records_sql($sql, [
+ 'groupingid' => $groupingid,
+ 'courseid' => $courseid,
+ ]);
+
+ return array_keys($records);
+ }
+
+ /**
+ * Changes the visibility of the groups in the database.
+ * @param array $groupids
+ * @param int $courseid
+ * @param string $action
+ * @return array
+ */
+ private static function update_group_visibility($groupids, $courseid, $action) {
+ global $DB;
+
+ $changedgroups = [];
+ $targetvisible = ($action === 'show');
+
+ foreach ($groupids as $groupid) {
+ $currentlyvisible = $DB->record_exists('block_groups_hide', ['id' => $groupid]);
+
+ if ($targetvisible === $currentlyvisible) {
+ continue;
+ }
+
+ block_groups_set_visibility($groupid, $courseid, $targetvisible);
+ $changedgroups[] = ['groupid' => $groupid];
+ }
+ return $changedgroups;
+ }
+
+ /**
+ * Generates the complete updated group list html.
+ *
+ * @param int $courseid
+ * @return string
+ */
+ private static function generate_groups_html($courseid) {
+ global $PAGE, $DB;
+
+ $renderer = $PAGE->get_renderer('block_groups');
+ $groups = $DB->get_records('groups', ['courseid' => $courseid], 'id ASC');
+ $groupsarray = [];
+
+ foreach ($groups as $group) {
+ $fullgroup = groups_get_group($group->id);
+ $href = new moodle_url(
+ '/blocks/groups/changevisibility.php',
+ [
+ 'courseid' => $courseid,
+ 'groupid' => $group->id,
+ ]
+ );
+ $countmembers = count(groups_get_members($group->id));
+ $groupvisible = $DB->record_exists('block_groups_hide', ['id' => $group->id]);
+ $hiddenforrenderer = !$groupvisible;
+ $groupsarray[] = $renderer->get_string_group(
+ $fullgroup,
+ $href,
+ $countmembers,
+ $hiddenforrenderer,
+ );
+ }
+ return html_writer::alist($groupsarray, ['class' => 'wrapperlistgroup']);
+ }
+
+ /**
+ * Returns status code depending on action and whether anything changed.
+ *
+ * @param string $action
+ * @param array $changedgroups
+ * @return int
+ */
+ private static function get_status_code($action, $changedgroups) {
+ if (empty($changedgroups)) {
+ return ($action === 'show') ? 4 : 3;
+ }
+
+ return ($action === 'hide') ? 1 : 2;
+ }
+}
diff --git a/classes/output/renderer.php b/classes/output/renderer.php
index de2a007..11563d2 100644
--- a/classes/output/renderer.php
+++ b/classes/output/renderer.php
@@ -92,12 +92,20 @@ public function get_string_group($value, $href, $countmembers, $visibility) {
}
/**
* Generates string for a grouping list item
- * @param string $name
+ * @param stdClass $grouping
* @param integer $counter
* @return string html-string
*/
- public function get_grouping($name, $counter) {
- return $name . ' ' . get_string('brackets', 'block_groups', $counter);
+ public function get_grouping($grouping, $counter) {
+ $line = html_writer::span(
+ $grouping->name . ' ' . get_string('brackets', 'block_groups', $counter),
+ 'wrapperblockgroupsgrouping'
+ );
+
+ $showlink = $this->create_grouping_link('show', $grouping->id, false);
+ $hidelink = $this->create_grouping_link('hide', $grouping->id, false);
+
+ return html_writer::span($line . $showlink . $hidelink, 'grouping-' . $grouping->id);
}
/**
* Renders line to change all groups.
@@ -166,4 +174,46 @@ private function create_all_groups_link($action, $reverseimage = true) {
$rightaligndiv = html_writer::div($icon, 'rightalign');
return html_writer::link($urlhide, $rightaligndiv, ['data-action' => $reverse, 'class' => 'block_groups_all_toggle']);
}
+
+ /**
+ * Creates a link to change visibility of all groups in one grouping.
+ *
+ * @param string $action show/hide
+ * @param int $groupingid grouping id
+ * @param bool $reverseimage whether to reverse the icon.
+ * @return string html-link
+ */
+ private function create_grouping_link($action, $groupingid, $reverseimage = true) {
+ global $CFG, $COURSE;
+ if ($action === 'hide') {
+ $actionnumber = 0;
+ $reverse = 'show';
+ } else {
+ $actionnumber = 1;
+ $reverse = 'hide';
+ }
+
+ $url = new moodle_url(
+ $CFG->wwwroot . '/blocks/groups/changegrouping.php',
+ ['courseid' => $COURSE->id, 'groupingid' => $groupingid, 'hide' => $actionnumber]
+ );
+
+ $icon = $this->output->pix_icon(
+ 't/' . ($reverseimage ? $reverse : $action),
+ get_string($reverse . 'grouping', 'block_groups'),
+ 'moodle',
+ ['class' => 'imggroup-grouping-' . $groupingid . ' imggroup']
+ );
+ $rightaligndiv = html_writer::div($icon, 'rightalign');
+
+ return html_writer::link(
+ $url,
+ $rightaligndiv,
+ [
+ 'data-groupingid' => $groupingid,
+ 'data-action' => $reverse,
+ 'class' => 'block_groups_grouping_toggle',
+ ]
+ );
+ }
}
diff --git a/db/services.php b/db/services.php
index 81e87e1..cd9603b 100644
--- a/db/services.php
+++ b/db/services.php
@@ -38,4 +38,11 @@
'capabilities' => 'moodle/course:managegroups',
'ajax' => true,
],
+ 'block_groups_create_grouping_output' => [
+ 'classname' => 'block_groups\external\create_grouping_output',
+ 'description' => 'Service to allow changing the visibility of groupings',
+ 'type' => 'write',
+ 'capabilities' => 'moodle/course:managegroups',
+ 'ajax' => true,
+ ],
];
diff --git a/lang/en/block_groups.php b/lang/en/block_groups.php
index 3f50174..8339522 100644
--- a/lang/en/block_groups.php
+++ b/lang/en/block_groups.php
@@ -31,11 +31,16 @@
$string['errortitle'] = 'Synchronization error';
$string['group'] = 'Groups ';
$string['grouping'] = 'Groupings ';
+$string['groupingchangedhidden'] = 'All groups in this grouping are hidden.';
+$string['groupingchangedvisible'] = 'All groups in this grouping are visible.';
+$string['groupinginstatehidden'] = 'The grouping is already hidden.';
+$string['groupinginstatevisible'] = 'The grouping is already visible.';
$string['groups:addinstance'] = 'Add a new Group block';
$string['groups:myaddinstance'] = 'Add a new Group block to Dashboard';
$string['groupschangedhidden'] = 'All groups are hidden.';
$string['groupschangedvisible'] = 'All groups are visible.';
$string['hidegroup'] = 'hide group';
+$string['hidegrouping'] = 'hide grouping';
$string['introduction'] = 'You are enrolled in the following groups';
$string['modify'] = 'modify groups';
$string['nochangeindatabasepossible'] = 'The required change was not possible. Please reload the page.';
@@ -46,4 +51,5 @@
$string['pluginname2'] = 'Groups';
$string['privacy:metadata'] = 'The Group block only displays information about groups, but does not effect or store any personal data. The existing groups and groupings data are stored in other locations.';
$string['showgroup'] = 'show group';
+$string['showgrouping'] = 'show grouping';
$string['showgroupsdefaultsetting'] = 'Show groups and groupings by default';
diff --git a/locallib.php b/locallib.php
index 63dee37..57c9eb4 100644
--- a/locallib.php
+++ b/locallib.php
@@ -60,3 +60,41 @@ function count_grouping_members($courseid) {
GROUP BY g.id, gg.groupingid
ORDER BY gg.groupingid DESC');
}
+
+/**
+ * Sets the visibility of a group.
+ * @param int $groupid
+ * @param int $courseid
+ * @param bool $visible if target visibility is visible or not
+ * @return bool true if something changed, false otherwise
+ */
+function block_groups_set_visibility($groupid, $courseid, $visible) {
+ global $DB;
+
+ $transaction = $DB->start_delegated_transaction();
+ $groupsuitable = $DB->get_record('groups', ['id' => $groupid, 'courseid' => $courseid]);
+
+ if (empty($groupsuitable)) {
+ $transaction->allow_commit();
+ return false;
+ }
+
+ $currentlyvisible = $DB->record_exists('block_groups_hide', ['id' => $groupid]);
+
+ // The group is currently not visible and should be visible now.
+ if ($visible && !$currentlyvisible) {
+ $DB->insert_record_raw('block_groups_hide', ['id' => $groupid], true, false, true);
+ $transaction->allow_commit();
+ return true;
+ }
+
+ // The group is currently visible and should be not visible now.
+ if (!$visible && $currentlyvisible) {
+ $DB->delete_records('block_groups_hide', ['id' => $groupid]);
+ $transaction->allow_commit();
+ return true;
+ }
+
+ $transaction->allow_commit();
+ return false;
+}
diff --git a/tests/behat/behat_block_groups.php b/tests/behat/behat_block_groups.php
index 754c19c..c40ace0 100644
--- a/tests/behat/behat_block_groups.php
+++ b/tests/behat/behat_block_groups.php
@@ -78,7 +78,7 @@ public function i_click_on_the_eye_icon_of_group_name_without_javascript_enabled
*
* @When /^I click in the groups block on all groups "(?P(?:[^"]|\\")*)"$/
*
- * @param string $action identifier of the Group
+ * @param string $action identifier of the action show/hide
*/
public function i_click_in_the_groups_block_on_all_groups($action) {
if ($action == 'hide') {
@@ -96,6 +96,37 @@ public function i_click_in_the_groups_block_on_all_groups($action) {
$eyeicon->click();
}
+ /**
+ * Clicks on all groups
+ *
+ * @When /^I click in the groups block on the grouping "(?P(?:[^"]|\\")*)" "(?P(?:[^"]|\\")*)"$/
+ *
+ * @param string $groupingname identifier of the grouping
+ * @param string $action identifier of the action show/hide
+ */
+ public function i_click_in_the_groups_block_on_the_grouping($groupingname, $action) {
+ $checkbox = $this->find('css', '#checkboxgrouping');
+ if (!$checkbox->isChecked()) {
+ $label = $this->find('css', 'label[for="checkboxgrouping"]');
+ $label->click();
+ }
+
+ if ($action == 'hide') {
+ $eyeicon = $this->find(
+ 'xpath',
+ "//span[contains(@class, 'grouping-')][contains(., " . behat_context_helper::escape($groupingname) . ")]//a[1]"
+ );
+ } else if ($action == 'show') {
+ $eyeicon = $this->find(
+ 'xpath',
+ "//span[contains(@class, 'grouping-')][contains(., " . behat_context_helper::escape($groupingname) . ")]//a[2]"
+ );
+ } else {
+ throw new \Exception('Unknwon action: ' . $action);
+ }
+ $eyeicon->click();
+ }
+
/**
* Sets the group default visibility setting to a specific value (0 or 1).
* This step allows tests to configure whether groups are visible or hidden by default.
diff --git a/tests/behat/change_grouping.feature b/tests/behat/change_grouping.feature
new file mode 100644
index 0000000..e5572bd
--- /dev/null
+++ b/tests/behat/change_grouping.feature
@@ -0,0 +1,126 @@
+@block @block_groups @groups_change_grouping
+Feature: Feature: Change group visibility by grouping in the groups block
+ In order to change the visibility of groups in a grouping with one click
+ As a course administrator
+ I need to change the visibility of all groups in a grouping
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "users" exist:
+ | username | firstname | lastname | email | idnumber |
+ | teacher1 | Teacher | 1 | teacher1@example.com | T1 |
+ | student1 | Student | 1 | student1@example.com | S1 |
+ | student2 | Student | 2 | student2@example.com | S2 |
+ | student3 | Student | 3 | student3@example.com | S3 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ | student3 | C1 | student |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ | Group 3 | C1 | G3 |
+ | Group 4 | C1 | G4 |
+ And the following "groupings" exist:
+ | name | course | idnumber |
+ | Grouping 1 | C1 | GG1 |
+ | Grouping 2 | C1 | GG2 |
+ And the following "group members" exist:
+ | user | group |
+ | student1 | G1 |
+ | student2 | G1 |
+ | student2 | G2 |
+ | student3 | G3 |
+ | student3 | G4 |
+ And the following "grouping groups" exist:
+ | grouping | group |
+ | GG1 | G1 |
+ | GG1 | G2 |
+ | GG2 | G2 |
+ | GG2 | G4 |
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage with editing mode on
+ And I add the "Groups and Groupings" block
+ And I log out
+
+ @javascript
+ Scenario: Showing a grouping shows all groups in that grouping
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ When I click in the groups block on the grouping "Grouping 1" "show"
+ Then I wait until the page is ready
+ Then "Groups and Groupings" "block" should exist
+ And I should see "All groups in this grouping are visible." in the "region-main" "region"
+ And I log out
+
+ When I log in as "student1"
+ And I am on "Course 1" course homepage
+ Then I should see "Group 1" in the "Groups" "block"
+ And I log out
+
+ When I log in as "student2"
+ And I am on "Course 1" course homepage
+ Then I should see "Group 1" in the "Groups" "block"
+ And I should see "Group 2" in the "Groups" "block"
+ And I log out
+
+ When I log in as "student3"
+ And I am on "Course 1" course homepage
+ Then "Groups" "block" should not exist
+ And I log out
+
+ @javascript
+ Scenario: Showing overlapping groupings shows shared and grouping-specific groups
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ When I click in the groups block on the grouping "Grouping 1" "show"
+ And I click in the groups block on the grouping "Grouping 2" "show"
+ Then I wait until the page is ready
+ Then "Groups and Groupings" "block" should exist
+ And I should see "All groups in this grouping are visible." in the "region-main" "region"
+ And I log out
+
+ When I log in as "student1"
+ And I am on "Course 1" course homepage
+ Then I should see "Group 1" in the "Groups" "block"
+ And I log out
+
+ When I log in as "student2"
+ And I am on "Course 1" course homepage
+ Then I should see "Group 1" in the "Groups" "block"
+ And I should see "Group 2" in the "Groups" "block"
+ And I log out
+
+ When I log in as "student3"
+ And I am on "Course 1" course homepage
+ Then I should see "Group 4" in the "Groups" "block"
+ And I should not see "Group 3" in the "Groups" "block"
+ And I log out
+
+ @javascript
+ Scenario: Hiding a grouping hides all groups in that grouping
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ When I click in the groups block on the grouping "Grouping 1" "show"
+ And I click in the groups block on the grouping "Grouping 2" "show"
+ And I click in the groups block on the grouping "Grouping 1" "hide"
+ Then I wait until the page is ready
+ Then "Groups and Groupings" "block" should exist
+ And I should see "All groups in this grouping are hidden." in the "region-main" "region"
+ And I log out
+
+ When I log in as "student2"
+ And I am on "Course 1" course homepage
+ Then "Groups" "block" should not exist
+ And I log out
+
+ When I log in as "student3"
+ And I am on "Course 1" course homepage
+ Then I should see "Group 4" in the "Groups" "block"
+ And I should not see "Group 3" in the "Groups" "block"
+ And I log out
diff --git a/tests/block_groups_test.php b/tests/block_groups_test.php
index 5392747..5ba31f1 100644
--- a/tests/block_groups_test.php
+++ b/tests/block_groups_test.php
@@ -73,6 +73,36 @@ public function test_locallib(): void {
// Test empty grouping.
$this->assertEquals(0, $functioncount[$data['grouping3']->id]->number);
}
+ /**
+ * Function to test setting the grouping visibility.
+ * @covers \block_groups_set_visibility
+ */
+ public function test_block_groups_set_visibility(): void {
+ global $DB, $CFG;
+ require_once($CFG->dirroot . '/blocks/groups/locallib.php');
+ $data = $this->set_up();
+ $groupid = $data['group1']->id;
+ $courseid = $data['course2']->id;
+
+ // At first, the groups are all hidden.
+ $this->assertFalse($DB->record_exists('block_groups_hide', ['id' => $groupid]));
+
+ // Setting the group to visible should add a record to the database and report a change.
+ $this->assertTrue(block_groups_set_visibility($groupid, $courseid, true));
+ $this->assertTrue($DB->record_exists('block_groups_hide', ['id' => $groupid]));
+
+ // Setting the group to visible again should not change anything.
+ $this->assertFalse(block_groups_set_visibility($groupid, $courseid, true));
+ $this->assertTrue($DB->record_exists('block_groups_hide', ['id' => $groupid]));
+
+ // Setting the group to hidden should remove the record from the database and report a change.
+ $this->assertTrue(block_groups_set_visibility($groupid, $courseid, false));
+ $this->assertFalse($DB->record_exists('block_groups_hide', ['id' => $groupid]));
+
+ // Setting the group to hidden again should not change anything.
+ $this->assertFalse(block_groups_set_visibility($groupid, $courseid, false));
+ $this->assertFalse($DB->record_exists('block_groups_hide', ['id' => $groupid]));
+ }
/**
* Methodes recommended by moodle to assure database and dataroot is reset.
* @coversNothing
diff --git a/version.php b/version.php
index 70d5e4a..4e467a7 100644
--- a/version.php
+++ b/version.php
@@ -24,8 +24,8 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2026052001; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2022112800; // Requires 4.1+ Moodle version.
+$plugin->version = 2026061700; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2024100712; // Requires 4.5+ Moodle version.
$plugin->component = 'block_groups'; // Full name of the plugin (used for diagnostics).
-$plugin->release = 'v5.0-r1';
+$plugin->release = 'v5.2-r1';
$plugin->maturity = MATURITY_STABLE;