diff --git a/contentbank/amd/build/search.min.js b/contentbank/amd/build/search.min.js index 9090c9bca4f39..16c80b09dfd88 100644 --- a/contentbank/amd/build/search.min.js +++ b/contentbank/amd/build/search.min.js @@ -1,2 +1,2 @@ -define ("core_contentbank/search",["exports","jquery","core_contentbank/selectors","core/str","core/pending","core/utils"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);e=g(e);function g(a){return a&&a.__esModule?a:{default:a}}function h(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=function(){var a=new e.default,d=(0,b.default)(c.default.elements.main);k(d);a.resolve()};a.init=j;var k=function(a){var b=a.find(c.default.elements.searchinput)[0];a.on("click",c.default.actions.search,function(c){c.preventDefault();l(a,b.value)});a.on("click",c.default.actions.clearSearch,function(c){c.preventDefault();b.value="";b.focus();l(a,b.value)});b.addEventListener("input",(0,f.debounce)(function(){l(a,b.value)},300))},l=function(){var a=i(regeneratorRuntime.mark(function a(b,e){var f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=b.find(c.default.elements.clearsearch)[0];g=b.find(c.default.elements.searchicon)[0];h=b.find(c.default.elements.cbnavbarbreadcrumb)[0];i=b.find(c.default.elements.cbnavbartotalsearch)[0];j=m(b,e);if(!(0"+a.substr(d,b.length)+""+a.substr(d+b.length)}}return c}}); +define ("core_contentbank/search",["exports","jquery","core_contentbank/selectors","core/str","core/pending","core/utils"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);e=g(e);function g(a){return a&&a.__esModule?a:{default:a}}function h(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=function(){var a=new e.default,d=(0,b.default)(c.default.regions.contentbank);k(d);a.resolve()};a.init=j;var k=function(a){var b=a.find(c.default.elements.searchinput)[0];a.on("click",c.default.actions.search,function(c){c.preventDefault();l(a,b.value)});a.on("click",c.default.actions.clearSearch,function(c){c.preventDefault();b.value="";b.focus();l(a,b.value)});b.addEventListener("input",(0,f.debounce)(function(){l(a,b.value)},300))},l=function(){var a=i(regeneratorRuntime.mark(function a(b,e){var f,g,h,i,j;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=b.find(c.default.elements.clearsearch)[0];g=b.find(c.default.elements.searchicon)[0];h=b.find(c.default.elements.cbnavbarbreadcrumb)[0];i=b.find(c.default.elements.cbnavbartotalsearch)[0];j=m(b,e);if(!(0"+a.substr(d,b.length)+""+a.substr(d+b.length)}}return c}}); //# sourceMappingURL=search.min.js.map diff --git a/contentbank/amd/build/search.min.js.map b/contentbank/amd/build/search.min.js.map index a3ddca13075dd..0e0625892fe71 100644 --- a/contentbank/amd/build/search.min.js.map +++ b/contentbank/amd/build/search.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/search.js"],"names":["init","pendingPromise","Pending","root","selectors","elements","main","registerListenerEvents","resolve","searchInput","find","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","body","searchQuery","clearSearchButton","clearsearch","searchIcon","searchicon","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","add","remove","innerHTML","searchTerm","contents","Array","from","cbfile","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","contentNameElement","querySelector","regions","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"mappings":"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","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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.elements.main);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.elements.clearsearch)[0];\n const searchIcon = body.find(selectors.elements.searchicon)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n searchIcon.classList.add('d-none');\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n searchIcon.classList.remove('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.cbfile));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-file');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"file":"search.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/search.js"],"names":["init","pendingPromise","Pending","root","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","body","searchQuery","clearSearchButton","clearsearch","searchIcon","searchicon","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","add","remove","innerHTML","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","contentNameElement","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"mappings":"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","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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.elements.clearsearch)[0];\n const searchIcon = body.find(selectors.elements.searchicon)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n searchIcon.classList.add('d-none');\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n searchIcon.classList.remove('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"file":"search.min.js"} \ No newline at end of file diff --git a/contentbank/amd/build/selectors.min.js b/contentbank/amd/build/selectors.min.js index c7322b9e47937..35b476aa37875 100644 --- a/contentbank/amd/build/selectors.min.js +++ b/contentbank/amd/build/selectors.min.js @@ -1,2 +1,2 @@ -define ("core_contentbank/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{cbcontentname:b("region","cb-content-name")},actions:{search:b("action","searchcontent"),clearSearch:b("action","clearsearchcontent")},elements:{cbfile:".cb-file",cbnavbarbreadcrumb:".cb-navbar-breadbrumb",cbnavbartotalsearch:".cb-navbar-totalsearch",clearsearch:".input-group-append .clear-icon",main:"#region-main",searchicon:".input-group-append .search-icon",searchinput:"#searchinput"}};a.default=c;return a.default}); +define ("core_contentbank/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{cbcontentname:b("region","cb-content-name"),contentbank:b("region","contentbank"),filearea:b("region","filearea")},actions:{search:b("action","searchcontent"),clearSearch:b("action","clearsearchcontent"),viewgrid:b("action","viewgrid"),viewlist:b("action","viewlist"),sortname:b("action","sortname"),sortdate:b("action","sortdate"),sortsize:b("action","sortsize"),sorttype:b("action","sorttype")},elements:{listitem:".cb-listitem",cbnavbarbreadcrumb:".cb-navbar-breadbrumb",cbnavbartotalsearch:".cb-navbar-totalsearch",clearsearch:".input-group-append .clear-icon",searchicon:".input-group-append .search-icon",searchinput:"#searchinput",sortbutton:".cb-btnsort"}};a.default=c;return a.default}); //# sourceMappingURL=selectors.min.js.map diff --git a/contentbank/amd/build/selectors.min.js.map b/contentbank/amd/build/selectors.min.js.map index 99b3b56554eeb..36d959f516719 100644 --- a/contentbank/amd/build/selectors.min.js.map +++ b/contentbank/amd/build/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/selectors.js"],"names":["getDataSelector","name","value","regions","cbcontentname","actions","search","clearSearch","elements","cbfile","cbnavbarbreadcrumb","cbnavbartotalsearch","clearsearch","main","searchicon","searchinput"],"mappings":"+IAgCMA,CAAAA,CAAe,CAAG,SAACC,CAAD,CAAOC,CAAP,CAAiB,CACrC,sBAAgBD,CAAhB,eAAyBC,CAAzB,OACH,C,GAEc,CACXC,OAAO,CAAE,CACLC,aAAa,CAAEJ,CAAe,CAAC,QAAD,CAAW,iBAAX,CADzB,CADE,CAIXK,OAAO,CAAE,CACLC,MAAM,CAAEN,CAAe,CAAC,QAAD,CAAW,eAAX,CADlB,CAELO,WAAW,CAAEP,CAAe,CAAC,QAAD,CAAW,oBAAX,CAFvB,CAJE,CAQXQ,QAAQ,CAAE,CACNC,MAAM,CAAE,UADF,CAENC,kBAAkB,CAAE,uBAFd,CAGNC,mBAAmB,CAAE,wBAHf,CAINC,WAAW,CAAE,iCAJP,CAKNC,IAAI,CAAE,cALA,CAMNC,UAAU,CAAE,kCANN,CAONC,WAAW,CAAE,cAPP,CARC,C","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 * Define all of the selectors we will be using on the contentbank interface.\n *\n * @module core_contentbank/selectors\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n cbcontentname: getDataSelector('region', 'cb-content-name'),\n },\n actions: {\n search: getDataSelector('action', 'searchcontent'),\n clearSearch: getDataSelector('action', 'clearsearchcontent'),\n },\n elements: {\n cbfile: '.cb-file',\n cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',\n cbnavbartotalsearch: '.cb-navbar-totalsearch',\n clearsearch: '.input-group-append .clear-icon',\n main: '#region-main',\n searchicon: '.input-group-append .search-icon',\n searchinput: '#searchinput',\n },\n};\n"],"file":"selectors.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/selectors.js"],"names":["getDataSelector","name","value","regions","cbcontentname","contentbank","filearea","actions","search","clearSearch","viewgrid","viewlist","sortname","sortdate","sortsize","sorttype","elements","listitem","cbnavbarbreadcrumb","cbnavbartotalsearch","clearsearch","searchicon","searchinput","sortbutton"],"mappings":"+IAgCMA,CAAAA,CAAe,CAAG,SAACC,CAAD,CAAOC,CAAP,CAAiB,CACrC,sBAAgBD,CAAhB,eAAyBC,CAAzB,OACH,C,GAEc,CACXC,OAAO,CAAE,CACLC,aAAa,CAAEJ,CAAe,CAAC,QAAD,CAAW,iBAAX,CADzB,CAELK,WAAW,CAAEL,CAAe,CAAC,QAAD,CAAW,aAAX,CAFvB,CAGLM,QAAQ,CAAEN,CAAe,CAAC,QAAD,CAAW,UAAX,CAHpB,CADE,CAMXO,OAAO,CAAE,CACLC,MAAM,CAAER,CAAe,CAAC,QAAD,CAAW,eAAX,CADlB,CAELS,WAAW,CAAET,CAAe,CAAC,QAAD,CAAW,oBAAX,CAFvB,CAGLU,QAAQ,CAAEV,CAAe,CAAC,QAAD,CAAW,UAAX,CAHpB,CAILW,QAAQ,CAAEX,CAAe,CAAC,QAAD,CAAW,UAAX,CAJpB,CAKLY,QAAQ,CAAEZ,CAAe,CAAC,QAAD,CAAW,UAAX,CALpB,CAMLa,QAAQ,CAAEb,CAAe,CAAC,QAAD,CAAW,UAAX,CANpB,CAOLc,QAAQ,CAAEd,CAAe,CAAC,QAAD,CAAW,UAAX,CAPpB,CAQLe,QAAQ,CAAEf,CAAe,CAAC,QAAD,CAAW,UAAX,CARpB,CANE,CAgBXgB,QAAQ,CAAE,CACNC,QAAQ,CAAE,cADJ,CAENC,kBAAkB,CAAE,uBAFd,CAGNC,mBAAmB,CAAE,wBAHf,CAINC,WAAW,CAAE,iCAJP,CAKNC,UAAU,CAAE,kCALN,CAMNC,WAAW,CAAE,cANP,CAONC,UAAU,CAAE,aAPN,CAhBC,C","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 * Define all of the selectors we will be using on the contentbank interface.\n *\n * @module core_contentbank/selectors\n * @package core_contentbank\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n cbcontentname: getDataSelector('region', 'cb-content-name'),\n contentbank: getDataSelector('region', 'contentbank'),\n filearea: getDataSelector('region', 'filearea')\n },\n actions: {\n search: getDataSelector('action', 'searchcontent'),\n clearSearch: getDataSelector('action', 'clearsearchcontent'),\n viewgrid: getDataSelector('action', 'viewgrid'),\n viewlist: getDataSelector('action', 'viewlist'),\n sortname: getDataSelector('action', 'sortname'),\n sortdate: getDataSelector('action', 'sortdate'),\n sortsize: getDataSelector('action', 'sortsize'),\n sorttype: getDataSelector('action', 'sorttype')\n },\n elements: {\n listitem: '.cb-listitem',\n cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',\n cbnavbartotalsearch: '.cb-navbar-totalsearch',\n clearsearch: '.input-group-append .clear-icon',\n searchicon: '.input-group-append .search-icon',\n searchinput: '#searchinput',\n sortbutton: '.cb-btnsort'\n },\n};\n"],"file":"selectors.min.js"} \ No newline at end of file diff --git a/contentbank/amd/build/sort.min.js b/contentbank/amd/build/sort.min.js new file mode 100644 index 0000000000000..b2785d18f0d84 --- /dev/null +++ b/contentbank/amd/build/sort.min.js @@ -0,0 +1,2 @@ +define ("core_contentbank/sort",["exports","core_contentbank/selectors","core/str","core/prefetch"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=e(b);d=e(d);function e(a){return a&&a.__esModule?a:{default:a}}var f=function(){var a=document.querySelector(b.default.regions.contentbank);d.default.prefetchStrings("contentbank",["sortbyx","sortbyxreverse","contentname","lastmodified","size","type"]);g(a)};a.init=f;var g=function(a){var c=document.querySelector(b.default.regions.filearea),d=c.querySelectorAll(b.default.elements.listitem),e=a.querySelector(b.default.actions.viewgrid),f=a.querySelector(b.default.actions.viewlist);e.addEventListener("click",function(){a.classList.remove("view-list");a.classList.add("view-grid");e.classList.add("active");f.classList.remove("active")});f.addEventListener("click",function(){a.classList.remove("view-grid");a.classList.add("view-list");f.classList.add("active");e.classList.remove("active")});var g=a.querySelector(b.default.actions.sortname);g.addEventListener("click",function(){var b=h(a,g);j(c,d,"data-file",b)});var i=a.querySelector(b.default.actions.sortdate);i.addEventListener("click",function(){var b=h(a,i);j(c,d,"data-timemodified",b)});var k=a.querySelector(b.default.actions.sortsize);k.addEventListener("click",function(){var b=h(a,k);j(c,d,"data-bytes",b)});var l=a.querySelector(b.default.actions.sorttype);l.addEventListener("click",function(){var b=h(a,l);j(c,d,"data-type",b)})},h=function(a,c){var d=a.querySelectorAll(b.default.elements.sortbutton);d.forEach(function(a){if(a!==c){a.classList.remove("dir-asc");a.classList.remove("dir-desc");a.classList.add("dir-none");i(a,!1)}});var e=!0;if(c.classList.contains("dir-none")){c.classList.remove("dir-none");c.classList.add("dir-asc")}else if(c.classList.contains("dir-asc")){c.classList.remove("dir-asc");c.classList.add("dir-desc");e=!1}else if(c.classList.contains("dir-desc")){c.classList.remove("dir-desc");c.classList.add("dir-asc")}i(c,e);return e},i=function(a,b){var d=b?"sortbyxreverse":"sortbyx";return(0,c.get_string)(a.dataset.string,"contentbank").then(function(a){return(0,c.get_string)(d,"core",a)}).then(function(b){a.setAttribute("title",b);return b}).catch()},j=function(a,b,c,d){var e=[].slice.call(b).sort(function(e,a){var b=e.getAttribute(c),f=a.getAttribute(c);if(!isNaN(b)){b=parseInt(b);f=parseInt(f)}if(d){return b>f?1:-1}else{return b.\n\n/**\n * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @package core_contentbank\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\n\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['sortbyx', 'sortbyxreverse', 'contentname',\n 'lastmodified', 'size', 'type']);\n registerListenerEvents(contentBank);\n};\n\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n // The search.\n const fileArea = document.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // The view buttons.\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n\n viewGrid.addEventListener('click', () => {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n });\n\n viewList.addEventListener('click', () => {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n });\n\n // Sort by file name alphabetical\n const sortByName = contentBank.querySelector(selectors.actions.sortname);\n sortByName.addEventListener('click', () => {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n });\n\n // Sort by date.\n const sortByDate = contentBank.querySelector(selectors.actions.sortdate);\n sortByDate.addEventListener('click', () => {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n });\n\n // Sort by size.\n const sortBySize = contentBank.querySelector(selectors.actions.sortsize);\n sortBySize.addEventListener('click', () => {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n });\n\n // Sort by type\n const sortByType = contentBank.querySelector(selectors.actions.sorttype);\n sortByType.addEventListener('click', () => {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n });\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute, the attribut to sort on\n * @param {Bool} ascending, Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(function (listItem) {\n fileArea.appendChild(listItem);\n });\n};"],"file":"sort.min.js"} \ No newline at end of file diff --git a/contentbank/amd/src/search.js b/contentbank/amd/src/search.js index bd5cb55c99766..e604abc037bc4 100644 --- a/contentbank/amd/src/search.js +++ b/contentbank/amd/src/search.js @@ -36,7 +36,7 @@ import {debounce} from 'core/utils'; export const init = () => { const pendingPromise = new Pending(); - const root = $(selectors.elements.main); + const root = $(selectors.regions.contentbank); registerListenerEvents(root); pendingPromise.resolve(); @@ -120,10 +120,10 @@ const toggleSearchResultsView = async(body, searchQuery) => { * @return {Array} */ const filterContents = (body, searchTerm) => { - const contents = Array.from(body.find(selectors.elements.cbfile)); + const contents = Array.from(body.find(selectors.elements.listitem)); const searchResults = []; contents.forEach((content) => { - const contentName = content.getAttribute('data-file'); + const contentName = content.getAttribute('data-name'); if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) { // The content matches the search criteria so it should be displayed and hightlighted. searchResults.push(content); diff --git a/contentbank/amd/src/selectors.js b/contentbank/amd/src/selectors.js index 080f85f98cec9..b8ca6a6875e42 100644 --- a/contentbank/amd/src/selectors.js +++ b/contentbank/amd/src/selectors.js @@ -37,18 +37,26 @@ const getDataSelector = (name, value) => { export default { regions: { cbcontentname: getDataSelector('region', 'cb-content-name'), + contentbank: getDataSelector('region', 'contentbank'), + filearea: getDataSelector('region', 'filearea') }, actions: { search: getDataSelector('action', 'searchcontent'), clearSearch: getDataSelector('action', 'clearsearchcontent'), + viewgrid: getDataSelector('action', 'viewgrid'), + viewlist: getDataSelector('action', 'viewlist'), + sortname: getDataSelector('action', 'sortname'), + sortdate: getDataSelector('action', 'sortdate'), + sortsize: getDataSelector('action', 'sortsize'), + sorttype: getDataSelector('action', 'sorttype') }, elements: { - cbfile: '.cb-file', + listitem: '.cb-listitem', cbnavbarbreadcrumb: '.cb-navbar-breadbrumb', cbnavbartotalsearch: '.cb-navbar-totalsearch', clearsearch: '.input-group-append .clear-icon', - main: '#region-main', searchicon: '.input-group-append .search-icon', searchinput: '#searchinput', + sortbutton: '.cb-btnsort' }, }; diff --git a/contentbank/amd/src/sort.js b/contentbank/amd/src/sort.js new file mode 100644 index 0000000000000..d76bd2f42c8e5 --- /dev/null +++ b/contentbank/amd/src/sort.js @@ -0,0 +1,193 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Content bank UI actions. + * + * @module core_contentbank/sort + * @package core_contentbank + * @copyright 2020 Bas Brands + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import selectors from 'core_contentbank/selectors'; +import {get_string as getString} from 'core/str'; +import Prefetch from 'core/prefetch'; + + +/** + * Set up the contentbank views. + * + * @method init + */ +export const init = () => { + const contentBank = document.querySelector(selectors.regions.contentbank); + Prefetch.prefetchStrings('contentbank', ['sortbyx', 'sortbyxreverse', 'contentname', + 'lastmodified', 'size', 'type']); + registerListenerEvents(contentBank); +}; + + +/** + * Register contentbank related event listeners. + * + * @method registerListenerEvents + * @param {HTMLElement} contentBank The DOM node of the content bank + */ +const registerListenerEvents = (contentBank) => { + + // The search. + const fileArea = document.querySelector(selectors.regions.filearea); + const shownItems = fileArea.querySelectorAll(selectors.elements.listitem); + + // The view buttons. + const viewGrid = contentBank.querySelector(selectors.actions.viewgrid); + const viewList = contentBank.querySelector(selectors.actions.viewlist); + + viewGrid.addEventListener('click', () => { + contentBank.classList.remove('view-list'); + contentBank.classList.add('view-grid'); + viewGrid.classList.add('active'); + viewList.classList.remove('active'); + }); + + viewList.addEventListener('click', () => { + contentBank.classList.remove('view-grid'); + contentBank.classList.add('view-list'); + viewList.classList.add('active'); + viewGrid.classList.remove('active'); + }); + + // Sort by file name alphabetical + const sortByName = contentBank.querySelector(selectors.actions.sortname); + sortByName.addEventListener('click', () => { + const ascending = updateSortButtons(contentBank, sortByName); + updateSortOrder(fileArea, shownItems, 'data-file', ascending); + }); + + // Sort by date. + const sortByDate = contentBank.querySelector(selectors.actions.sortdate); + sortByDate.addEventListener('click', () => { + const ascending = updateSortButtons(contentBank, sortByDate); + updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending); + }); + + // Sort by size. + const sortBySize = contentBank.querySelector(selectors.actions.sortsize); + sortBySize.addEventListener('click', () => { + const ascending = updateSortButtons(contentBank, sortBySize); + updateSortOrder(fileArea, shownItems, 'data-bytes', ascending); + }); + + // Sort by type + const sortByType = contentBank.querySelector(selectors.actions.sorttype); + sortByType.addEventListener('click', () => { + const ascending = updateSortButtons(contentBank, sortByType); + updateSortOrder(fileArea, shownItems, 'data-type', ascending); + }); +}; + +/** + * Update the sort button view. + * + * @method updateSortButtons + * @param {HTMLElement} contentBank The DOM node of the contentbank button + * @param {HTMLElement} sortButton The DOM node of the sort button + * @return {Bool} sort ascending + */ +const updateSortButtons = (contentBank, sortButton) => { + const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton); + + sortButtons.forEach((button) => { + if (button !== sortButton) { + button.classList.remove('dir-asc'); + button.classList.remove('dir-desc'); + button.classList.add('dir-none'); + + updateButtonTitle(button, false); + } + }); + + let ascending = true; + + if (sortButton.classList.contains('dir-none')) { + sortButton.classList.remove('dir-none'); + sortButton.classList.add('dir-asc'); + } else if (sortButton.classList.contains('dir-asc')) { + sortButton.classList.remove('dir-asc'); + sortButton.classList.add('dir-desc'); + ascending = false; + } else if (sortButton.classList.contains('dir-desc')) { + sortButton.classList.remove('dir-desc'); + sortButton.classList.add('dir-asc'); + } + + updateButtonTitle(sortButton, ascending); + + return ascending; +}; + +/** + * Update the button title. + * + * @method updateButtonTitle + * @param {HTMLElement} button Button to update + * @param {Bool} ascending Sort direction + * @return {Promise} string promise + */ +const updateButtonTitle = (button, ascending) => { + + const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx'); + + return getString(button.dataset.string, 'contentbank') + .then(columnName => { + return getString(sortString, 'core', columnName); + }) + .then(sortByString => { + button.setAttribute('title', sortByString); + return sortByString; + }) + .catch(); +}; + +/** + * Update the sort order of the itemlist and update the DOM + * + * @method updateSortOrder + * @param {HTMLElement} fileArea the Dom container for the itemlist + * @param {Array} itemList Nodelist of Dom elements + * @param {String} attribute, the attribut to sort on + * @param {Bool} ascending, Sort Ascending + */ +const updateSortOrder = (fileArea, itemList, attribute, ascending) => { + const sortList = [].slice.call(itemList).sort(function(a, b) { + + let aa = a.getAttribute(attribute); + let bb = b.getAttribute(attribute); + if (!isNaN(aa)) { + aa = parseInt(aa); + bb = parseInt(bb); + } + + if (ascending) { + return aa > bb ? 1 : -1; + } else { + return aa < bb ? 1 : -1; + } + }); + sortList.forEach(function (listItem) { + fileArea.appendChild(listItem); + }); +}; \ No newline at end of file diff --git a/contentbank/classes/content.php b/contentbank/classes/content.php index 5e8c7bc37b636..769395f631eaf 100644 --- a/contentbank/classes/content.php +++ b/contentbank/classes/content.php @@ -85,6 +85,16 @@ public function get_content_type(): string { return $this->content->contenttype; } + + /** + * Returns $this->content->timemodified. + * + * @return int $this->content->timemodified. + */ + public function get_timemodified(): int { + return $this->content->timemodified; + } + /** * Updates content_bank table with information in $this->content. * diff --git a/contentbank/classes/output/bankcontent.php b/contentbank/classes/output/bankcontent.php index 2ea5c4d83917e..6574b0409c7fa 100644 --- a/contentbank/classes/output/bankcontent.php +++ b/contentbank/classes/output/bankcontent.php @@ -75,18 +75,26 @@ public function export_for_template(renderer_base $output): stdClass { global $PAGE; $PAGE->requires->js_call_amd('core_contentbank/search', 'init'); + $PAGE->requires->js_call_amd('core_contentbank/sort', 'init'); $data = new stdClass(); $contentdata = array(); foreach ($this->contents as $content) { - $record = $content->get_content(); + $file = $content->get_file(); + $filesize = $file ? $file->get_filesize() : 0; + $mimetype = $file ? get_mimetype_description($file) : ''; $contenttypeclass = $content->get_content_type().'\\contenttype'; $contenttype = new $contenttypeclass($this->context); $name = $content->get_name(); $contentdata[] = array( 'name' => $name, + 'title' => strtolower($name), 'link' => $contenttype->get_view_url($content), - 'icon' => $contenttype->get_icon($content) + 'icon' => $contenttype->get_icon($content), + 'timemodified' => $content->get_timemodified(), + 'bytes' => $filesize, + 'size' => display_size($filesize), + 'type' => $mimetype ); } $data->contents = $contentdata; diff --git a/contentbank/templates/bankcontent.mustache b/contentbank/templates/bankcontent.mustache index 8c4362683bcc3..0826a65e482e5 100644 --- a/contentbank/templates/bankcontent.mustache +++ b/contentbank/templates/bankcontent.mustache @@ -21,7 +21,12 @@ { "contents": [ { - "name": "accordion.h5p", + "name": "Accordion.h5p", + "title": "accordion.h5p", + "timemodified": 1589792272, + "size": "699.3KB", + "bytes": 716126, + "type": "Archive (H5P)", "link": "http://something/contentbank/contenttype/h5p/view.php?url=http://something/pluginfile.php/1/contentbank/public/accordion.h5p", "icon" : "http://something/theme/image.php/boost/core/1581597850/f/h5p-64" }, @@ -43,43 +48,92 @@ } }} -
-
- {{>core_contentbank/bankcontent/search}} -
-
- {{>core_contentbank/bankcontent/toolbar}} +
+
+
+ {{>core_contentbank/bankcontent/search}} +
+
+ {{>core_contentbank/bankcontent/toolbar}} +
-
-
-
-
-
- {{#pix}} i/folder {{/pix}} -
-
+
+
+
+
+ {{#pix}} i/folder {{/pix}} +
+
+
-
-
- {{#contents}} - diff --git a/contentbank/templates/bankcontent/toolbar.mustache b/contentbank/templates/bankcontent/toolbar.mustache index 88f4a4ce9d882..04c762a132026 100644 --- a/contentbank/templates/bankcontent/toolbar.mustache +++ b/contentbank/templates/bankcontent/toolbar.mustache @@ -32,16 +32,19 @@ } }} -
-
-
- {{#tools}} - {{#link}}{{/link}} -
- {{#pix}} {{{ icon }}} {{/pix}} {{{ name }}} -
- {{#link}}
{{/link}} - {{/tools}} -
-
-
+ +{{#tools}} + + {{#pix}} {{{ icon }}} {{/pix}} {{{ name }}} + +{{/tools}} + + \ No newline at end of file diff --git a/contentbank/tests/behat/sort_content.feature b/contentbank/tests/behat/sort_content.feature new file mode 100644 index 0000000000000..b4ca7ad9081c1 --- /dev/null +++ b/contentbank/tests/behat/sort_content.feature @@ -0,0 +1,32 @@ +@core @core_contentbank @contentbank_h5p @javascript +Feature: Sort content in the content bank + In order to temporarily organise the content of the content bank + As an admin + I need to be able to sort the content bank in various ways + + Background: + Given the following "contentbank content" exist: + | contextlevel | reference | contenttype | user | contentname | + | System | | contenttype_h5p | admin | Dragon_santjordi.h5p | + | System | | contenttype_h5p | admin | mathsbook.h5p | + | System | | contenttype_h5p | admin | historybook.h5p | + | System | | contenttype_h5p | admin | santjordi.h5p | + | System | | contenttype_h5p | admin | santjordi_rose.h5p | + | System | | contenttype_h5p | admin | SantJordi_book | + + Scenario: Admins can order content in the content bank + Given I log in as "admin" + And I am on site homepage + And I turn editing mode on + And I add the "Navigation" block if not present + And I expand "Site pages" node + And I click on "Content bank" "link" + When I click on "Display contentbank with file details" "button" + And I click on "Sort by Content name ascending" "button" + And "Dragon_santjordi.h5p" "text" should appear before "historybook.h5p" "text" + And "historybook.h5p" "text" should appear before "mathsbook.h5p" "text" + And "SantJordi_book" "text" should appear before "santjordi_rose.h5p" "text" + And I click on "Sort by Content name descending" "button" + And "historybook.h5p" "text" should appear before "Dragon_santjordi.h5p" "text" + And "mathsbook.h5p" "text" should appear before "historybook.h5p" "text" + Then "santjordi_rose.h5p" "text" should appear before "SantJordi_book" "text" diff --git a/lang/en/contentbank.php b/lang/en/contentbank.php index 14b1c98a2395b..55108d356fbc9 100644 --- a/lang/en/contentbank.php +++ b/lang/en/contentbank.php @@ -38,9 +38,12 @@ $string['errordeletingcontentfromcategory'] = 'Error deleting content from category {$a}.'; $string['deletecontent'] = 'Delete content'; $string['deletecontentconfirm'] = 'Are you sure you want to delete the content \'{$a->name}\' and all associated files? This action cannot be undone.'; +$string['displaydetails'] = 'Display contentbank with file details'; +$string['displayicons'] = 'Display contentbank with icons'; $string['file'] = 'Upload content'; $string['file_help'] = 'Files may be stored in the content bank for use in courses. Only files used by content types enabled on the site may be uploaded.'; $string['itemsfound'] = '{$a} items found'; +$string['lastmodified'] = 'Last modified'; $string['name'] = 'Content'; $string['nopermissiontodelete'] = 'You do not have permission to delete content.'; $string['nopermissiontomanage'] = 'You do not have permission to manage content.'; @@ -55,6 +58,8 @@ $string['rename'] = 'Rename'; $string['renamecontent'] = 'Rename content'; $string['searchcontentbankbyname'] = 'Search for content by name'; +$string['size'] = 'Size'; $string['timecreated'] = 'Time created'; +$string['type'] = 'Type'; $string['unsupported'] = 'This content type is not supported.'; $string['upload'] = 'Upload'; diff --git a/theme/boost/scss/moodle/contentbank.scss b/theme/boost/scss/moodle/contentbank.scss index 4b075cb2cad6d..49e47bc621764 100644 --- a/theme/boost/scss/moodle/contentbank.scss +++ b/theme/boost/scss/moodle/contentbank.scss @@ -1,16 +1,123 @@ -@include media-breakpoint-down(sm) { - .content-bank-container .cb-file { - flex-basis: 50%; +.content-bank-container { + .cb-content-wrapper { + padding: 0.5rem; + min-height: 140px; + max-height: 500px; + overflow-x: auto; + flex-wrap: wrap; + } + .cb-thumbnail { + width: 24px; + height: 24px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; } -} + &.view-grid { + .cb-listitem { + margin-bottom: 0.5rem; + } + + @include media-breakpoint-down(sm) { + .cb-listitem { + flex-basis: 50%; + } + } + + @include media-breakpoint-up(sm) { + .cb-listitem { + max-width: 120px; + min-width: 120px; + } + } -@include media-breakpoint-up(sm) { - .content-bank-container .cb-file { - max-width: 120px; - min-width: 120px; + .cb-name { + text-align: center; + } + .cb-file { + padding: 0.5rem; + } + .cb-thumbnail { + width: 64px; + height: 64px; + margin-left: auto; + margin-right: auto; + margin-bottom: 0.5rem; + } + .cb-heading, + .cb-date, + .cb-size, + .cb-type { + display: none; + } } -} -.content-bank-container { - min-height: 140px; + &.view-list { + .cb-content-wrapper { + padding: 0 0.5rem; + flex-direction: column; + flex-wrap: nowrap; + } + + .cb-thumbnail { + margin-right: 0.5rem; + } + + .cb-listitem, + .cb-heading { + display: flex; + flex-wrap: wrap; + width: 100%; + border-bottom: $border-width solid $border-color; + } + + .cb-column { + display: flex; + padding: 0.25rem; + } + + .cb-column { + border-right: $border-width solid $border-color; + } + + @include media-breakpoint-down(sm) { + .cb-column { + flex: 0 0 50%; + max-width: 50%; + } + } + + @include media-breakpoint-up(sm) { + .cb-heading { + position: sticky; + top: 0; + z-index: 1; + } + + .cb-file, + .cb-date { + flex: 0 0 35%; + max-width: 35%; + } + .cb-size, + .cb-type { + flex: 0 0 15%; + max-width: 15%; + } + .cb-column.last { + border-right: 0; + } + } + + .cb-btnsort { + span { + display: none; + } + &.dir-none .default, + &.dir-asc .asc, + &.dir-desc .desc { + display: block; + } + } + } } \ No newline at end of file diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index a71a92b7d7296..add353709711e 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -12489,17 +12489,101 @@ table.calendartable caption { .cal_courses_flt { color: #868e96; } +.content-bank-container .cb-content-wrapper { + padding: 0.5rem; + min-height: 140px; + max-height: 500px; + overflow-x: auto; + flex-wrap: wrap; } + +.content-bank-container .cb-thumbnail { + width: 24px; + height: 24px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; } + +.content-bank-container.view-grid .cb-listitem { + margin-bottom: 0.5rem; } + @media (max-width: 767.98px) { - .content-bank-container .cb-file { + .content-bank-container.view-grid .cb-listitem { flex-basis: 50%; } } @media (min-width: 576px) { - .content-bank-container .cb-file { + .content-bank-container.view-grid .cb-listitem { max-width: 120px; min-width: 120px; } } -.content-bank-container { - min-height: 140px; } +.content-bank-container.view-grid .cb-name { + text-align: center; } + +.content-bank-container.view-grid .cb-file { + padding: 0.5rem; } + +.content-bank-container.view-grid .cb-thumbnail { + width: 64px; + height: 64px; + margin-left: auto; + margin-right: auto; + margin-bottom: 0.5rem; } + +.content-bank-container.view-grid .cb-heading, +.content-bank-container.view-grid .cb-date, +.content-bank-container.view-grid .cb-size, +.content-bank-container.view-grid .cb-type { + display: none; } + +.content-bank-container.view-list .cb-content-wrapper { + padding: 0 0.5rem; + flex-direction: column; + flex-wrap: nowrap; } + +.content-bank-container.view-list .cb-thumbnail { + margin-right: 0.5rem; } + +.content-bank-container.view-list .cb-listitem, +.content-bank-container.view-list .cb-heading { + display: flex; + flex-wrap: wrap; + width: 100%; + border-bottom: 1px solid #dee2e6; } + +.content-bank-container.view-list .cb-column { + display: flex; + padding: 0.25rem; } + +.content-bank-container.view-list .cb-column { + border-right: 1px solid #dee2e6; } + +@media (max-width: 767.98px) { + .content-bank-container.view-list .cb-column { + flex: 0 0 50%; + max-width: 50%; } } + +@media (min-width: 576px) { + .content-bank-container.view-list .cb-heading { + position: sticky; + top: 0; + z-index: 1; } + .content-bank-container.view-list .cb-file, + .content-bank-container.view-list .cb-date { + flex: 0 0 35%; + max-width: 35%; } + .content-bank-container.view-list .cb-size, + .content-bank-container.view-list .cb-type { + flex: 0 0 15%; + max-width: 15%; } + .content-bank-container.view-list .cb-column.last { + border-right: 0; } } + +.content-bank-container.view-list .cb-btnsort span { + display: none; } + +.content-bank-container.view-list .cb-btnsort.dir-none .default, +.content-bank-container.view-list .cb-btnsort.dir-asc .asc, +.content-bank-container.view-list .cb-btnsort.dir-desc .desc { + display: block; } /* course.less */ /* COURSE CONTENT */ diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index d5ea721cab2c8..1b2c012955461 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -12702,17 +12702,101 @@ table.calendartable caption { .cal_courses_flt { color: #868e96; } +.content-bank-container .cb-content-wrapper { + padding: 0.5rem; + min-height: 140px; + max-height: 500px; + overflow-x: auto; + flex-wrap: wrap; } + +.content-bank-container .cb-thumbnail { + width: 24px; + height: 24px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; } + +.content-bank-container.view-grid .cb-listitem { + margin-bottom: 0.5rem; } + @media (max-width: 767.98px) { - .content-bank-container .cb-file { + .content-bank-container.view-grid .cb-listitem { flex-basis: 50%; } } @media (min-width: 576px) { - .content-bank-container .cb-file { + .content-bank-container.view-grid .cb-listitem { max-width: 120px; min-width: 120px; } } -.content-bank-container { - min-height: 140px; } +.content-bank-container.view-grid .cb-name { + text-align: center; } + +.content-bank-container.view-grid .cb-file { + padding: 0.5rem; } + +.content-bank-container.view-grid .cb-thumbnail { + width: 64px; + height: 64px; + margin-left: auto; + margin-right: auto; + margin-bottom: 0.5rem; } + +.content-bank-container.view-grid .cb-heading, +.content-bank-container.view-grid .cb-date, +.content-bank-container.view-grid .cb-size, +.content-bank-container.view-grid .cb-type { + display: none; } + +.content-bank-container.view-list .cb-content-wrapper { + padding: 0 0.5rem; + flex-direction: column; + flex-wrap: nowrap; } + +.content-bank-container.view-list .cb-thumbnail { + margin-right: 0.5rem; } + +.content-bank-container.view-list .cb-listitem, +.content-bank-container.view-list .cb-heading { + display: flex; + flex-wrap: wrap; + width: 100%; + border-bottom: 1px solid #dee2e6; } + +.content-bank-container.view-list .cb-column { + display: flex; + padding: 0.25rem; } + +.content-bank-container.view-list .cb-column { + border-right: 1px solid #dee2e6; } + +@media (max-width: 767.98px) { + .content-bank-container.view-list .cb-column { + flex: 0 0 50%; + max-width: 50%; } } + +@media (min-width: 576px) { + .content-bank-container.view-list .cb-heading { + position: sticky; + top: 0; + z-index: 1; } + .content-bank-container.view-list .cb-file, + .content-bank-container.view-list .cb-date { + flex: 0 0 35%; + max-width: 35%; } + .content-bank-container.view-list .cb-size, + .content-bank-container.view-list .cb-type { + flex: 0 0 15%; + max-width: 15%; } + .content-bank-container.view-list .cb-column.last { + border-right: 0; } } + +.content-bank-container.view-list .cb-btnsort span { + display: none; } + +.content-bank-container.view-list .cb-btnsort.dir-none .default, +.content-bank-container.view-list .cb-btnsort.dir-asc .asc, +.content-bank-container.view-list .cb-btnsort.dir-desc .desc { + display: block; } /* course.less */ /* COURSE CONTENT */