diff --git a/lib/amd/build/tag.min.js b/lib/amd/build/tag.min.js index ad1a96a3baf..49cbc9865a1 100644 --- a/lib/amd/build/tag.min.js +++ b/lib/amd/build/tag.min.js @@ -1,4 +1,4 @@ -define("core/tag",["exports","jquery","core/ajax","core/notification","core/templates","core/str","core/modal_events","core/pending","core/modal_save_cancel","core/config"],(function(_exports,_jquery,_ajax,Notification,Templates,_str,ModalEvents,_pending,_modal_save_cancel,_config){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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core/tag",["exports","jquery","core/ajax","core/notification","core/templates","core/str","core/modal_events","core/pending","core/modal_save_cancel","core/config","core_reportbuilder/local/selectors"],(function(_exports,_jquery,_ajax,Notification,Templates,_str,ModalEvents,_pending,_modal_save_cancel,_config,reportSelectors){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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * AJAX helper for the tag management page. * @@ -6,6 +6,6 @@ define("core/tag",["exports","jquery","core/ajax","core/notification","core/temp * @copyright 2015 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.0 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initTagindexPage=_exports.initManagePage=_exports.initManageCollectionsPage=void 0,_jquery=_interopRequireDefault(_jquery),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),ModalEvents=_interopRequireWildcard(ModalEvents),_pending=_interopRequireDefault(_pending),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_config=_interopRequireDefault(_config);const getCheckedTags=form=>form.querySelectorAll('input[data-togglegroup="tags-manage"][data-toggle="slave"]:checked');_exports.initTagindexPage=async()=>{document.addEventListener("click",(async e=>{const targetArea=e.target.closest('a[data-quickload="1"]');if(!targetArea)return;const tagArea=targetArea.closest(".tagarea[data-ta]");if(!tagArea)return;e.preventDefault();const pendingPromise=new _pending.default("core/tag:initTagindexPage"),query=targetArea.search.replace(/^\?/,""),params=Object.fromEntries(new URLSearchParams(query).entries());try{const data=await(tagindex=params,(0,_ajax.call)([{methodname:"core_tag_get_tagindex",args:{tagindex:tagindex}}])[0]),{html:html,js:js}=await Templates.renderForPromise("core_tag/index",data);Templates.replaceNode(tagArea,html,js)}catch(error){Notification.exception(error)}var tagindex;pendingPromise.resolve()}))};_exports.initManagePage=()=>{(0,_jquery.default)("body").on("updated","[data-inplaceeditable]",(function(e){var pendingPromise=new _pending.default("core/tag:initManagePage");if((0,_str.get_strings)([{key:"selecttag",component:"core_tag"},{key:"now",component:"core"}]).then((function(result){(0,_jquery.default)('label[for="tagselect'+e.ajaxreturn.itemid+'"]').html(result[0]),(0,_jquery.default)(e.target).closest("tr").find("td.col-timemodified").html(result[1])})).always(pendingPromise.resolve).catch(Notification.exception),"tagflag"===e.ajaxreturn.itemtype){var row=(0,_jquery.default)(e.target).closest("tr");"0"===e.ajaxreturn.value?row.removeClass("table-warning"):row.addClass("table-warning")}})),document.addEventListener("click",(async e=>{const tagManagementCombine=e.target.closest("#tag-management-combine");tagManagementCombine&&(e.preventDefault(),(async tagManagementCombine=>{const pendingPromise=new _pending.default("core/tag:tag-management-combine"),form=tagManagementCombine.closest("form"),checkedTags=getCheckedTags(form);if(checkedTags.length<=1)return void Notification.alert((0,_str.get_string)("combineselected","tag"),(0,_str.get_string)("selectmultipletags","tag"),(0,_str.get_string)("ok"));const tags=Array.from(checkedTags.values()).map((tag=>{const namedElement=document.querySelector('.inplaceeditable[data-itemtype=tagname][data-itemid="'.concat(tag.value,'"]'));return{id:tag.value,name:namedElement.dataset.value}})),modal=await _modal_save_cancel.default.create({title:(0,_str.get_string)("combineselected","tag"),buttons:{save:(0,_str.get_string)("continue","core")},body:Templates.render("core_tag/combine_tags",{tags:tags}),show:!0,removeOnClose:!0});modal.getRoot().on(ModalEvents.save,(e=>{e.preventDefault();const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=tagManagementCombine.name,form.append(tempElement);var maintag=(0,_jquery.default)("input[name=maintag]:checked","#combinetags_form").val();(0,_jquery.default)("").attr("name","maintag").attr("value",maintag).appendTo(form),form.submit()})),await modal.getBodyPromise();const firstOption=document.querySelector("#combinetags_form input[type=radio]");firstOption.focus(),firstOption.checked=!0,pendingPromise.resolve()})(tagManagementCombine)),e.target.closest('a[data-action="addstandardtag"]')&&(e.preventDefault(),(async()=>{var pendingPromise=new _pending.default("core/tag:addstandardtag");const modal=await _modal_save_cancel.default.create({title:(0,_str.get_string)("addotags","tag"),body:Templates.render("core_tag/add_tags",{actionurl:window.location.href,sesskey:M.cfg.sesskey}),buttons:{save:(0,_str.get_string)("continue","core")},removeOnClose:!0,show:!0});modal.getRoot().on(ModalEvents.save,(e=>{var tagsInput=(0,_jquery.default)(e.currentTarget).find("#id_tagslist"),name=tagsInput.val().trim();tagsInput.val(name);var tagsForm=(0,_jquery.default)("#addtags_form");return tagsForm.on("submit",(function(e){var form=(0,_jquery.default)("#addtags_form");!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="tagslistinput"]').addClass("error");var errorMessage=(0,_jquery.default)("#id_tagslist_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),tagsForm.submit(),!1})),await modal.getBodyPromise(),pendingPromise.resolve()})());const bulkActionDeleteButton=e.target.closest("#tag-management-delete");bulkActionDeleteButton&&(e.preventDefault(),(async form=>{if(getCheckedTags(form).length)try{await Notification.saveCancelPromise((0,_str.get_string)("delete"),(0,_str.get_string)("confirmdeletetags","tag"),(0,_str.get_string)("yes"),(0,_str.get_string)("no"));const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name="bulkdelete",form.append(tempElement),form.submit()}catch{return}})(bulkActionDeleteButton.closest("form")));const rowDeleteButton=e.target.closest(".tagdelete");rowDeleteButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.get_string)("delete"),(0,_str.get_string)("confirmdeletetag","tag"),(0,_str.get_string)("yes"),(0,_str.get_string)("no")),window.location.href=button.href}catch{return}})(rowDeleteButton))})),(0,_jquery.default)("body").on("updatefailed","[data-inplaceeditable][data-itemtype=tagname]",(async e=>{var exception=e.exception,newvalue=e.newvalue,tagid=(0,_jquery.default)(e.target).attr("data-itemid");if("namesalreadybeeingused"===exception.errorcode){e.preventDefault();try{await Notification.saveCancelPromise((0,_str.get_string)("confirm"),(0,_str.get_string)("nameuseddocombine","tag"),(0,_str.get_string)("yes"),(0,_str.get_string)("cancel"));const redirectTarget=new URL(window.location);redirectTarget.searchParams.set("newname",newvalue),redirectTarget.searchParams.set("tagid",tagid),redirectTarget.searchParams.set("action","renamecombine"),redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}}}))};_exports.initManageCollectionsPage=()=>{(0,_jquery.default)("body").on("updated","[data-inplaceeditable]",(function(e){var areaid,collid,pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-updated"),ajaxreturn=e.ajaxreturn;"core_tag"===ajaxreturn.component&&"tagareaenable"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),"1"===ajaxreturn.value?((0,_jquery.default)(this).closest("tr").removeClass("dimmed_text"),collid=(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareacollection"]').attr("data-value"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()):(0,_jquery.default)(this).closest("tr").addClass("dimmed_text")),"core_tag"===ajaxreturn.component&&"tagareacollection"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),collid=(0,_jquery.default)(this).attr("data-value"),"1"===(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareaenable"]').attr("data-value")&&(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()),pendingPromise.resolve()})),document.addEventListener("click",(async e=>{const addTagCollectionNode=e.target.closest(".addtagcoll > a");if(addTagCollectionNode)return e.preventDefault(),void(async link=>{const pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-addtagcoll"),href=link.dataset.url;(await _modal_save_cancel.default.create({title:(0,_str.get_string)("addtagcoll","tag"),buttons:{save:(0,_str.get_string)("create","core")},body:Templates.render("core_tag/add_tag_collection",{actionurl:href,sesskey:M.cfg.sesskey}),removeOnClose:!0,show:!0})).getRoot().on(ModalEvents.save,(e=>{const collectionInput=(0,_jquery.default)(e.currentTarget).find("#addtagcoll_name"),name=collectionInput.val().trim();collectionInput.val(name);const form=(0,_jquery.default)("#addtagcoll_form");return form.on("submit",(function(e){!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="addtagcoll_nameinput"]').addClass("error");const errorMessage=(0,_jquery.default)("#id_addtagcoll_name_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),form.submit(),!1})),pendingPromise.resolve()})(addTagCollectionNode);const deleteCollectionButton=e.target.closest(".tag-collections-table .action_delete");deleteCollectionButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.get_string)("delete"),(0,_str.get_string)("suredeletecoll","tag",button.dataset.collname),(0,_str.get_string)("yes"),(0,_str.get_string)("no"));const redirectTarget=new URL(button.dataset.url);redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}})(deleteCollectionButton))}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initTagindexPage=_exports.initManagePage=_exports.initManageCollectionsPage=void 0,_jquery=_interopRequireDefault(_jquery),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),ModalEvents=_interopRequireWildcard(ModalEvents),_pending=_interopRequireDefault(_pending),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_config=_interopRequireDefault(_config),reportSelectors=_interopRequireWildcard(reportSelectors);const getCheckedTags=root=>root.querySelectorAll('[data-togglegroup="report-select-all"][data-toggle="slave"]:checked');_exports.initTagindexPage=async()=>{document.addEventListener("click",(async e=>{const targetArea=e.target.closest('a[data-quickload="1"]');if(!targetArea)return;const tagArea=targetArea.closest(".tagarea[data-ta]");if(!tagArea)return;e.preventDefault();const pendingPromise=new _pending.default("core/tag:initTagindexPage"),query=targetArea.search.replace(/^\?/,""),params=Object.fromEntries(new URLSearchParams(query).entries());try{const data=await(tagindex=params,(0,_ajax.call)([{methodname:"core_tag_get_tagindex",args:{tagindex:tagindex}}])[0]),{html:html,js:js}=await Templates.renderForPromise("core_tag/index",data);Templates.replaceNode(tagArea,html,js)}catch(error){Notification.exception(error)}var tagindex;pendingPromise.resolve()}))};_exports.initManagePage=()=>{(0,_jquery.default)("body").on("updated","[data-inplaceeditable][data-itemtype=tagflag]",(function(e){(0,_jquery.default)(e.target).closest("tr").toggleClass("table-warning","1"===e.ajaxreturn.value)})),document.addEventListener("click",(async e=>{const tagManagementCombine=e.target.closest("#tag-management-combine");tagManagementCombine&&(e.preventDefault(),(async tagManagementCombine=>{const pendingPromise=new _pending.default("core/tag:tag-management-combine"),form=tagManagementCombine.closest("form"),reportElement=document.querySelector(reportSelectors.regions.report),checkedTags=getCheckedTags(reportElement);if(checkedTags.length<=1)return void Notification.alert((0,_str.getString)("combineselected","tag"),(0,_str.getString)("selectmultipletags","tag"),(0,_str.getString)("ok"));const tags=Array.from(checkedTags.values()).map((tag=>{const namedElement=document.querySelector('.inplaceeditable[data-itemtype=tagname][data-itemid="'.concat(tag.value,'"]'));return{id:tag.value,name:namedElement.dataset.value}})),modal=await _modal_save_cancel.default.create({title:(0,_str.getString)("combineselected","tag"),buttons:{save:(0,_str.getString)("continue","core")},body:Templates.render("core_tag/combine_tags",{tags:tags}),show:!0,removeOnClose:!0});modal.getRoot().on(ModalEvents.save,(e=>{e.preventDefault();const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=tagManagementCombine.name,form.append(tempElement);const tagsElement=document.createElement("input");tagsElement.hidden=!0,tagsElement.name="tagschecked",tagsElement.value=[...checkedTags].map((check=>check.value)).join(","),form.append(tagsElement);var maintag=(0,_jquery.default)("input[name=maintag]:checked","#combinetags_form").val();(0,_jquery.default)("").attr("name","maintag").attr("value",maintag).appendTo(form),form.submit()})),await modal.getBodyPromise();const firstOption=document.querySelector("#combinetags_form input[type=radio]");firstOption.focus(),firstOption.checked=!0,pendingPromise.resolve()})(tagManagementCombine)),e.target.closest('[data-action="addstandardtag"]')&&(e.preventDefault(),(async()=>{var pendingPromise=new _pending.default("core/tag:addstandardtag");const modal=await _modal_save_cancel.default.create({title:(0,_str.getString)("addotags","tag"),body:Templates.render("core_tag/add_tags",{actionurl:window.location.href,sesskey:M.cfg.sesskey}),buttons:{save:(0,_str.getString)("continue","core")},removeOnClose:!0,show:!0});modal.getRoot().on(ModalEvents.save,(e=>{var tagsInput=(0,_jquery.default)(e.currentTarget).find("#id_tagslist"),name=tagsInput.val().trim();tagsInput.val(name);var tagsForm=(0,_jquery.default)("#addtags_form");return tagsForm.on("submit",(function(e){var form=(0,_jquery.default)("#addtags_form");!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="tagslistinput"]').addClass("error");var errorMessage=(0,_jquery.default)("#id_tagslist_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),tagsForm.submit(),!1})),await modal.getBodyPromise(),pendingPromise.resolve()})());const bulkActionDeleteButton=e.target.closest("#tag-management-delete");bulkActionDeleteButton&&(e.preventDefault(),(async bulkActionDeleteButton=>{const form=bulkActionDeleteButton.closest("form"),reportElement=document.querySelector(reportSelectors.regions.report),checkedTags=getCheckedTags(reportElement);if(checkedTags.length)try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("confirmdeletetags","tag"),(0,_str.getString)("yes"),(0,_str.getString)("no"));const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=bulkActionDeleteButton.name,form.append(tempElement);const tagsElement=document.createElement("input");tagsElement.hidden=!0,tagsElement.name="tagschecked",tagsElement.value=[...checkedTags].map((check=>check.value)).join(","),form.append(tagsElement),form.submit()}catch{return}})(bulkActionDeleteButton));const rowDeleteButton=e.target.closest(".tagdelete");rowDeleteButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("confirmdeletetag","tag"),(0,_str.getString)("yes"),(0,_str.getString)("no")),window.location.href=button.href}catch{return}})(rowDeleteButton))})),(0,_jquery.default)("body").on("updatefailed","[data-inplaceeditable][data-itemtype=tagname]",(async e=>{var exception=e.exception,newvalue=e.newvalue,tagid=(0,_jquery.default)(e.target).attr("data-itemid");if("namesalreadybeeingused"===exception.errorcode){e.preventDefault();try{await Notification.saveCancelPromise((0,_str.getString)("confirm"),(0,_str.getString)("nameuseddocombine","tag"),(0,_str.getString)("yes"),(0,_str.getString)("cancel"));const redirectTarget=new URL(window.location);redirectTarget.searchParams.set("newname",newvalue),redirectTarget.searchParams.set("tagid",tagid),redirectTarget.searchParams.set("action","renamecombine"),redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}}}))};_exports.initManageCollectionsPage=()=>{(0,_jquery.default)("body").on("updated","[data-inplaceeditable]",(function(e){var areaid,collid,pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-updated"),ajaxreturn=e.ajaxreturn;"core_tag"===ajaxreturn.component&&"tagareaenable"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),"1"===ajaxreturn.value?((0,_jquery.default)(this).closest("tr").removeClass("dimmed_text"),collid=(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareacollection"]').attr("data-value"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()):(0,_jquery.default)(this).closest("tr").addClass("dimmed_text")),"core_tag"===ajaxreturn.component&&"tagareacollection"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),collid=(0,_jquery.default)(this).attr("data-value"),"1"===(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareaenable"]').attr("data-value")&&(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()),pendingPromise.resolve()})),document.addEventListener("click",(async e=>{const addTagCollectionNode=e.target.closest(".addtagcoll > a");if(addTagCollectionNode)return e.preventDefault(),void(async link=>{const pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-addtagcoll"),href=link.dataset.url;(await _modal_save_cancel.default.create({title:(0,_str.getString)("addtagcoll","tag"),buttons:{save:(0,_str.getString)("create","core")},body:Templates.render("core_tag/add_tag_collection",{actionurl:href,sesskey:M.cfg.sesskey}),removeOnClose:!0,show:!0})).getRoot().on(ModalEvents.save,(e=>{const collectionInput=(0,_jquery.default)(e.currentTarget).find("#addtagcoll_name"),name=collectionInput.val().trim();collectionInput.val(name);const form=(0,_jquery.default)("#addtagcoll_form");return form.on("submit",(function(e){!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="addtagcoll_nameinput"]').addClass("error");const errorMessage=(0,_jquery.default)("#id_addtagcoll_name_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),form.submit(),!1})),pendingPromise.resolve()})(addTagCollectionNode);const deleteCollectionButton=e.target.closest(".tag-collections-table .action_delete");deleteCollectionButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("suredeletecoll","tag",button.dataset.collname),(0,_str.getString)("yes"),(0,_str.getString)("no"));const redirectTarget=new URL(button.dataset.url);redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}})(deleteCollectionButton))}))}})); //# sourceMappingURL=tag.min.js.map \ No newline at end of file diff --git a/lib/amd/build/tag.min.js.map b/lib/amd/build/tag.min.js.map index a7b8f12e25e..24719af21f2 100644 --- a/lib/amd/build/tag.min.js.map +++ b/lib/amd/build/tag.min.js.map @@ -1 +1 @@ -{"version":3,"file":"tag.min.js","sources":["../src/tag.js"],"sourcesContent":["// 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 * AJAX helper for the tag management page.\n *\n * @module core/tag\n * @copyright 2015 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.0\n */\n\nimport $ from 'jquery';\nimport {call as fetchMany} from 'core/ajax';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport {\n get_string as getString,\n get_strings as getStrings,\n} from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport Config from 'core/config';\n\nconst getTagIndex = (tagindex) => fetchMany([{\n methodname: 'core_tag_get_tagindex',\n args: {tagindex}\n}])[0];\n\nconst getCheckedTags = (form) => form.querySelectorAll('input[data-togglegroup=\"tags-manage\"][data-toggle=\"slave\"]:checked');\n\nconst handleCombineRequest = async(tagManagementCombine) => {\n const pendingPromise = new Pending('core/tag:tag-management-combine');\n const form = tagManagementCombine.closest('form');\n const checkedTags = getCheckedTags(form);\n\n if (checkedTags.length <= 1) {\n // We need at least 2 tags to combine them.\n Notification.alert(\n getString('combineselected', 'tag'),\n getString('selectmultipletags', 'tag'),\n getString('ok'),\n );\n\n return;\n }\n\n const tags = Array.from(checkedTags.values()).map((tag) => {\n const namedElement = document.querySelector(`.inplaceeditable[data-itemtype=tagname][data-itemid=\"${tag.value}\"]`);\n return {\n id: tag.value,\n name: namedElement.dataset.value,\n };\n });\n\n const modal = await SaveCancelModal.create({\n title: getString('combineselected', 'tag'),\n buttons: {\n save: getString('continue', 'core'),\n },\n body: Templates.render('core_tag/combine_tags', {tags}),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n\n // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n const tempElement = document.createElement('input');\n tempElement.hidden = true;\n tempElement.name = tagManagementCombine.name;\n form.append(tempElement);\n\n // Get the selected tag from the modal.\n var maintag = $('input[name=maintag]:checked', '#combinetags_form').val();\n // Append this in the tags list form.\n $(\"\").attr('name', 'maintag').attr('value', maintag).appendTo(form);\n // Submit the tags list form.\n form.submit();\n });\n\n await modal.getBodyPromise();\n // Tick the first option.\n const firstOption = document.querySelector('#combinetags_form input[type=radio]');\n firstOption.focus();\n firstOption.checked = true;\n\n pendingPromise.resolve();\n\n return;\n};\n\nconst addStandardTags = async() => {\n var pendingPromise = new Pending('core/tag:addstandardtag');\n\n const modal = await SaveCancelModal.create({\n title: getString('addotags', 'tag'),\n body: Templates.render('core_tag/add_tags', {\n actionurl: window.location.href,\n sesskey: M.cfg.sesskey,\n }),\n buttons: {\n save: getString('continue', 'core'),\n },\n removeOnClose: true,\n show: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n var tagsInput = $(e.currentTarget).find('#id_tagslist');\n var name = tagsInput.val().trim();\n\n // Set the text field's value to the trimmed value.\n tagsInput.val(name);\n\n // Add submit event listener to the form.\n var tagsForm = $('#addtags_form');\n tagsForm.on('submit', function(e) {\n // Validate the form.\n var form = $('#addtags_form');\n if (form[0].checkValidity() === false) {\n e.preventDefault();\n e.stopPropagation();\n }\n form.addClass('was-validated');\n\n // BS2 compatibility.\n $('[data-region=\"tagslistinput\"]').addClass('error');\n var errorMessage = $('#id_tagslist_error_message');\n errorMessage.removeAttr('hidden');\n errorMessage.addClass('help-block');\n });\n\n // Try to submit the form.\n tagsForm.submit();\n\n return false;\n });\n\n await modal.getBodyPromise();\n pendingPromise.resolve();\n};\n\nconst deleteSelectedTags = async(form) => {\n const checkedTags = getCheckedTags(form);\n if (!checkedTags.length) {\n return;\n }\n\n try {\n await Notification.saveCancelPromise(\n getString('delete'),\n getString('confirmdeletetags', 'tag'),\n getString('yes'),\n getString('no'),\n );\n\n // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n const tempElement = document.createElement('input');\n tempElement.hidden = true;\n tempElement.name = 'bulkdelete';\n form.append(tempElement);\n form.submit();\n } catch {\n return;\n }\n};\n\nconst deleteSelectedTag = async(button) => {\n try {\n await Notification.saveCancelPromise(\n getString('delete'),\n getString('confirmdeletetag', 'tag'),\n getString('yes'),\n getString('no'),\n );\n\n window.location.href = button.href;\n } catch {\n return;\n }\n};\n\nconst deleteSelectedCollection = async(button) => {\n try {\n await Notification.saveCancelPromise(\n getString('delete'),\n getString('suredeletecoll', 'tag', button.dataset.collname),\n getString('yes'),\n getString('no'),\n );\n\n const redirectTarget = new URL(button.dataset.url);\n redirectTarget.searchParams.set('sesskey', Config.sesskey);\n window.location.href = redirectTarget;\n } catch {\n return;\n }\n};\n\nconst addTagCollection = async(link) => {\n const pendingPromise = new Pending('core/tag:initManageCollectionsPage-addtagcoll');\n const href = link.dataset.url;\n\n const modal = await SaveCancelModal.create({\n title: getString('addtagcoll', 'tag'),\n buttons: {\n save: getString('create', 'core'),\n },\n body: Templates.render('core_tag/add_tag_collection', {\n actionurl: href,\n sesskey: M.cfg.sesskey,\n }),\n removeOnClose: true,\n show: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n const collectionInput = $(e.currentTarget).find('#addtagcoll_name');\n const name = collectionInput.val().trim();\n // Set the text field's value to the trimmed value.\n collectionInput.val(name);\n\n // Add submit event listener to the form.\n const form = $('#addtagcoll_form');\n form.on('submit', function(e) {\n // Validate the form.\n if (form[0].checkValidity() === false) {\n e.preventDefault();\n e.stopPropagation();\n }\n form.addClass('was-validated');\n\n // BS2 compatibility.\n $('[data-region=\"addtagcoll_nameinput\"]').addClass('error');\n const errorMessage = $('#id_addtagcoll_name_error_message');\n errorMessage.removeAttr('hidden');\n errorMessage.addClass('help-block');\n });\n\n // Try to submit the form.\n form.submit();\n\n return false;\n });\n\n pendingPromise.resolve();\n};\n\n/**\n * Initialises tag index page.\n *\n * @method initTagindexPage\n */\nexport const initTagindexPage = async() => {\n document.addEventListener('click', async(e) => {\n const targetArea = e.target.closest('a[data-quickload=\"1\"]');\n if (!targetArea) {\n return;\n }\n const tagArea = targetArea.closest('.tagarea[data-ta]');\n if (!tagArea) {\n return;\n }\n\n e.preventDefault();\n const pendingPromise = new Pending('core/tag:initTagindexPage');\n\n const query = targetArea.search.replace(/^\\?/, '');\n const params = Object.fromEntries((new URLSearchParams(query)).entries());\n\n try {\n const data = await getTagIndex(params);\n const {html, js} = await Templates.renderForPromise('core_tag/index', data);\n Templates.replaceNode(tagArea, html, js);\n } catch (error) {\n Notification.exception(error);\n }\n pendingPromise.resolve();\n });\n};\n\n/**\n * Initialises tag management page.\n *\n * @method initManagePage\n */\nexport const initManagePage = () => {\n // Set cell 'time modified' to 'now' when any of the element is updated in this row.\n $('body').on('updated', '[data-inplaceeditable]', function(e) {\n var pendingPromise = new Pending('core/tag:initManagePage');\n\n getStrings([\n {\n key: 'selecttag',\n component: 'core_tag',\n },\n {\n key: 'now',\n component: 'core',\n },\n ])\n .then(function(result) {\n $('label[for=\"tagselect' + e.ajaxreturn.itemid + '\"]').html(result[0]);\n $(e.target).closest('tr').find('td.col-timemodified').html(result[1]);\n\n return;\n })\n .always(pendingPromise.resolve)\n .catch(Notification.exception);\n\n if (e.ajaxreturn.itemtype === 'tagflag') {\n var row = $(e.target).closest('tr');\n if (e.ajaxreturn.value === '0') {\n row.removeClass('table-warning');\n } else {\n row.addClass('table-warning');\n }\n }\n });\n\n // Confirmation for bulk tag combine button.\n document.addEventListener('click', async(e) => {\n const tagManagementCombine = e.target.closest('#tag-management-combine');\n if (tagManagementCombine) {\n e.preventDefault();\n handleCombineRequest(tagManagementCombine);\n }\n\n if (e.target.closest('a[data-action=\"addstandardtag\"]')) {\n e.preventDefault();\n addStandardTags();\n }\n\n const bulkActionDeleteButton = e.target.closest('#tag-management-delete');\n if (bulkActionDeleteButton) {\n e.preventDefault();\n deleteSelectedTags(bulkActionDeleteButton.closest('form'));\n }\n\n const rowDeleteButton = e.target.closest('.tagdelete');\n if (rowDeleteButton) {\n e.preventDefault();\n deleteSelectedTag(rowDeleteButton);\n }\n });\n\n // When user changes tag name to some name that already exists suggest to combine the tags.\n $('body').on('updatefailed', '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {\n var exception = e.exception; // The exception object returned by the callback.\n var newvalue = e.newvalue; // The value that user tried to udpated the element to.\n var tagid = $(e.target).attr('data-itemid');\n if (exception.errorcode !== 'namesalreadybeeingused') {\n return;\n }\n e.preventDefault(); // This will prevent default error dialogue.\n\n try {\n await Notification.saveCancelPromise(\n getString('confirm'),\n getString('nameuseddocombine', 'tag'),\n getString('yes'),\n getString('cancel'),\n );\n\n // The Promise will resolve on 'Yes' button, and reject on 'Cancel' button.\n const redirectTarget = new URL(window.location);\n redirectTarget.searchParams.set('newname', newvalue);\n redirectTarget.searchParams.set('tagid', tagid);\n redirectTarget.searchParams.set('action', 'renamecombine');\n redirectTarget.searchParams.set('sesskey', Config.sesskey);\n\n window.location.href = redirectTarget;\n } catch {\n return;\n }\n });\n};\n\n/**\n * Initialises tag collection management page.\n *\n * @method initManageCollectionsPage\n */\nexport const initManageCollectionsPage = () => {\n $('body').on('updated', '[data-inplaceeditable]', function(e) {\n var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');\n\n var ajaxreturn = e.ajaxreturn,\n areaid, collid, isenabled;\n if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {\n areaid = $(this).attr('data-itemid');\n $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n isenabled = ajaxreturn.value;\n if (isenabled === '1') {\n $(this).closest('tr').removeClass('dimmed_text');\n collid = $(this).closest('tr').find('[data-itemtype=\"tagareacollection\"]').attr(\"data-value\");\n $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n } else {\n $(this).closest('tr').addClass('dimmed_text');\n }\n }\n if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareacollection') {\n areaid = $(this).attr('data-itemid');\n $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n collid = $(this).attr('data-value');\n isenabled = $(this).closest('tr').find('[data-itemtype=\"tagareaenable\"]').attr(\"data-value\");\n if (isenabled === \"1\") {\n $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n }\n }\n\n pendingPromise.resolve();\n });\n\n document.addEventListener('click', async(e) => {\n const addTagCollectionNode = e.target.closest('.addtagcoll > a');\n if (addTagCollectionNode) {\n e.preventDefault();\n addTagCollection(addTagCollectionNode);\n return;\n }\n\n const deleteCollectionButton = e.target.closest('.tag-collections-table .action_delete');\n if (deleteCollectionButton) {\n e.preventDefault();\n deleteSelectedCollection(deleteCollectionButton);\n }\n });\n};\n"],"names":["getCheckedTags","form","querySelectorAll","async","document","addEventListener","targetArea","e","target","closest","tagArea","preventDefault","pendingPromise","Pending","query","search","replace","params","Object","fromEntries","URLSearchParams","entries","data","tagindex","methodname","args","html","js","Templates","renderForPromise","replaceNode","error","Notification","exception","resolve","on","key","component","then","result","ajaxreturn","itemid","find","always","catch","itemtype","row","value","removeClass","addClass","tagManagementCombine","checkedTags","length","alert","tags","Array","from","values","map","tag","namedElement","querySelector","id","name","dataset","modal","SaveCancelModal","create","title","buttons","save","body","render","show","removeOnClose","getRoot","ModalEvents","tempElement","createElement","hidden","append","maintag","val","attr","appendTo","submit","getBodyPromise","firstOption","focus","checked","handleCombineRequest","actionurl","window","location","href","sesskey","M","cfg","tagsInput","currentTarget","trim","tagsForm","checkValidity","stopPropagation","errorMessage","removeAttr","addStandardTags","bulkActionDeleteButton","saveCancelPromise","deleteSelectedTags","rowDeleteButton","button","deleteSelectedTag","newvalue","tagid","errorcode","redirectTarget","URL","searchParams","set","Config","areaid","collid","this","hide","addTagCollectionNode","link","url","collectionInput","addTagCollection","deleteCollectionButton","collname","deleteSelectedCollection"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"tag.min.js","sources":["../src/tag.js"],"sourcesContent":["// 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 * AJAX helper for the tag management page.\n *\n * @module core/tag\n * @copyright 2015 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.0\n */\n\nimport $ from 'jquery';\nimport {call as fetchMany} from 'core/ajax';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport Config from 'core/config';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst getTagIndex = (tagindex) => fetchMany([{\n methodname: 'core_tag_get_tagindex',\n args: {tagindex}\n}])[0];\n\nconst getCheckedTags = (root) => root.querySelectorAll('[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked');\n\nconst handleCombineRequest = async(tagManagementCombine) => {\n const pendingPromise = new Pending('core/tag:tag-management-combine');\n const form = tagManagementCombine.closest('form');\n\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const checkedTags = getCheckedTags(reportElement);\n\n if (checkedTags.length <= 1) {\n // We need at least 2 tags to combine them.\n Notification.alert(\n getString('combineselected', 'tag'),\n getString('selectmultipletags', 'tag'),\n getString('ok'),\n );\n\n return;\n }\n\n const tags = Array.from(checkedTags.values()).map((tag) => {\n const namedElement = document.querySelector(`.inplaceeditable[data-itemtype=tagname][data-itemid=\"${tag.value}\"]`);\n return {\n id: tag.value,\n name: namedElement.dataset.value,\n };\n });\n\n const modal = await SaveCancelModal.create({\n title: getString('combineselected', 'tag'),\n buttons: {\n save: getString('continue', 'core'),\n },\n body: Templates.render('core_tag/combine_tags', {tags}),\n show: true,\n removeOnClose: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n\n // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n const tempElement = document.createElement('input');\n tempElement.hidden = true;\n tempElement.name = tagManagementCombine.name;\n form.append(tempElement);\n\n // Append selected tags element.\n const tagsElement = document.createElement('input');\n tagsElement.hidden = true;\n tagsElement.name = 'tagschecked';\n tagsElement.value = [...checkedTags].map(check => check.value).join(',');\n form.append(tagsElement);\n\n // Get the selected tag from the modal.\n var maintag = $('input[name=maintag]:checked', '#combinetags_form').val();\n // Append this in the tags list form.\n $(\"\").attr('name', 'maintag').attr('value', maintag).appendTo(form);\n // Submit the tags list form.\n form.submit();\n });\n\n await modal.getBodyPromise();\n // Tick the first option.\n const firstOption = document.querySelector('#combinetags_form input[type=radio]');\n firstOption.focus();\n firstOption.checked = true;\n\n pendingPromise.resolve();\n\n return;\n};\n\nconst addStandardTags = async() => {\n var pendingPromise = new Pending('core/tag:addstandardtag');\n\n const modal = await SaveCancelModal.create({\n title: getString('addotags', 'tag'),\n body: Templates.render('core_tag/add_tags', {\n actionurl: window.location.href,\n sesskey: M.cfg.sesskey,\n }),\n buttons: {\n save: getString('continue', 'core'),\n },\n removeOnClose: true,\n show: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n var tagsInput = $(e.currentTarget).find('#id_tagslist');\n var name = tagsInput.val().trim();\n\n // Set the text field's value to the trimmed value.\n tagsInput.val(name);\n\n // Add submit event listener to the form.\n var tagsForm = $('#addtags_form');\n tagsForm.on('submit', function(e) {\n // Validate the form.\n var form = $('#addtags_form');\n if (form[0].checkValidity() === false) {\n e.preventDefault();\n e.stopPropagation();\n }\n form.addClass('was-validated');\n\n // BS2 compatibility.\n $('[data-region=\"tagslistinput\"]').addClass('error');\n var errorMessage = $('#id_tagslist_error_message');\n errorMessage.removeAttr('hidden');\n errorMessage.addClass('help-block');\n });\n\n // Try to submit the form.\n tagsForm.submit();\n\n return false;\n });\n\n await modal.getBodyPromise();\n pendingPromise.resolve();\n};\n\nconst deleteSelectedTags = async(bulkActionDeleteButton) => {\n const form = bulkActionDeleteButton.closest('form');\n\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const checkedTags = getCheckedTags(reportElement);\n\n if (!checkedTags.length) {\n return;\n }\n\n try {\n await Notification.saveCancelPromise(\n getString('delete'),\n getString('confirmdeletetags', 'tag'),\n getString('yes'),\n getString('no'),\n );\n\n // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n const tempElement = document.createElement('input');\n tempElement.hidden = true;\n tempElement.name = bulkActionDeleteButton.name;\n form.append(tempElement);\n\n // Append selected tags element.\n const tagsElement = document.createElement('input');\n tagsElement.hidden = true;\n tagsElement.name = 'tagschecked';\n tagsElement.value = [...checkedTags].map(check => check.value).join(',');\n form.append(tagsElement);\n\n form.submit();\n } catch {\n return;\n }\n};\n\nconst deleteSelectedTag = async(button) => {\n try {\n await Notification.saveCancelPromise(\n getString('delete'),\n getString('confirmdeletetag', 'tag'),\n getString('yes'),\n getString('no'),\n );\n\n window.location.href = button.href;\n } catch {\n return;\n }\n};\n\nconst deleteSelectedCollection = async(button) => {\n try {\n await Notification.saveCancelPromise(\n getString('delete'),\n getString('suredeletecoll', 'tag', button.dataset.collname),\n getString('yes'),\n getString('no'),\n );\n\n const redirectTarget = new URL(button.dataset.url);\n redirectTarget.searchParams.set('sesskey', Config.sesskey);\n window.location.href = redirectTarget;\n } catch {\n return;\n }\n};\n\nconst addTagCollection = async(link) => {\n const pendingPromise = new Pending('core/tag:initManageCollectionsPage-addtagcoll');\n const href = link.dataset.url;\n\n const modal = await SaveCancelModal.create({\n title: getString('addtagcoll', 'tag'),\n buttons: {\n save: getString('create', 'core'),\n },\n body: Templates.render('core_tag/add_tag_collection', {\n actionurl: href,\n sesskey: M.cfg.sesskey,\n }),\n removeOnClose: true,\n show: true,\n });\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, (e) => {\n const collectionInput = $(e.currentTarget).find('#addtagcoll_name');\n const name = collectionInput.val().trim();\n // Set the text field's value to the trimmed value.\n collectionInput.val(name);\n\n // Add submit event listener to the form.\n const form = $('#addtagcoll_form');\n form.on('submit', function(e) {\n // Validate the form.\n if (form[0].checkValidity() === false) {\n e.preventDefault();\n e.stopPropagation();\n }\n form.addClass('was-validated');\n\n // BS2 compatibility.\n $('[data-region=\"addtagcoll_nameinput\"]').addClass('error');\n const errorMessage = $('#id_addtagcoll_name_error_message');\n errorMessage.removeAttr('hidden');\n errorMessage.addClass('help-block');\n });\n\n // Try to submit the form.\n form.submit();\n\n return false;\n });\n\n pendingPromise.resolve();\n};\n\n/**\n * Initialises tag index page.\n *\n * @method initTagindexPage\n */\nexport const initTagindexPage = async() => {\n document.addEventListener('click', async(e) => {\n const targetArea = e.target.closest('a[data-quickload=\"1\"]');\n if (!targetArea) {\n return;\n }\n const tagArea = targetArea.closest('.tagarea[data-ta]');\n if (!tagArea) {\n return;\n }\n\n e.preventDefault();\n const pendingPromise = new Pending('core/tag:initTagindexPage');\n\n const query = targetArea.search.replace(/^\\?/, '');\n const params = Object.fromEntries((new URLSearchParams(query)).entries());\n\n try {\n const data = await getTagIndex(params);\n const {html, js} = await Templates.renderForPromise('core_tag/index', data);\n Templates.replaceNode(tagArea, html, js);\n } catch (error) {\n Notification.exception(error);\n }\n pendingPromise.resolve();\n });\n};\n\n/**\n * Initialises tag management page.\n *\n * @method initManagePage\n */\nexport const initManagePage = () => {\n // Toggle row class when updating flag.\n $('body').on('updated', '[data-inplaceeditable][data-itemtype=tagflag]', function(e) {\n var row = $(e.target).closest('tr');\n row.toggleClass('table-warning', e.ajaxreturn.value === '1');\n });\n\n // Confirmation for bulk tag combine button.\n document.addEventListener('click', async(e) => {\n const tagManagementCombine = e.target.closest('#tag-management-combine');\n if (tagManagementCombine) {\n e.preventDefault();\n handleCombineRequest(tagManagementCombine);\n }\n\n if (e.target.closest('[data-action=\"addstandardtag\"]')) {\n e.preventDefault();\n addStandardTags();\n }\n\n const bulkActionDeleteButton = e.target.closest('#tag-management-delete');\n if (bulkActionDeleteButton) {\n e.preventDefault();\n deleteSelectedTags(bulkActionDeleteButton);\n }\n\n const rowDeleteButton = e.target.closest('.tagdelete');\n if (rowDeleteButton) {\n e.preventDefault();\n deleteSelectedTag(rowDeleteButton);\n }\n });\n\n // When user changes tag name to some name that already exists suggest to combine the tags.\n $('body').on('updatefailed', '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {\n var exception = e.exception; // The exception object returned by the callback.\n var newvalue = e.newvalue; // The value that user tried to udpated the element to.\n var tagid = $(e.target).attr('data-itemid');\n if (exception.errorcode !== 'namesalreadybeeingused') {\n return;\n }\n e.preventDefault(); // This will prevent default error dialogue.\n\n try {\n await Notification.saveCancelPromise(\n getString('confirm'),\n getString('nameuseddocombine', 'tag'),\n getString('yes'),\n getString('cancel'),\n );\n\n // The Promise will resolve on 'Yes' button, and reject on 'Cancel' button.\n const redirectTarget = new URL(window.location);\n redirectTarget.searchParams.set('newname', newvalue);\n redirectTarget.searchParams.set('tagid', tagid);\n redirectTarget.searchParams.set('action', 'renamecombine');\n redirectTarget.searchParams.set('sesskey', Config.sesskey);\n\n window.location.href = redirectTarget;\n } catch {\n return;\n }\n });\n};\n\n/**\n * Initialises tag collection management page.\n *\n * @method initManageCollectionsPage\n */\nexport const initManageCollectionsPage = () => {\n $('body').on('updated', '[data-inplaceeditable]', function(e) {\n var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');\n\n var ajaxreturn = e.ajaxreturn,\n areaid, collid, isenabled;\n if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {\n areaid = $(this).attr('data-itemid');\n $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n isenabled = ajaxreturn.value;\n if (isenabled === '1') {\n $(this).closest('tr').removeClass('dimmed_text');\n collid = $(this).closest('tr').find('[data-itemtype=\"tagareacollection\"]').attr(\"data-value\");\n $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n } else {\n $(this).closest('tr').addClass('dimmed_text');\n }\n }\n if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareacollection') {\n areaid = $(this).attr('data-itemid');\n $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n collid = $(this).attr('data-value');\n isenabled = $(this).closest('tr').find('[data-itemtype=\"tagareaenable\"]').attr(\"data-value\");\n if (isenabled === \"1\") {\n $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n }\n }\n\n pendingPromise.resolve();\n });\n\n document.addEventListener('click', async(e) => {\n const addTagCollectionNode = e.target.closest('.addtagcoll > a');\n if (addTagCollectionNode) {\n e.preventDefault();\n addTagCollection(addTagCollectionNode);\n return;\n }\n\n const deleteCollectionButton = e.target.closest('.tag-collections-table .action_delete');\n if (deleteCollectionButton) {\n e.preventDefault();\n deleteSelectedCollection(deleteCollectionButton);\n }\n });\n};\n"],"names":["getCheckedTags","root","querySelectorAll","async","document","addEventListener","targetArea","e","target","closest","tagArea","preventDefault","pendingPromise","Pending","query","search","replace","params","Object","fromEntries","URLSearchParams","entries","data","tagindex","methodname","args","html","js","Templates","renderForPromise","replaceNode","error","Notification","exception","resolve","on","toggleClass","ajaxreturn","value","tagManagementCombine","form","reportElement","querySelector","reportSelectors","regions","report","checkedTags","length","alert","tags","Array","from","values","map","tag","namedElement","id","name","dataset","modal","SaveCancelModal","create","title","buttons","save","body","render","show","removeOnClose","getRoot","ModalEvents","tempElement","createElement","hidden","append","tagsElement","check","join","maintag","val","attr","appendTo","submit","getBodyPromise","firstOption","focus","checked","handleCombineRequest","actionurl","window","location","href","sesskey","M","cfg","tagsInput","currentTarget","find","trim","tagsForm","checkValidity","stopPropagation","addClass","errorMessage","removeAttr","addStandardTags","bulkActionDeleteButton","saveCancelPromise","deleteSelectedTags","rowDeleteButton","button","deleteSelectedTag","newvalue","tagid","errorcode","redirectTarget","URL","searchParams","set","Config","areaid","collid","component","itemtype","this","hide","removeClass","addTagCollectionNode","link","url","collectionInput","addTagCollection","deleteCollectionButton","collname","deleteSelectedCollection"],"mappings":";;;;;;;;iiBAwCMA,eAAkBC,MAASA,KAAKC,iBAAiB,iGA0PvBC,UAC5BC,SAASC,iBAAiB,SAASF,MAAAA,UACzBG,WAAaC,EAAEC,OAAOC,QAAQ,6BAC/BH,wBAGCI,QAAUJ,WAAWG,QAAQ,yBAC9BC,eAILH,EAAEI,uBACIC,eAAiB,IAAIC,iBAAQ,6BAE7BC,MAAQR,WAAWS,OAAOC,QAAQ,MAAO,IACzCC,OAASC,OAAOC,YAAa,IAAIC,gBAAgBN,OAAQO,qBAGrDC,WAjRGC,SAiRsBN,QAjRT,cAAU,CAAC,CACzCO,WAAY,wBACZC,KAAM,CAACF,SAAAA,aACP,KA+QcG,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,iBAAkBP,MACtEM,UAAUE,YAAYpB,QAASgB,KAAMC,IACvC,MAAOI,OACLC,aAAaC,UAAUF,OArRdR,IAAAA,SAuRbX,eAAesB,sCASO,yBAExB,QAAQC,GAAG,UAAW,iDAAiD,SAAS5B,IACpE,mBAAEA,EAAEC,QAAQC,QAAQ,MAC1B2B,YAAY,gBAAwC,MAAvB7B,EAAE8B,WAAWC,UAIlDlC,SAASC,iBAAiB,SAASF,MAAAA,UACzBoC,qBAAuBhC,EAAEC,OAAOC,QAAQ,2BAC1C8B,uBACAhC,EAAEI,iBApSeR,OAAAA,6BACnBS,eAAiB,IAAIC,iBAAQ,mCAC7B2B,KAAOD,qBAAqB9B,QAAQ,QAEpCgC,cAAgBrC,SAASsC,cAAcC,gBAAgBC,QAAQC,QAC/DC,YAAc9C,eAAeyC,kBAE/BK,YAAYC,QAAU,cAEtBf,aAAagB,OACT,kBAAU,kBAAmB,QAC7B,kBAAU,qBAAsB,QAChC,kBAAU,aAMZC,KAAOC,MAAMC,KAAKL,YAAYM,UAAUC,KAAKC,YACzCC,aAAenD,SAASsC,6EAAsEY,IAAIhB,mBACjG,CACHkB,GAAIF,IAAIhB,MACRmB,KAAMF,aAAaG,QAAQpB,UAI7BqB,YAAcC,2BAAgBC,OAAO,CACvCC,OAAO,kBAAU,kBAAmB,OACpCC,QAAS,CACLC,MAAM,kBAAU,WAAY,SAEhCC,KAAMrC,UAAUsC,OAAO,wBAAyB,CAACjB,KAAAA,OACjDkB,MAAM,EACNC,eAAe,IAInBT,MAAMU,UAAUlC,GAAGmC,YAAYN,MAAOzD,IAClCA,EAAEI,uBAGI4D,YAAcnE,SAASoE,cAAc,SAC3CD,YAAYE,QAAS,EACrBF,YAAYd,KAAOlB,qBAAqBkB,KACxCjB,KAAKkC,OAAOH,mBAGNI,YAAcvE,SAASoE,cAAc,SAC3CG,YAAYF,QAAS,EACrBE,YAAYlB,KAAO,cACnBkB,YAAYrC,MAAQ,IAAIQ,aAAaO,KAAIuB,OAASA,MAAMtC,QAAOuC,KAAK,KACpErC,KAAKkC,OAAOC,iBAGRG,SAAU,mBAAE,8BAA+B,qBAAqBC,0BAElE,0BAA0BC,KAAK,OAAQ,WAAWA,KAAK,QAASF,SAASG,SAASzC,MAEpFA,KAAK0C,kBAGHvB,MAAMwB,uBAENC,YAAchF,SAASsC,cAAc,uCAC3C0C,YAAYC,QACZD,YAAYE,SAAU,EAEtB1E,eAAesB,WAkOPqD,CAAqBhD,uBAGrBhC,EAAEC,OAAOC,QAAQ,oCACjBF,EAAEI,iBAjOUR,eAChBS,eAAiB,IAAIC,iBAAQ,iCAE3B8C,YAAcC,2BAAgBC,OAAO,CACvCC,OAAO,kBAAU,WAAY,OAC7BG,KAAMrC,UAAUsC,OAAO,oBAAqB,CACxCsB,UAAWC,OAAOC,SAASC,KAC3BC,QAASC,EAAEC,IAAIF,UAEnB7B,QAAS,CACLC,MAAM,kBAAU,WAAY,SAEhCI,eAAe,EACfD,MAAM,IAIVR,MAAMU,UAAUlC,GAAGmC,YAAYN,MAAOzD,QAC9BwF,WAAY,mBAAExF,EAAEyF,eAAeC,KAAK,gBACpCxC,KAAOsC,UAAUhB,MAAMmB,OAG3BH,UAAUhB,IAAItB,UAGV0C,UAAW,mBAAE,wBACjBA,SAAShE,GAAG,UAAU,SAAS5B,OAEvBiC,MAAO,mBAAE,kBACmB,IAA5BA,KAAK,GAAG4D,kBACR7F,EAAEI,iBACFJ,EAAE8F,mBAEN7D,KAAK8D,SAAS,qCAGZ,iCAAiCA,SAAS,aACxCC,cAAe,mBAAE,8BACrBA,aAAaC,WAAW,UACxBD,aAAaD,SAAS,iBAI1BH,SAASjB,UAEF,WAGLvB,MAAMwB,iBACZvE,eAAesB,WAiLPuE,UAGEC,uBAAyBnG,EAAEC,OAAOC,QAAQ,0BAC5CiG,yBACAnG,EAAEI,iBAnLaR,OAAAA,+BACjBqC,KAAOkE,uBAAuBjG,QAAQ,QAEtCgC,cAAgBrC,SAASsC,cAAcC,gBAAgBC,QAAQC,QAC/DC,YAAc9C,eAAeyC,kBAE9BK,YAAYC,iBAKPf,aAAa2E,mBACf,kBAAU,WACV,kBAAU,oBAAqB,QAC/B,kBAAU,QACV,kBAAU,aAIRpC,YAAcnE,SAASoE,cAAc,SAC3CD,YAAYE,QAAS,EACrBF,YAAYd,KAAOiD,uBAAuBjD,KAC1CjB,KAAKkC,OAAOH,mBAGNI,YAAcvE,SAASoE,cAAc,SAC3CG,YAAYF,QAAS,EACrBE,YAAYlB,KAAO,cACnBkB,YAAYrC,MAAQ,IAAIQ,aAAaO,KAAIuB,OAASA,MAAMtC,QAAOuC,KAAK,KACpErC,KAAKkC,OAAOC,aAEZnC,KAAK0C,SACP,eAoJM0B,CAAmBF,+BAGjBG,gBAAkBtG,EAAEC,OAAOC,QAAQ,cACrCoG,kBACAtG,EAAEI,iBApJYR,OAAAA,mBAEZ6B,aAAa2E,mBACf,kBAAU,WACV,kBAAU,mBAAoB,QAC9B,kBAAU,QACV,kBAAU,OAGdlB,OAAOC,SAASC,KAAOmB,OAAOnB,KAChC,eA2IMoB,CAAkBF,yCAKxB,QAAQ1E,GAAG,eAAgB,iDAAiDhC,MAAAA,QACtE8B,UAAY1B,EAAE0B,UACd+E,SAAWzG,EAAEyG,SACbC,OAAQ,mBAAE1G,EAAEC,QAAQwE,KAAK,kBACD,2BAAxB/C,UAAUiF,WAGd3G,EAAEI,2BAGQqB,aAAa2E,mBACf,kBAAU,YACV,kBAAU,oBAAqB,QAC/B,kBAAU,QACV,kBAAU,iBAIRQ,eAAiB,IAAIC,IAAI3B,OAAOC,UACtCyB,eAAeE,aAAaC,IAAI,UAAWN,UAC3CG,eAAeE,aAAaC,IAAI,QAASL,OACzCE,eAAeE,aAAaC,IAAI,SAAU,iBAC1CH,eAAeE,aAAaC,IAAI,UAAWC,gBAAO3B,SAElDH,OAAOC,SAASC,KAAOwB,eACzB,sDAW+B,yBACnC,QAAQhF,GAAG,UAAW,0BAA0B,SAAS5B,OAInDiH,OAAQC,OAHR7G,eAAiB,IAAIC,iBAAQ,8CAE7BwB,WAAa9B,EAAE8B,WAEU,aAAzBA,WAAWqF,WAAoD,kBAAxBrF,WAAWsF,WAClDH,QAAS,mBAAEI,MAAM5C,KAAK,mCACpB,+DAAiEwC,OAAS,KAAKK,OAE/D,MADNxF,WAAWC,2BAEjBsF,MAAMnH,QAAQ,MAAMqH,YAAY,eAClCL,QAAS,mBAAEG,MAAMnH,QAAQ,MAAMwF,KAAK,uCAAuCjB,KAAK,kCAC9E,+CAAiDyC,OAAS,oBAAsBD,OAAS,KAAKrD,4BAE9FyD,MAAMnH,QAAQ,MAAM6F,SAAS,gBAGV,aAAzBjE,WAAWqF,WAAoD,sBAAxBrF,WAAWsF,WAClDH,QAAS,mBAAEI,MAAM5C,KAAK,mCACpB,+DAAiEwC,OAAS,KAAKK,OACjFJ,QAAS,mBAAEG,MAAM5C,KAAK,cAEJ,OADN,mBAAE4C,MAAMnH,QAAQ,MAAMwF,KAAK,mCAAmCjB,KAAK,mCAEzE,+CAAiDyC,OAAS,oBAAsBD,OAAS,KAAKrD,QAIxGvD,eAAesB,aAGnB9B,SAASC,iBAAiB,SAASF,MAAAA,UACzB4H,qBAAuBxH,EAAEC,OAAOC,QAAQ,sBAC1CsH,4BACAxH,EAAEI,qBAhMWR,OAAAA,aACfS,eAAiB,IAAIC,iBAAQ,iDAC7B8E,KAAOqC,KAAKtE,QAAQuE,WAENrE,2BAAgBC,OAAO,CACvCC,OAAO,kBAAU,aAAc,OAC/BC,QAAS,CACLC,MAAM,kBAAU,SAAU,SAE9BC,KAAMrC,UAAUsC,OAAO,8BAA+B,CAClDsB,UAAWG,KACXC,QAASC,EAAEC,IAAIF,UAEnBxB,eAAe,EACfD,MAAM,KAIJE,UAAUlC,GAAGmC,YAAYN,MAAOzD,UAC5B2H,iBAAkB,mBAAE3H,EAAEyF,eAAeC,KAAK,oBAC1CxC,KAAOyE,gBAAgBnD,MAAMmB,OAEnCgC,gBAAgBnD,IAAItB,YAGdjB,MAAO,mBAAE,2BACfA,KAAKL,GAAG,UAAU,SAAS5B,IAES,IAA5BiC,KAAK,GAAG4D,kBACR7F,EAAEI,iBACFJ,EAAE8F,mBAEN7D,KAAK8D,SAAS,qCAGZ,wCAAwCA,SAAS,eAC7CC,cAAe,mBAAE,qCACvBA,aAAaC,WAAW,UACxBD,aAAaD,SAAS,iBAI1B9D,KAAK0C,UAEE,KAGXtE,eAAesB,WAkJPiG,CAAiBJ,4BAIfK,uBAAyB7H,EAAEC,OAAOC,QAAQ,yCAC5C2H,yBACA7H,EAAEI,iBAxNmBR,OAAAA,mBAEnB6B,aAAa2E,mBACf,kBAAU,WACV,kBAAU,iBAAkB,MAAOG,OAAOpD,QAAQ2E,WAClD,kBAAU,QACV,kBAAU,aAGRlB,eAAiB,IAAIC,IAAIN,OAAOpD,QAAQuE,KAC9Cd,eAAeE,aAAaC,IAAI,UAAWC,gBAAO3B,SAClDH,OAAOC,SAASC,KAAOwB,eACzB,eA6MMmB,CAAyBF"} \ No newline at end of file diff --git a/lib/amd/src/tag.js b/lib/amd/src/tag.js index 042bcdcb4b5..e8c6d98c6d0 100644 --- a/lib/amd/src/tag.js +++ b/lib/amd/src/tag.js @@ -26,26 +26,26 @@ import $ from 'jquery'; import {call as fetchMany} from 'core/ajax'; import * as Notification from 'core/notification'; import * as Templates from 'core/templates'; -import { - get_string as getString, - get_strings as getStrings, -} from 'core/str'; +import {getString} from 'core/str'; import * as ModalEvents from 'core/modal_events'; import Pending from 'core/pending'; import SaveCancelModal from 'core/modal_save_cancel'; import Config from 'core/config'; +import * as reportSelectors from 'core_reportbuilder/local/selectors'; const getTagIndex = (tagindex) => fetchMany([{ methodname: 'core_tag_get_tagindex', args: {tagindex} }])[0]; -const getCheckedTags = (form) => form.querySelectorAll('input[data-togglegroup="tags-manage"][data-toggle="slave"]:checked'); +const getCheckedTags = (root) => root.querySelectorAll('[data-togglegroup="report-select-all"][data-toggle="slave"]:checked'); const handleCombineRequest = async(tagManagementCombine) => { const pendingPromise = new Pending('core/tag:tag-management-combine'); const form = tagManagementCombine.closest('form'); - const checkedTags = getCheckedTags(form); + + const reportElement = document.querySelector(reportSelectors.regions.report); + const checkedTags = getCheckedTags(reportElement); if (checkedTags.length <= 1) { // We need at least 2 tags to combine them. @@ -86,6 +86,13 @@ const handleCombineRequest = async(tagManagementCombine) => { tempElement.name = tagManagementCombine.name; form.append(tempElement); + // Append selected tags element. + const tagsElement = document.createElement('input'); + tagsElement.hidden = true; + tagsElement.name = 'tagschecked'; + tagsElement.value = [...checkedTags].map(check => check.value).join(','); + form.append(tagsElement); + // Get the selected tag from the modal. var maintag = $('input[name=maintag]:checked', '#combinetags_form').val(); // Append this in the tags list form. @@ -157,8 +164,12 @@ const addStandardTags = async() => { pendingPromise.resolve(); }; -const deleteSelectedTags = async(form) => { - const checkedTags = getCheckedTags(form); +const deleteSelectedTags = async(bulkActionDeleteButton) => { + const form = bulkActionDeleteButton.closest('form'); + + const reportElement = document.querySelector(reportSelectors.regions.report); + const checkedTags = getCheckedTags(reportElement); + if (!checkedTags.length) { return; } @@ -174,8 +185,16 @@ const deleteSelectedTags = async(form) => { // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!? const tempElement = document.createElement('input'); tempElement.hidden = true; - tempElement.name = 'bulkdelete'; + tempElement.name = bulkActionDeleteButton.name; form.append(tempElement); + + // Append selected tags element. + const tagsElement = document.createElement('input'); + tagsElement.hidden = true; + tagsElement.name = 'tagschecked'; + tagsElement.value = [...checkedTags].map(check => check.value).join(','); + form.append(tagsElement); + form.submit(); } catch { return; @@ -303,37 +322,10 @@ export const initTagindexPage = async() => { * @method initManagePage */ export const initManagePage = () => { - // Set cell 'time modified' to 'now' when any of the element is updated in this row. - $('body').on('updated', '[data-inplaceeditable]', function(e) { - var pendingPromise = new Pending('core/tag:initManagePage'); - - getStrings([ - { - key: 'selecttag', - component: 'core_tag', - }, - { - key: 'now', - component: 'core', - }, - ]) - .then(function(result) { - $('label[for="tagselect' + e.ajaxreturn.itemid + '"]').html(result[0]); - $(e.target).closest('tr').find('td.col-timemodified').html(result[1]); - - return; - }) - .always(pendingPromise.resolve) - .catch(Notification.exception); - - if (e.ajaxreturn.itemtype === 'tagflag') { - var row = $(e.target).closest('tr'); - if (e.ajaxreturn.value === '0') { - row.removeClass('table-warning'); - } else { - row.addClass('table-warning'); - } - } + // Toggle row class when updating flag. + $('body').on('updated', '[data-inplaceeditable][data-itemtype=tagflag]', function(e) { + var row = $(e.target).closest('tr'); + row.toggleClass('table-warning', e.ajaxreturn.value === '1'); }); // Confirmation for bulk tag combine button. @@ -344,7 +336,7 @@ export const initManagePage = () => { handleCombineRequest(tagManagementCombine); } - if (e.target.closest('a[data-action="addstandardtag"]')) { + if (e.target.closest('[data-action="addstandardtag"]')) { e.preventDefault(); addStandardTags(); } @@ -352,7 +344,7 @@ export const initManagePage = () => { const bulkActionDeleteButton = e.target.closest('#tag-management-delete'); if (bulkActionDeleteButton) { e.preventDefault(); - deleteSelectedTags(bulkActionDeleteButton.closest('form')); + deleteSelectedTags(bulkActionDeleteButton); } const rowDeleteButton = e.target.closest('.tagdelete'); diff --git a/mod/book/tests/behat/edit_tags.feature b/mod/book/tests/behat/edit_tags.feature index ac3a03d5f85..7e1f3f14427 100644 --- a/mod/book/tests/behat/edit_tags.feature +++ b/mod/book/tests/behat/edit_tags.feature @@ -40,13 +40,11 @@ Feature: Edited book chapters handle tags correctly @javascript Scenario: Book chapter edition of standard tags works as expected - Given I log in as "admin" - And I change window size to "large" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I follow "Add standard tags" - And I set the field "Enter comma-separated list of new tags" to "OT1, OT2, OT3" - And I press "Continue" + Given the following "tags" exist: + | name | isstandard | + | OT1 | 1 | + | OT2 | 1 | + | OT3 | 1 | And I am on the "Test book" "book activity" page logged in as teacher1 And I open the autocomplete suggestions list And I should see "OT1" in the ".form-autocomplete-suggestions" "css_element" diff --git a/mod/forum/tests/behat/edit_tags.feature b/mod/forum/tests/behat/edit_tags.feature index 83a6d60d5a0..50620a92808 100644 --- a/mod/forum/tests/behat/edit_tags.feature +++ b/mod/forum/tests/behat/edit_tags.feature @@ -38,13 +38,11 @@ Feature: Edited forum posts handle tags correctly @javascript Scenario: Forum post edition of standard tags works as expected - Given I log in as "admin" - And I change window size to "large" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I follow "Add standard tags" - And I set the field "Enter comma-separated list of new tags" to "OT1, OT2, OT3" - And I press "Continue" + Given the following "tags" exist: + | name | isstandard | + | OT1 | 1 | + | OT2 | 1 | + | OT3 | 1 | And I am on the "Test forum name" "forum activity" page logged in as teacher1 And I click on "Add discussion topic" "link" And I click on "Advanced" "button" diff --git a/mod/glossary/tests/behat/edit_tags.feature b/mod/glossary/tests/behat/edit_tags.feature index 59b2a1a850e..86458963948 100644 --- a/mod/glossary/tests/behat/edit_tags.feature +++ b/mod/glossary/tests/behat/edit_tags.feature @@ -40,15 +40,12 @@ Feature: Edited glossary entries handle tags correctly Then I should see "Cool" in the ".form-autocomplete-selection" "css_element" Scenario: Glossary entry edition of standard tags works as expected - Given I log in as "admin" - And I change window size to "large" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I follow "Add standard tags" - And I set the field "Enter comma-separated list of new tags" to "OT1, OT2, OT3" - And I press "Continue" - And I log out - Given I am on the "Test glossary" "glossary activity" page logged in as teacher1 + Given the following "tags" exist: + | name | isstandard | + | OT1 | 1 | + | OT2 | 1 | + | OT3 | 1 | + And I am on the "Test glossary" "glossary activity" page logged in as teacher1 And I press "Add entry" And I expand all fieldsets And I open the autocomplete suggestions list diff --git a/mod/wiki/tests/behat/edit_tags.feature b/mod/wiki/tests/behat/edit_tags.feature index 496c2754a90..2017784615d 100644 --- a/mod/wiki/tests/behat/edit_tags.feature +++ b/mod/wiki/tests/behat/edit_tags.feature @@ -41,15 +41,12 @@ Feature: Edited wiki pages handle tags correctly @javascript Scenario: Wiki page edition of standard tags works as expected - Given I log in as "admin" - And I change window size to "large" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I follow "Add standard tags" - And I set the field "Enter comma-separated list of new tags" to "OT1, OT2, OT3" - And I press "Continue" - And I log out - Given I am on the "Test wiki name" "wiki activity" page logged in as student1 + Given the following "tags" exist: + | name | isstandard | + | OT1 | 1 | + | OT2 | 1 | + | OT3 | 1 | + And I am on the "Test wiki name" "wiki activity" page logged in as student1 And I press "Create page" And I open the autocomplete suggestions list And I should see "OT1" in the ".form-autocomplete-suggestions" "css_element" diff --git a/tag/classes/manage_table.php b/tag/classes/manage_table.php index 872c09234da..ce636c7510a 100644 --- a/tag/classes/manage_table.php +++ b/tag/classes/manage_table.php @@ -26,6 +26,7 @@ require_once($CFG->libdir . '/tablelib.php'); * @package core_tag * @copyright 2015 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @deprecated since Moodle 4.4 */ class core_tag_manage_table extends table_sql { @@ -43,11 +44,13 @@ class core_tag_manage_table extends table_sql { public function __construct($tagcollid) { global $USER, $PAGE, $OUTPUT; + debugging('core_tag_manage_table is deprecated, please use the new system report', DEBUG_DEVELOPER); + parent::__construct('tag-management-list-'.$USER->id); $this->tagcollid = $tagcollid; - $perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); + $perpage = optional_param('perpage', 30, PARAM_INT); $page = optional_param('page', 0, PARAM_INT); $filter = optional_param('filter', '', PARAM_NOTAGS); $baseurl = new moodle_url('/tag/manage.php', array('tc' => $tagcollid, diff --git a/tag/classes/reportbuilder/local/systemreports/tags.php b/tag/classes/reportbuilder/local/systemreports/tags.php new file mode 100644 index 00000000000..5217a68cbb2 --- /dev/null +++ b/tag/classes/reportbuilder/local/systemreports/tags.php @@ -0,0 +1,220 @@ +. + +declare(strict_types=1); + +namespace core_tag\reportbuilder\local\systemreports; + +use core\context\system; +use core_reportbuilder\local\entities\user; +use core_reportbuilder\local\report\{action, column}; +use core_reportbuilder\system_report; +use core_tag\output\{tagflag, tagisstandard, tagname}; +use core_tag\reportbuilder\local\entities\tag; +use lang_string; +use moodle_url; +use pix_icon; +use stdClass; + +/** + * Tags collection system report + * + * @package core_tag + * @copyright 2023 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tags extends system_report { + + /** + * Report initialisation + */ + protected function initialise(): void { + $tag = new tag(); + $this->add_entity($tag); + + $tagalias = $tag->get_table_alias('tag'); + $this->set_main_table('tag', $tagalias); + + // Base fields required for various callbacks. + $this->add_base_fields("{$tagalias}.id, {$tagalias}.rawname, {$tagalias}.flag, {$tagalias}.tagcollid"); + $this->set_checkbox_toggleall(static function(stdClass $tag): array { + return [$tag->id, get_string('selecttag', 'core_tag', $tag->rawname)]; + }); + + // Limit tags to current collection. + $this->add_base_condition_simple("{$tagalias}.tagcollid", $this->get_parameter('collection', 0, PARAM_INT)); + + // Join the user entity to represent the tag author. + $user = new user(); + $useralias = $user->get_table_alias('user'); + $this->add_entity($user->add_join("LEFT JOIN {user} {$useralias} ON {$useralias}.id = {$tagalias}.userid")); + + $this->add_columns($tag); + $this->add_filters(); + $this->add_actions(); + + $this->set_downloadable(false); + } + + /** + * Report access + * + * @return bool + */ + protected function can_view(): bool { + global $CFG; + + return !empty($CFG->usetags) && has_capability('moodle/tag:manage', system::instance()); + } + + /** + * Report columns + * + * @param tag $tag + */ + public function add_columns(tag $tag): void { + $tagentity = $tag->get_entity_name(); + $tagalias = $tag->get_table_alias('tag'); + + // Name (editable). + $this->add_column((new column( + 'nameeditable', + new lang_string('name', 'core_tag'), + $tagentity, + )) + ->add_fields("{$tagalias}.name, {$tagalias}.rawname, {$tagalias}.tagcollid, {$tagalias}.id") + ->set_type(column::TYPE_TEXT) + ->set_is_sortable(true) + ->set_callback(static function(string $name, stdClass $tag): string { + global $PAGE; + $editable = new tagname($tag); + return $editable->render($PAGE->get_renderer('core')); + }) + ); + + // User. + $this->add_column_from_entity('user:fullnamewithlink'); + + // Count (TODO MDL-76392 use native entity aggregation). + $this->add_column((new column( + 'instancecount', + new lang_string('count', 'core_tag'), + $tagentity, + )) + ->add_field("(SELECT COUNT(*) FROM {tag_instance} WHERE tagid = {$tagalias}.id)", 'instancecount') + ->set_type(column::TYPE_INTEGER) + ->set_is_sortable(true) + ); + + // Flag (editable). + $this->add_column((new column( + 'flageditable', + new lang_string('flag', 'core_tag'), + $tagentity, + )) + ->add_fields("{$tagalias}.flag, {$tagalias}.id") + ->set_type(column::TYPE_BOOLEAN) + ->set_is_sortable(true) + ->set_callback(static function(bool $flag, stdClass $tag): string { + global $PAGE; + $editable = new tagflag($tag); + return $editable->render($PAGE->get_renderer('core')); + }) + ); + + // Time modified. + $this->add_column_from_entity('tag:timemodified') + ->set_callback(fn($timemodified) => format_time(time() - $timemodified)); + + // Standard (editable). + $this->add_column((new column( + 'standardeditable', + new lang_string('standardtag', 'core_tag'), + $tagentity, + )) + ->add_fields("{$tagalias}.isstandard, {$tagalias}.id") + ->set_type(column::TYPE_BOOLEAN) + ->set_is_sortable(true) + ->set_callback(static function(bool $standard, stdClass $tag): string { + global $PAGE; + $editable = new tagisstandard($tag); + return $editable->render($PAGE->get_renderer('core')); + }) + ); + + $this->set_initial_sort_column('tag:flageditable', SORT_DESC); + } + + /** + * Report filters + */ + protected function add_filters(): void { + $this->add_filters_from_entities([ + 'tag:name', + 'tag:standard', + 'tag:flagged', + ]); + } + + /** + * Report actions + */ + protected function add_actions(): void { + + // Edit. + $this->add_action((new action( + new moodle_url('/tag/edit.php', [ + 'id' => ':id', + 'returnurl' => ':returnurl', + ]), + new pix_icon('t/edit', ''), + [], + false, + new lang_string('edit'), + )) + ->add_callback(static function(stdClass $tag): bool { + $tag->returnurl = (new moodle_url('/tag/manage.php', ['tc' => $tag->tagcollid]))->out_as_local_url(false); + return true; + }) + ); + + // Delete. + $this->add_action(new action( + new moodle_url('/tag/manage.php', [ + 'tc' => ':tagcollid', + 'tagid' => ':id', + 'action' => 'delete', + 'sesskey' => sesskey(), + ]), + new pix_icon('t/delete', ''), + [ + 'class' => 'tagdelete text-danger', + ], + false, + new lang_string('delete'), + )); + } + + /** + * Report row class + * + * @param stdClass $row + * @return string + */ + public function get_row_class(stdClass $row): string { + return $row->flag ? 'table-warning' : ''; + } +} diff --git a/tag/manage.php b/tag/manage.php index e4165a80955..144160f44f7 100644 --- a/tag/manage.php +++ b/tag/manage.php @@ -23,36 +23,22 @@ */ require_once('../config.php'); -require_once($CFG->libdir.'/tablelib.php'); require_once('lib.php'); require_once($CFG->libdir.'/adminlib.php'); -define('SHOW_ALL_PAGE_SIZE', 50000); -define('DEFAULT_PAGE_SIZE', 30); +use core\context\system; +use core_reportbuilder\system_report_factory; +use core_tag\reportbuilder\local\systemreports\tags; -$tagschecked = optional_param_array('tagschecked', array(), PARAM_INT); $tagid = optional_param('tagid', null, PARAM_INT); $isstandard = optional_param('isstandard', null, PARAM_INT); $action = optional_param('action', '', PARAM_ALPHA); -$perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); -$page = optional_param('page', 0, PARAM_INT); $tagcollid = optional_param('tc', 0, PARAM_INT); -$tagareaid = optional_param('ta', null, PARAM_INT); -$filter = optional_param('filter', '', PARAM_NOTAGS); $params = array(); -if ($perpage != DEFAULT_PAGE_SIZE) { - $params['perpage'] = $perpage; -} -if ($page > 0) { - $params['page'] = $page; -} if ($tagcollid) { $params['tc'] = $tagcollid; } -if ($filter !== '') { - $params['filter'] = $filter; -} admin_externalpage_setup('managetags', '', $params, '', array('pagelayout' => 'report')); @@ -66,7 +52,6 @@ if ($tagid) { $tagcollid = $tagobject->tagcollid; } $tagcoll = core_tag_collection::get_by_id($tagcollid); -$tagarea = core_tag_area::get_by_id($tagareaid); $manageurl = new moodle_url('/tag/manage.php'); if ($tagcoll) { // We are inside a tag collection - add it to the breadcrumb. @@ -125,6 +110,7 @@ switch($action) { break; case 'bulk': + $tagschecked = explode(',', optional_param('tagschecked', '', PARAM_SEQUENCE)); if (optional_param('bulkdelete', null, PARAM_RAW) !== null) { if ($tagschecked) { require_sesskey(); @@ -206,70 +192,39 @@ if (!$tagcoll) { } // Tag collection is specified. Manage tags in this collection. -echo $OUTPUT->heading(core_tag_collection::display_name($tagcoll)); +echo html_writer::div( + $OUTPUT->heading(core_tag_collection::display_name($tagcoll)) . + html_writer::tag( + 'button', + $OUTPUT->pix_icon('t/add', '') . get_string('addotags', 'core_tag'), + [ + 'type' => 'button', + 'class' => 'btn btn-primary my-auto', + 'data-action' => 'addstandardtag', + ], + ), + 'd-flex justify-content-between mb-2', +); -$hiddenfields = [ - (object) ['type' => 'hidden', 'name' => 'tc', 'value' => $tagcollid], - (object) ['type' => 'hidden', 'name' => 'perpage', 'value' => $perpage] -]; +// Render the report. +$report = system_report_factory::create(tags::class, system::instance(), '', '', 0, ['collection' => $tagcoll->id]); +echo $report->output(); -$otherfields = ''; -if ($filter !== '') { - $otherfields = html_writer::link(new moodle_url($PAGE->url, ['filter' => null]), - get_string('resetfilter', 'tag')); -} - -$data = [ - 'action' => new moodle_url('/tag/manage.php'), - 'hiddenfields' => $hiddenfields, - 'inputname' => 'filter', - 'searchstring' => get_string('search'), - 'query' => s($filter), - 'extraclasses' => 'mb-2', - 'otherfields' => $otherfields -]; -echo $OUTPUT->render_from_template('core/search_input', $data); - -// Link to add an standard tags. -$img = $OUTPUT->pix_icon('t/add', ''); -echo '
' . - html_writer::link('#', $img . get_string('addotags', 'tag'), array('data-action' => 'addstandardtag')) . - '
'; - -$table = new core_tag_manage_table($tagcollid); -echo '
'; -echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'tc', 'value' => $tagcollid)); -echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); -echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'bulk')); -echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'perpage', 'value' => $perpage)); -echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'page', 'value' => $page)); -echo $table->out($perpage, true); - -if ($table->rawdata) { - echo html_writer::start_tag('p'); +// Render bulk actions. +if ($DB->record_exists('tag', [])) { + echo ''; + echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'tc', 'value' => $tagcoll->id]); + echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); + echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'action', 'value' => 'bulk']); echo html_writer::tag('button', get_string('deleteselected', 'tag'), array('id' => 'tag-management-delete', 'type' => 'submit', 'class' => 'tagdeleteselected btn btn-secondary', 'name' => 'bulkdelete')); echo html_writer::tag('button', get_string('combineselected', 'tag'), array('id' => 'tag-management-combine', 'type' => 'submit', 'class' => 'tagcombineselected btn btn-secondary', 'name' => 'bulkcombine')); - echo html_writer::end_tag('p'); + echo '
'; } -echo ''; -$totalcount = $table->totalcount; -if ($perpage == SHOW_ALL_PAGE_SIZE) { - echo html_writer::start_tag('div', array('id' => 'showall')); - $params = array('perpage' => DEFAULT_PAGE_SIZE, 'page' => 0); - $url = new moodle_url($PAGE->url, $params); - echo html_writer::link($url, get_string('showperpage', '', DEFAULT_PAGE_SIZE)); - echo html_writer::end_tag('div'); -} else if ($totalcount > 0 and $perpage < $totalcount) { - echo html_writer::start_tag('div', array('id' => 'showall')); - $params = array('perpage' => SHOW_ALL_PAGE_SIZE, 'page' => 0); - $url = new moodle_url($PAGE->url, $params); - echo html_writer::link($url, get_string('showall', '', $totalcount)); - echo html_writer::end_tag('div'); -} +$PAGE->requires->js_call_amd('core/tag', 'initManagePage'); echo $OUTPUT->footer(); diff --git a/tag/tests/behat/delete_tag.feature b/tag/tests/behat/delete_tag.feature index 0da6773cbf2..008313aa680 100644 --- a/tag/tests/behat/delete_tag.feature +++ b/tag/tests/behat/delete_tag.feature @@ -29,34 +29,17 @@ Feature: Manager is able to delete tags And I should not see "Dog" And I log out - Scenario: Deleting multiple tags with javascript disabled - When I log in as "manager1" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I set the following fields to these values: - | Select tag Dog | 1 | - | Select tag Neverusedtag | 1 | - And I press "Delete selected" - Then I should see "Tag(s) deleted" - And I should not see "Dog" - And I should not see "Neverusedtag" - And I follow "Cat" - And I follow "User 1" - And I should see "Cat" - And I should not see "Dog" - And I log out - @javascript Scenario: Deleting a tag with javascript enabled When I log in as "manager1" And I navigate to "Appearance > Manage tags" in site administration And I follow "Default collection" - And I click on "Delete" "link" in the "Turtle" "table_row" + And I press "Delete" action in the "Turtle" report row Then I should see "Are you sure you want to delete this tag?" And I click on "Cancel" "button" in the "Delete" "dialogue" And I should not see "Tag(s) deleted" And I should see "Turtle" - And I click on "Delete" "link" in the "Dog" "table_row" + And I press "Delete" action in the "Dog" report row And I should see "Are you sure you want to delete this tag?" And I press "Yes" And I should see "Tag(s) deleted" diff --git a/tag/tests/behat/edit_tag.feature b/tag/tests/behat/edit_tag.feature index c9408b22be5..84d2ace856a 100644 --- a/tag/tests/behat/edit_tag.feature +++ b/tag/tests/behat/edit_tag.feature @@ -120,7 +120,7 @@ Feature: Users can edit tags to add description or rename When I log in as "manager1" And I navigate to "Appearance > Manage tags" in site administration And I follow "Default collection" - And I click on "Edit this tag" "link" in the "Cat" "table_row" + And I press "Edit" action in the "Cat" report row And I set the following fields to these values: | Tag name | Kitten | | Description | Description of tag 1 | @@ -138,7 +138,7 @@ Feature: Users can edit tags to add description or rename When I log in as "manager1" And I navigate to "Appearance > Manage tags" in site administration And I follow "Default collection" - And I click on "Edit this tag" "link" in the "Cat" "table_row" + And I press "Edit" action in the "Cat" report row And I set the following fields to these values: | Tag name | DOG | And I press "Update" @@ -146,7 +146,7 @@ Feature: Users can edit tags to add description or rename And I set the following fields to these values: | Tag name | Kitten | And I press "Update" - And I click on "Edit this tag" "link" in the "Kitten" "table_row" + And I press "Edit" action in the "Kitten" report row And I set the following fields to these values: | Tag name | KITTEN | And I press "Update" @@ -160,10 +160,10 @@ Feature: Users can edit tags to add description or rename And I follow "Default collection" # Renaming tag to a valid name And I set the field "Edit tag name" in the "Cat" "table_row" to "Kitten" - Then I should not see "Cat" - And "New name for tag" "field" should not exist - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" + Then the following should not exist in the "reportbuilder-table" table: + | First name | Tag name | + | Admin User | Cat | + And I reload the page And I should see "Kitten" And I should not see "Cat" # Renaming tag to an invalid name @@ -174,22 +174,6 @@ Feature: Users can edit tags to add description or rename And I should see "Turtle" And I should see "Dog" And I should not see "DOG" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I should see "Turtle" - And I should see "Dog" - And I should not see "DOG" - # Cancel tag renaming - And I click on "Edit tag name" "link" in the "Dog" "table_row" - And I type "Penguin" - And I press the escape key - And "New name for tag" "field" should not exist - And I should see "Turtle" - And I should not see "Penguin" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And I should see "Turtle" - And I should not see "Penguin" @javascript Scenario: Combining tags when renaming @@ -238,18 +222,17 @@ Feature: Users can edit tags to add description or rename And I should see "Turtle" And I should not see "Neverusedtag" + @javascript Scenario: Filtering tags When I log in as "manager1" And I navigate to "Appearance > Manage tags" in site administration And I follow "Default collection" - And I should not see "Reset filter" - And I set the field "Search" to "t" - And I press "Search" - Then the field "Search" matches value "t" - And I should not see "Dog" - And I should see "Cat" - And I should see "Turtle" - And I follow "Reset filter" - And I should see "Dog" - And I should see "Cat" - And I should see "Turtle" + And I click on "Filters" "button" + And I set the following fields in the "Tag name" "core_reportbuilder > Filter" to these values: + | Tag name operator | Is equal to | + | Tag name value | Cat,Dog | + And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element" + Then I should see "Cat" in the "reportbuilder-table" "table" + And I should see "Dog" in the "reportbuilder-table" "table" + And I should not see "Turtle" in the "reportbuilder-table" "table" + And I should not see "Neverusedtag" in the "reportbuilder-table" "table" diff --git a/tag/tests/behat/flag_tags.feature b/tag/tests/behat/flag_tags.feature index bc8348fcf5a..aa062977f3b 100644 --- a/tag/tests/behat/flag_tags.feature +++ b/tag/tests/behat/flag_tags.feature @@ -69,25 +69,35 @@ Feature: Users can flag tags and manager can reset flags And I follow "Default collection" Then "Sweartag" "link" should appear before "Badtag" "link" And "Badtag" "link" should appear before "Nicetag" "link" - And "(2)" "text" should exist in the "//tr[contains(.,'Sweartag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(1)" "text" should exist in the "//tr[contains(.,'Badtag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(" "text" should not exist in the "//tr[contains(.,'Nicetag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(" "text" should not exist in the "//tr[contains(.,'Neverusedtag')]//td[contains(@class,'col-flag')]" "xpath_element" + And the following should exist in the "reportbuilder-table" table: + | Tag name | Flag | + | Sweartag | (2) | + | Badtag | (1) | + And the following should not exist in the "reportbuilder-table" table: + | Tag name | Flag | + | Nicetag | ( | + | Neverusertag | ( | And I click on "Reset flag" "link" in the "Sweartag" "table_row" And I click on "Reset flag" "link" in the "Badtag" "table_row" And I wait until "//tr[contains(.,'Sweartag')]//a[contains(@title,'Flag as inappropriate')]" "xpath_element" exists And I click on "Flag as inappropriate" "link" in the "Sweartag" "table_row" And I click on "Flag as inappropriate" "link" in the "Nicetag" "table_row" - And "(1)" "text" should exist in the "//tr[contains(.,'Sweartag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(1)" "text" should exist in the "//tr[contains(.,'Nicetag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(" "text" should not exist in the "//tr[contains(.,'Badtag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(" "text" should not exist in the "//tr[contains(.,'Neverusedtag')]//td[contains(@class,'col-flag')]" "xpath_element" - And I navigate to "Appearance > Manage tags" in site administration - And I follow "Default collection" - And "Nicetag" "link" should appear before "Sweartag" "link" + And the following should exist in the "reportbuilder-table" table: + | Tag name | Flag | + | Sweartag | (1) | + | Nicetag | (1) | + And the following should not exist in the "reportbuilder-table" table: + | Tag name | Flag | + | Badtag | ( | + | Neverusertag | ( | + And I reload the page + And "Nicetag" "link" should appear before "Neverusedtag" "link" And "Sweartag" "link" should appear before "Badtag" "link" - And "(1)" "text" should exist in the "//tr[contains(.,'Sweartag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(1)" "text" should exist in the "//tr[contains(.,'Nicetag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(" "text" should not exist in the "//tr[contains(.,'Badtag')]//td[contains(@class,'col-flag')]" "xpath_element" - And "(" "text" should not exist in the "//tr[contains(.,'Neverusedtag')]//td[contains(@class,'col-flag')]" "xpath_element" - And I log out + And the following should exist in the "reportbuilder-table" table: + | Tag name | Flag | + | Sweartag | (1) | + | Nicetag | (1) | + And the following should not exist in the "reportbuilder-table" table: + | Tag name | Flag | + | Badtag | ( | + | Neverusertag | ( | diff --git a/tag/tests/behat/standard_tags.feature b/tag/tests/behat/standard_tags.feature index dbc7e98bb0f..16c816205ee 100644 --- a/tag/tests/behat/standard_tags.feature +++ b/tag/tests/behat/standard_tags.feature @@ -28,7 +28,7 @@ Feature: Manager can add standard tags and change the tag type of existing tags And "Make standard" "link" should exist in the "Tag1" "table_row" And "Make standard" "link" should exist in the "Tag2" "table_row" And "Remove from standard tags" "link" should exist in the "Tag3" "table_row" - And I follow "Add standard tags" + And I click on "Add standard tags" "button" And I set the field "Enter comma-separated list of new tags" to "Tag1,TAG2,Tag3,Tag4,Tag5" And I press "Continue" And I should see "Standard tag(s) added" @@ -75,12 +75,12 @@ Feature: Manager can add standard tags and change the tag type of existing tags When I log in as "manager1" And I navigate to "Appearance > Manage tags" in site administration And I follow "Default collection" - And I click on "Edit this tag" "link" in the "Tag1" "table_row" + And I press "Edit" action in the "Tag1" report row And I set the following fields to these values: | Standard | 1 | And I press "Update" Then "Remove from standard tags" "link" should exist in the "Tag1" "table_row" - And I click on "Edit this tag" "link" in the "Tag1" "table_row" + And I press "Edit" action in the "Tag1" report row And I set the following fields to these values: | Standard | 0 | And I press "Update" diff --git a/tag/upgrade.txt b/tag/upgrade.txt index 9ff30c0e2b8..b96213e2552 100644 --- a/tag/upgrade.txt +++ b/tag/upgrade.txt @@ -5,6 +5,7 @@ here is intended especially for developers. * New additional property 'viewurl' has been added to the pre-defined structure in tag_item_exporter. This property represents the URL to view a given tag. +* The `core_tag_manage_table` class has been deprecated, in favour of new report builder implementation === 3.6 ===