diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index 92a0ddf..8b50d1e 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -1,10 +1,10 @@ name: Moodle Plugin CI -on: [push, pull_request] +on: [pull_request] jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 services: postgres: @@ -28,17 +28,17 @@ jobs: fail-fast: false matrix: include: - - php: '8.0' - moodle-branch: 'MOODLE_403_STABLE' - database: 'pgsql' - - php: '8.0' - moodle-branch: 'MOODLE_402_STABLE' + - php: '8.2' + moodle-branch: 'MOODLE_404_STABLE' database: 'mariadb' - - php: '8.0' - moodle-branch: 'MOODLE_401_STABLE' + - php: '8.2' + moodle-branch: 'MOODLE_404_STABLE' + database: 'pgsql' + - php: '8.1' + moodle-branch: 'MOODLE_404_STABLE' database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_400_STABLE' + - php: '8.1' + moodle-branch: 'MOODLE_404_STABLE' database: 'mariadb' steps: diff --git a/amd/build/designer_section.min.js b/amd/build/designer_section.min.js index b5e6ed5..16c330e 100644 --- a/amd/build/designer_section.min.js +++ b/amd/build/designer_section.min.js @@ -5,6 +5,6 @@ * @copyright 2021 bdecent gmbh * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("format_designer/designer_section",["jquery","core/fragment","core/templates","core/loadingicon","core/ajax","core_course/actions","core_message/toggle_contact_button","theme_boost/popover","core/notification"],(function($,Fragment,Templates,Loadingicon,Ajax,Actions,Contact,Notification){var SELECTOR={ACTIVITYLI:"li.activity",SECTIONLI:"li.section",ACTIVITYACTION:"a.cm-edit-action",SECTIONACTIONMENU:".section_action_menu.designer-menu"};Y.use("moodle-course-coursebase",(function(){var courseformatselector=M.course.format.get_section_selector();courseformatselector&&(SELECTOR.SECTIONLI=courseformatselector)}));let DesignerSection=function(courseId,contextId,popupActivities,videoTime,issubpanel,sectionreturn){var self=this;self.courseId=courseId,self.contextId=contextId,self.popupActivities=popupActivities,self.videoTime=videoTime,self.isSubpanel=issubpanel,self.sectionReturn=sectionreturn,$(".course-info-block .carousel .carousel-item:nth-child(1)").addClass("active"),$(".course-info-block #courseStaffinfoControls.carousel").addClass("active"),$("body").delegate(self.SectionController,"click",self.sectionLayoutaction.bind(this)),$("body").delegate(self.SectionSubmenuSwitcher,"click",self.sectionLayoutaction.bind(this)),$("body").delegate(self.RestrictInfo,"click",self.moduleHandler.bind(this)),$("body").delegate(self.sectionRestricted,"click",this.sectionRestrictHandler.bind(this)),$("body").delegate(self.fullDescription,"click",self.fullmodcontentHandler.bind(this)),$("body").delegate(self.trimDescription,"click",self.trimmodcontentHandler.bind(this)),$("body").delegate(self.goToURL,"click",self.redirectToModule.bind(this)),$("body").delegate(self.goToSectionURL,"click",self.redirectToSection.bind(this)),window.onhashchange=function(){self.expandSection()},this.expandSection(),$(".course-type-flow").length>0&&$(".collapse").on("show.bs.collapse",(function(){$(this).parents("li.section").addClass("stack-header-collapsing");var sectionid=$(this).parents("li.section").attr("id"),distance=document.getElementById(sectionid).offsetTop-document.body.scrollTop;setTimeout((()=>window.scroll(0,distance)),50)})).on("shown.bs.collapse",(function(){$(this).parents("li.section").removeClass("stack-header-collapsing")}));var contactModal=document.getElementsByClassName("toggle-contact-button");Array.from(contactModal).forEach((function(element){element.addEventListener("click",(function(e){e.preventDefault(),null!=e.currentTarget.dataset.userid&&Contact.enhance(e.currentTarget)}))})),$('.progress .progress-bar[data-toggle="popover"]').popover()};return DesignerSection.prototype.goToURL='.designer [data-action="go-to-url"]',DesignerSection.prototype.goToSectionURL='.designer [data-action="go-to-section-url"]',DesignerSection.prototype.SectionController=".designer #section-designer-action .dropdown-menu a",DesignerSection.prototype.SectionSubmenuSwitcher=".designer .section_action_menu .dropdown-subpanel a[data-value=section-designer-action] + .dropdown-menu a",DesignerSection.prototype.RestrictInfo=".designer .designer-section-content .call-action-block",DesignerSection.prototype.moduleBlock=".designer .designer-section-content li.activity",DesignerSection.prototype.loadingElement=".icon-loader-block",DesignerSection.prototype.sectionRestricted=".designer .availability-section-block .section-restricted-action",DesignerSection.prototype.fullDescription=".designer-section-content li .fullcontent-summary .mod-description-action",DesignerSection.prototype.trimDescription=".designer-section-content li .trim-summary .mod-description-action",DesignerSection.prototype.modules=null,DesignerSection.prototype.redirectToModule=function(event){let nodeName=event.target.nodeName,iscircle=event.target.closest("li.activity").classList.contains("circle-layout"),isDescription=event.target.classList.contains("mod-description-action"),isPadlock=event.target.classList.contains("fa-lock"),ispopupModule=event.target.closest("li.activity").classList.contains("popmodule"),isModHasURL=event.target.closest('li.activity div[data-action="go-to-url"]').getAttribute("data-url"),isCompletionButton=event.target.closest('button[data-action="toggle-manual-completion"]');if(nodeName in["a","button","form"]||document.body.classList.contains("editing")||iscircle||isDescription||isPadlock||ispopupModule||""==isModHasURL||isCompletionButton){if(ispopupModule&&!document.body.classList.contains("editing"))if(null===event.target.closest("button[data-action='toggle-manual-completion']")&&null===event.target.closest(".mod-description-action"))event.target.closest("li.activity").querySelector("a[href]").click();return null}let modurl=event.target.closest("[data-action=go-to-url]").getAttribute("data-url");return window.location.href=modurl,!0},DesignerSection.prototype.redirectToSection=function(event){let isPadlock=event.target.classList.contains("fa-lock");if(document.body.classList.contains("editing")||isPadlock)return null;var singlesection=event.target.closest("[data-action=go-to-section-url]");let sectionurl=singlesection.getAttribute("data-url"),sectiontarget="_self",target=singlesection.getAttribute("data-target");return target&&(sectiontarget=target),window.open(sectionurl,sectiontarget),!0},DesignerSection.prototype.expandSection=()=>{var sectionID=window.location.hash;if(sectionID){var id=sectionID.substring(1),section=document.getElementById(id);if(section){var title=section.querySelector(".section-header-content");title&&(title.classList.remove("collapsed"),title.setAttribute("aria-expanded",!0));var content=section.querySelector(".content");content&&content.classList.add("show"),null!==document.getElementById("section-course-accordion")&&(document.getElementById("section-head-0").classList.add("collapsed"),document.getElementById("section-content-0").classList.remove("show")),section.scrollIntoView()}}},DesignerSection.prototype.fullmodcontentHandler=function(event){var THIS=$(event.currentTarget);let fullContent=$(THIS).closest("li.activity").find(".fullcontent-summary"),trimcontent=$(THIS).closest("li.activity").find(".trim-summary");trimcontent.hasClass("summary-hide")&&(trimcontent.removeClass("summary-hide"),fullContent.addClass("summary-hide"))},DesignerSection.prototype.trimmodcontentHandler=function(event){var THIS=$(event.currentTarget);let fullContent=$(THIS).closest("li.activity").find(".fullcontent-summary"),trimcontent=$(THIS).closest("li.activity").find(".trim-summary");fullContent.hasClass("summary-hide")&&(fullContent.removeClass("summary-hide"),trimcontent.addClass("summary-hide"))},DesignerSection.prototype.sectionRestrictHandler=function(event){var sectionRestrictInfo=$(event.currentTarget).prev();sectionRestrictInfo&&(sectionRestrictInfo.hasClass("show")?sectionRestrictInfo.removeClass("show"):sectionRestrictInfo.addClass("show"))},DesignerSection.prototype.moduleHandler=function(event){event.preventDefault();var restrictBlock=$(event.currentTarget).parents(".restrict-block");restrictBlock.length&&(restrictBlock.hasClass("show")?restrictBlock.removeClass("show"):restrictBlock.addClass("show"))},DesignerSection.prototype.updateVideoTimeInstance=function(sectionId){var sectionVideotimes="body "+("#"+sectionId)+" .activity.videotime";0!=$(sectionVideotimes).length&&$(sectionVideotimes).each(function(index,module){this.CreateInstance(module)}.bind(this))},DesignerSection.prototype.CreateInstance=function(module){if($(module).find(".instancename").length&&($(module).find(".vimeo-embed").length||$(module).find(".video-js").length)){var args={cmid:module.getAttribute("data-id")};Ajax.call([{methodname:"format_designer_get_videotime_instace",args:args}],!0)[0].then((function(data){var template=JSON.parse(data);if("videojs"==template.playertype)var uniqueid=$(module).find(".video-js").first().attr("id").replace("vimeo-embed-","");else uniqueid=$(module).find(".vimeo-embed").first().attr("id").replace("vimeo-embed-","");template.uniqueid=uniqueid,Templates.render(template.templatename,template).then((function(html,js){return Templates.runTemplateJS(js),!0})).fail(Notification.exception)}))}},DesignerSection.prototype.sectionLayoutaction=function(event){event.preventDefault();var self=this;let sectionId=event.target.closest("li.section").getAttribute("id");var sectionid=event.target.closest("li.section").getAttribute("data-id"),sectionitem=document.getElementById(sectionId),iconBlock="#"+sectionId+" "+self.loadingElement,layout=$(event.currentTarget).data("value"),layouttext=$(event.currentTarget).text();$(event.target).parents(".dropdown").find(".btn").html(layouttext),$(event.target).parents(".dropdown").find(".btn").val(layout),$(event.target).parent().find("a.dropdown-item").each((function(){$(this).removeClass("active")})),$(event.target).addClass("active");let dataid=event.target.closest("li.section").getAttribute("data-id");var args={courseid:self.courseId,sectionid:dataid,options:[{name:$(event.currentTarget).data("option"),value:layout}]},promises=Ajax.call([{methodname:"format_designer_set_section_options",args:args}],!0);$.when.apply($,promises).done((function(){if(self.isSubpanel){Fragment.loadFragment("core_courseformat","section",self.contextId,{id:sectionid,courseid:self.courseId,sr:self.sectionReturn}).then(((html,js)=>{Templates.replaceNode(sectionitem,html,js)})).catch()}else{Actions.refreshSection("#"+sectionId,dataid,0).then((()=>"")).catch()}})),Loadingicon.addIconToContainerRemoveOnCompletion(iconBlock,promises),setTimeout((function(){self.videoTime&&self.updateVideoTimeInstance(sectionId)}),2e3)},{init:function(courseId,contextId,popupActivities,videoTime,issubpanel,sectionreturn){return new DesignerSection(courseId,contextId,popupActivities,videoTime,issubpanel,sectionreturn)}}})); +define("format_designer/designer_section",["jquery","core/fragment","core/templates","core/loadingicon","core/ajax","core_course/actions","core_message/toggle_contact_button","theme_boost/popover","core/notification"],(function($,Fragment,Templates,Loadingicon,Ajax,Actions,Contact,Notification){var SELECTOR={ACTIVITYLI:"li.activity",SECTIONLI:"li.section",ACTIVITYACTION:"a.cm-edit-action",SECTIONACTIONMENU:".section_action_menu.designer-menu"};Y.use("moodle-course-coursebase",(function(){var courseformatselector=M.course.format.get_section_selector();courseformatselector&&(SELECTOR.SECTIONLI=courseformatselector)}));let DesignerSection=function(courseId,contextId,popupActivities,videoTime,issubpanel,sectionreturn){var self=this;self.courseId=courseId,self.contextId=contextId,self.popupActivities=popupActivities,self.videoTime=videoTime,self.isSubpanel=issubpanel,self.sectionReturn=sectionreturn,$(".course-info-block .carousel .carousel-item:nth-child(1)").addClass("active"),$(".course-info-block #courseStaffinfoControls.carousel").addClass("active"),$("body").delegate(self.SectionController,"click",self.sectionLayoutaction.bind(this)),$("body").delegate(self.SectionSubmenuSwitcher,"click",self.sectionLayoutaction.bind(this)),$("body").delegate(self.RestrictInfo,"click",self.moduleHandler.bind(this)),$("body").delegate(self.sectionRestricted,"click",this.sectionRestrictHandler.bind(this)),$("body").delegate(self.fullDescription,"click",self.fullmodcontentHandler.bind(this)),$("body").delegate(self.trimDescription,"click",self.trimmodcontentHandler.bind(this)),$("body").delegate(self.goToURL,"click",self.redirectToModule.bind(this)),$("body").delegate(self.goToSectionURL,"click",self.redirectToSection.bind(this)),window.onhashchange=function(){self.expandSection()},this.expandSection();var contactModal=document.getElementsByClassName("toggle-contact-button");Array.from(contactModal).forEach((function(element){element.addEventListener("click",(function(e){e.preventDefault(),null!=e.currentTarget.dataset.userid&&Contact.enhance(e.currentTarget)}))})),$('.progress .progress-bar[data-toggle="popover"]').popover()};return DesignerSection.prototype.goToURL='.designer [data-action="go-to-url"]',DesignerSection.prototype.goToSectionURL='.designer [data-action="go-to-section-url"]',DesignerSection.prototype.SectionController=".designer #section-designer-action .dropdown-menu a",DesignerSection.prototype.SectionSubmenuSwitcher=".designer .section_action_menu .dropdown-subpanel a[data-value=section-designer-action] + .dropdown-menu a",DesignerSection.prototype.RestrictInfo=".designer .designer-section-content .call-action-block",DesignerSection.prototype.moduleBlock=".designer .designer-section-content li.activity",DesignerSection.prototype.loadingElement=".icon-loader-block",DesignerSection.prototype.sectionRestricted=".designer .availability-section-block .section-restricted-action",DesignerSection.prototype.fullDescription=".designer-section-content li .fullcontent-summary .mod-description-action",DesignerSection.prototype.trimDescription=".designer-section-content li .trim-summary .mod-description-action",DesignerSection.prototype.modules=null,DesignerSection.prototype.redirectToModule=function(event){let nodeName=event.target.nodeName,iscircle=event.target.closest("li.activity").classList.contains("circle-layout"),isDescription=event.target.classList.contains("mod-description-action"),isPadlock=event.target.classList.contains("fa-lock"),ispopupModule=event.target.closest("li.activity").classList.contains("popmodule"),isModHasURL=event.target.closest('li.activity div[data-action="go-to-url"]').getAttribute("data-url"),isCompletionButton=event.target.closest('button[data-action="toggle-manual-completion"]');if(nodeName in["a","button","form"]||document.body.classList.contains("editing")||iscircle||isDescription||isPadlock||ispopupModule||""==isModHasURL||isCompletionButton){if(ispopupModule&&!document.body.classList.contains("editing"))if(null===event.target.closest("button[data-action='toggle-manual-completion']")&&null===event.target.closest(".mod-description-action"))event.target.closest("li.activity").querySelector("a[href]").click();return null}let modurl=event.target.closest("[data-action=go-to-url]").getAttribute("data-url");return window.location.href=modurl,!0},DesignerSection.prototype.redirectToSection=function(event){let isPadlock=event.target.classList.contains("fa-lock");if(document.body.classList.contains("editing")||isPadlock)return null;var singlesection=event.target.closest("[data-action=go-to-section-url]");let sectionurl=singlesection.getAttribute("data-url"),sectiontarget="_self",target=singlesection.getAttribute("data-target");return target&&(sectiontarget=target),window.open(sectionurl,sectiontarget),!0},DesignerSection.prototype.expandSection=()=>{var sectionID=window.location.hash;if(sectionID){var id=sectionID.substring(1),section=document.getElementById(id);if(section){var title=section.querySelector(".section-header-content");title&&(title.classList.remove("collapsed"),title.setAttribute("aria-expanded",!0));var content=section.querySelector(".content");content&&content.classList.add("show"),null!==document.getElementById("section-course-accordion")&&(document.getElementById("section-head-0").classList.add("collapsed"),document.getElementById("section-content-0").classList.remove("show")),section.scrollIntoView()}}},DesignerSection.prototype.fullmodcontentHandler=function(event){var THIS=$(event.currentTarget);let fullContent=$(THIS).closest("li.activity").find(".fullcontent-summary"),trimcontent=$(THIS).closest("li.activity").find(".trim-summary");trimcontent.hasClass("summary-hide")&&(trimcontent.removeClass("summary-hide"),fullContent.addClass("summary-hide"))},DesignerSection.prototype.trimmodcontentHandler=function(event){var THIS=$(event.currentTarget);let fullContent=$(THIS).closest("li.activity").find(".fullcontent-summary"),trimcontent=$(THIS).closest("li.activity").find(".trim-summary");fullContent.hasClass("summary-hide")&&(fullContent.removeClass("summary-hide"),trimcontent.addClass("summary-hide"))},DesignerSection.prototype.sectionRestrictHandler=function(event){var sectionRestrictInfo=$(event.currentTarget).prev();sectionRestrictInfo&&(sectionRestrictInfo.hasClass("show")?sectionRestrictInfo.removeClass("show"):sectionRestrictInfo.addClass("show"))},DesignerSection.prototype.moduleHandler=function(event){event.preventDefault();var restrictBlock=$(event.currentTarget).parents(".restrict-block");restrictBlock.length&&(restrictBlock.hasClass("show")?restrictBlock.removeClass("show"):restrictBlock.addClass("show"))},DesignerSection.prototype.updateVideoTimeInstance=function(sectionId){var sectionVideotimes="body "+("#"+sectionId)+" .activity.videotime";0!=$(sectionVideotimes).length&&$(sectionVideotimes).each(function(index,module){this.CreateInstance(module)}.bind(this))},DesignerSection.prototype.CreateInstance=function(module){if($(module).find(".instancename").length&&($(module).find(".vimeo-embed").length||$(module).find(".video-js").length)){var args={cmid:module.getAttribute("data-id")};Ajax.call([{methodname:"format_designer_get_videotime_instace",args:args}],!0)[0].then((function(data){var template=JSON.parse(data);if("videojs"==template.playertype)var uniqueid=$(module).find(".video-js").first().attr("id").replace("vimeo-embed-","");else uniqueid=$(module).find(".vimeo-embed").first().attr("id").replace("vimeo-embed-","");template.uniqueid=uniqueid,Templates.render(template.templatename,template).then((function(html,js){return Templates.runTemplateJS(js),!0})).fail(Notification.exception)}))}},DesignerSection.prototype.sectionLayoutaction=function(event){event.preventDefault();var self=this;let sectionId=event.target.closest("li.section").getAttribute("id");var sectionid=event.target.closest("li.section").getAttribute("data-id"),sectionitem=document.getElementById(sectionId),iconBlock="#"+sectionId+" "+self.loadingElement,layout=$(event.currentTarget).data("value"),layouttext=$(event.currentTarget).text();$(event.target).parents(".dropdown").find(".btn").html(layouttext),$(event.target).parents(".dropdown").find(".btn").val(layout),$(event.target).parent().find("a.dropdown-item").each((function(){$(this).removeClass("active")})),$(event.target).addClass("active");let dataid=event.target.closest("li.section").getAttribute("data-id");var args={courseid:self.courseId,sectionid:dataid,options:[{name:$(event.currentTarget).data("option"),value:layout}]},promises=Ajax.call([{methodname:"format_designer_set_section_options",args:args}],!0);$.when.apply($,promises).done((function(){if(self.isSubpanel){Fragment.loadFragment("core_courseformat","section",self.contextId,{id:sectionid,courseid:self.courseId,sr:self.sectionReturn}).then(((html,js)=>{Templates.replaceNode(sectionitem,html,js)})).catch()}else{Actions.refreshSection("#"+sectionId,dataid,0).then((()=>"")).catch()}})),Loadingicon.addIconToContainerRemoveOnCompletion(iconBlock,promises),setTimeout((function(){self.videoTime&&self.updateVideoTimeInstance(sectionId)}),2e3)},{init:function(courseId,contextId,popupActivities,videoTime,issubpanel,sectionreturn){return new DesignerSection(courseId,contextId,popupActivities,videoTime,issubpanel,sectionreturn)}}})); //# sourceMappingURL=designer_section.min.js.map \ No newline at end of file diff --git a/amd/build/designer_section.min.js.map b/amd/build/designer_section.min.js.map index 423fe8a..c510728 100644 --- a/amd/build/designer_section.min.js.map +++ b/amd/build/designer_section.min.js.map @@ -1 +1 @@ -{"version":3,"file":"designer_section.min.js","sources":["../src/designer_section.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/**\n * Implemented designer format js.\n *\n * @module format_designer/designer_section\n * @copyright 2021 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n define(['jquery', 'core/fragment', 'core/templates', 'core/loadingicon', 'core/ajax',\n 'core_course/actions', 'core_message/toggle_contact_button', 'theme_boost/popover', 'core/notification',],\n function($, Fragment, Templates, Loadingicon, Ajax, Actions, Contact, Notification) {\n\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n SECTIONLI: 'li.section',\n ACTIVITYACTION: 'a.cm-edit-action',\n SECTIONACTIONMENU: '.section_action_menu.designer-menu',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n\n /**\n * Control designer format action\n * @param {int} courseId\n * @param {int} contextId\n * @param {array} popupActivities\n * @param {bool} videoTime\n * @param {bool} issubpanel\n * @param {int} sectionreturn\n */\n let DesignerSection = function(courseId, contextId, popupActivities, videoTime, issubpanel, sectionreturn) {\n var self = this;\n self.courseId = courseId;\n self.contextId = contextId;\n self.popupActivities = popupActivities;\n self.videoTime = videoTime;\n self.isSubpanel = issubpanel;\n self.sectionReturn = sectionreturn;\n\n $(\".course-info-block .carousel .carousel-item:nth-child(1)\").addClass('active');\n $(\".course-info-block #courseStaffinfoControls.carousel\").addClass('active');\n\n $('body').delegate(self.SectionController, 'click', self.sectionLayoutaction.bind(this));\n $('body').delegate(self.SectionSubmenuSwitcher, 'click', self.sectionLayoutaction.bind(this));\n\n $(\"body\").delegate(self.RestrictInfo, \"click\", self.moduleHandler.bind(this));\n $(\"body\").delegate(self.sectionRestricted, \"click\", this.sectionRestrictHandler.bind(this));\n $('body').delegate(self.fullDescription, \"click\", self.fullmodcontentHandler.bind(this));\n $('body').delegate(self.trimDescription, \"click\", self.trimmodcontentHandler.bind(this));\n $('body').delegate(self.goToURL, \"click\", self.redirectToModule.bind(this));\n $('body').delegate(self.goToSectionURL, \"click\", self.redirectToSection.bind(this));\n window.onhashchange = function() {\n self.expandSection();\n };\n this.expandSection();\n\n if ($('.course-type-flow').length > 0) {\n $('.collapse').on('show.bs.collapse', function() {\n $(this).parents('li.section').addClass('stack-header-collapsing');\n var sectionid = $(this).parents('li.section').attr('id');\n var section = document.getElementById(sectionid);\n var distance = section.offsetTop - document.body.scrollTop;\n setTimeout(() => window.scroll(0, distance), 50);\n }).on('shown.bs.collapse', function() {\n $(this).parents('li.section').removeClass('stack-header-collapsing');\n });\n }\n\n var contactModal = document.getElementsByClassName('toggle-contact-button');\n Array.from(contactModal).forEach(function(element) {\n element.addEventListener('click', function(e) {\n e.preventDefault();\n if (e.currentTarget.dataset.userid != undefined) {\n Contact.enhance(e.currentTarget);\n }\n });\n });\n\n $('.progress .progress-bar[data-toggle=\"popover\"]').popover();\n\n };\n\n /**\n * Selector section controller.\n */\n DesignerSection.prototype.goToURL = '.designer [data-action=\"go-to-url\"]';\n\n DesignerSection.prototype.goToSectionURL = '.designer [data-action=\"go-to-section-url\"]';\n\n DesignerSection.prototype.SectionController = \".designer #section-designer-action .dropdown-menu a\";\n\n DesignerSection.prototype.SectionSubmenuSwitcher\n = \".designer .section_action_menu .dropdown-subpanel a[data-value=section-designer-action] + .dropdown-menu a\";\n\n DesignerSection.prototype.RestrictInfo = \".designer .designer-section-content .call-action-block\";\n\n DesignerSection.prototype.moduleBlock = \".designer .designer-section-content li.activity\";\n\n DesignerSection.prototype.loadingElement = \".icon-loader-block\";\n\n DesignerSection.prototype.sectionRestricted = \".designer .availability-section-block .section-restricted-action\";\n\n DesignerSection.prototype.fullDescription = \".designer-section-content li .fullcontent-summary .mod-description-action\";\n\n DesignerSection.prototype.trimDescription = \".designer-section-content li .trim-summary .mod-description-action\";\n\n DesignerSection.prototype.modules = null;\n\n DesignerSection.prototype.redirectToModule = function(event) {\n let nodeName = event.target.nodeName;\n let preventionNodes = ['a', 'button', 'form'];\n let iscircle = event.target.closest('li.activity').classList.contains('circle-layout');\n let isDescription = event.target.classList.contains('mod-description-action');\n let isPadlock = event.target.classList.contains('fa-lock');\n let ispopupModule = event.target.closest('li.activity').classList.contains('popmodule');\n let isModHasURL = event.target.closest('li.activity div[data-action=\"go-to-url\"]').getAttribute('data-url');\n let isCompletionButton = event.target.closest('button[data-action=\"toggle-manual-completion\"]');\n if ((nodeName in preventionNodes)\n || document.body.classList.contains('editing') || iscircle || isDescription || isPadlock || ispopupModule\n || isModHasURL == '' || isCompletionButton) {\n if (ispopupModule && !document.body.classList.contains('editing')) {\n if (event.target.closest(\"button[data-action='toggle-manual-completion']\") === null &&\n event.target.closest(\".mod-description-action\") === null) {\n var li = event.target.closest('li.activity');\n li.querySelector('a[href]').click();\n }\n }\n return null;\n }\n var card = event.target.closest(\"[data-action=go-to-url]\");\n let modurl = card.getAttribute('data-url');\n window.location.href = modurl;\n return true;\n };\n\n DesignerSection.prototype.redirectToSection = function(event) {\n let isPadlock = event.target.classList.contains('fa-lock');\n if (document.body.classList.contains('editing') || isPadlock) {\n return null;\n }\n var singlesection = event.target.closest(\"[data-action=go-to-section-url]\");\n let sectionurl = singlesection.getAttribute('data-url');\n let sectiontarget = \"_self\";\n let target = singlesection.getAttribute('data-target');\n if (target) {\n sectiontarget = target;\n }\n window.open(sectionurl, sectiontarget);\n return true;\n };\n\n DesignerSection.prototype.expandSection = () => {\n var sectionID = window.location.hash;\n if (sectionID) {\n var id = sectionID.substring(1);\n var section = document.getElementById(id);\n if (section) {\n var title = section.querySelector('.section-header-content');\n if (title) {\n title.classList.remove('collapsed');\n title.setAttribute('aria-expanded', true);\n }\n var content = section.querySelector('.content');\n if (content) {\n content.classList.add('show');\n }\n if (document.getElementById('section-course-accordion') !== null) {\n document.getElementById('section-head-0').classList.add('collapsed');\n document.getElementById('section-content-0').classList.remove('show');\n }\n section.scrollIntoView();\n }\n }\n };\n\n DesignerSection.prototype.fullmodcontentHandler = function(event) {\n var THIS = $(event.currentTarget);\n let fullContent = $(THIS).closest('li.activity').find('.fullcontent-summary');\n let trimcontent = $(THIS).closest('li.activity').find('.trim-summary');\n if (trimcontent.hasClass('summary-hide')) {\n trimcontent.removeClass('summary-hide');\n fullContent.addClass('summary-hide');\n }\n };\n\n DesignerSection.prototype.trimmodcontentHandler = function(event) {\n var THIS = $(event.currentTarget);\n let fullContent = $(THIS).closest('li.activity').find('.fullcontent-summary');\n let trimcontent = $(THIS).closest('li.activity').find('.trim-summary');\n if (fullContent.hasClass('summary-hide')) {\n fullContent.removeClass('summary-hide');\n trimcontent.addClass('summary-hide');\n }\n };\n\n DesignerSection.prototype.sectionRestrictHandler = function(event) {\n var sectionRestrictInfo = $(event.currentTarget).prev();\n if (sectionRestrictInfo) {\n if (!sectionRestrictInfo.hasClass('show')) {\n sectionRestrictInfo.addClass('show');\n } else {\n sectionRestrictInfo.removeClass('show');\n }\n }\n };\n\n DesignerSection.prototype.moduleHandler = function(event) {\n event.preventDefault();\n var restrictBlock = $(event.currentTarget).parents('.restrict-block');\n if (restrictBlock.length) {\n if (!restrictBlock.hasClass('show')) {\n restrictBlock.addClass('show');\n } else {\n restrictBlock.removeClass('show');\n }\n }\n };\n\n DesignerSection.prototype.updateVideoTimeInstance = function(sectionId) {\n var section = \"#\" + sectionId;\n var sectionVideotimes = \"body \"+ section + \" .activity.videotime\";\n if ($(sectionVideotimes).length == 0) {\n return;\n }\n $(sectionVideotimes).each(function(index, module) {\n this.CreateInstance(module);\n }.bind(this));\n };\n\n DesignerSection.prototype.CreateInstance = function (module) {\n if (\n $(module).find('.instancename').length\n && ($(module).find('.vimeo-embed').length\n || $(module).find('.video-js').length)\n ) {\n var cmId = module.getAttribute(\"data-id\");\n var args = {cmid : cmId};\n // Get module instance.\n var promises = Ajax.call([{\n methodname: 'format_designer_get_videotime_instace',\n args: args\n }], true);\n promises[0].then(function(data) {\n var template = JSON.parse(data);\n if (template.playertype == 'videojs') {\n var uniqueid = $(module).find('.video-js').first().attr('id').replace('vimeo-embed-', '');\n } else {\n var uniqueid = $(module).find('.vimeo-embed').first().attr('id').replace('vimeo-embed-', '');\n }\n template.uniqueid = uniqueid;\n Templates.render(template.templatename, template).then(function(html, js) {\n Templates.runTemplateJS(js);\n return true;\n }).fail(Notification.exception);\n });\n }\n };\n\n /**\n * Implementaion swith the section layout.\n * @param {object} event\n */\n DesignerSection.prototype.sectionLayoutaction = function(event) {\n event.preventDefault();\n var self = this;\n let sectionId = event.target.closest('li.section').getAttribute('id');\n var sectionid = event.target.closest('li.section').getAttribute('data-id');\n var sectionitem = document.getElementById(sectionId);\n var iconBlock = \"#\" + sectionId + \" \" + self.loadingElement;\n var layout = $(event.currentTarget).data('value');\n var layouttext = $(event.currentTarget).text();\n $(event.target).parents(\".dropdown\").find(\".btn\").html(layouttext);\n $(event.target).parents(\".dropdown\").find(\".btn\").val(layout);\n $(event.target).parent().find(\"a.dropdown-item\").each(function() {\n $(this).removeClass('active');\n });\n $(event.target).addClass('active');\n let dataid = event.target.closest('li.section').getAttribute('data-id');\n var args = {\n courseid: self.courseId,\n sectionid: dataid,\n options: [{name: $(event.currentTarget).data('option'), value: layout}]\n };\n var promises = Ajax.call([{\n methodname: 'format_designer_set_section_options',\n args: args\n }], true);\n $.when.apply($, promises)\n .done(function() {\n if (self.isSubpanel) {\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'section',\n self.contextId,\n {\n id: sectionid,\n courseid: self.courseId,\n sr: self.sectionReturn,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(sectionitem, html, js);\n }).catch();\n } else {\n const sectionpromise = Actions.refreshSection('#' + sectionId, dataid, 0);\n sectionpromise.then(() => {\n return '';\n }).catch();\n }\n });\n Loadingicon.addIconToContainerRemoveOnCompletion(iconBlock, promises);\n // If videotime exist update the module.\n setTimeout(function() {\n if (self.videoTime) {\n self.updateVideoTimeInstance(sectionId);\n }\n }, 2000);\n };\n\n return {\n init: function(courseId, contextId, popupActivities, videoTime, issubpanel, sectionreturn) {\n return new DesignerSection(courseId, contextId, popupActivities, videoTime, issubpanel, sectionreturn);\n }\n };\n});\n"],"names":["define","$","Fragment","Templates","Loadingicon","Ajax","Actions","Contact","Notification","SELECTOR","ACTIVITYLI","SECTIONLI","ACTIVITYACTION","SECTIONACTIONMENU","Y","use","courseformatselector","M","course","format","get_section_selector","DesignerSection","courseId","contextId","popupActivities","videoTime","issubpanel","sectionreturn","self","this","isSubpanel","sectionReturn","addClass","delegate","SectionController","sectionLayoutaction","bind","SectionSubmenuSwitcher","RestrictInfo","moduleHandler","sectionRestricted","sectionRestrictHandler","fullDescription","fullmodcontentHandler","trimDescription","trimmodcontentHandler","goToURL","redirectToModule","goToSectionURL","redirectToSection","window","onhashchange","expandSection","length","on","parents","sectionid","attr","distance","document","getElementById","offsetTop","body","scrollTop","setTimeout","scroll","removeClass","contactModal","getElementsByClassName","Array","from","forEach","element","addEventListener","e","preventDefault","undefined","currentTarget","dataset","userid","enhance","popover","prototype","moduleBlock","loadingElement","modules","event","nodeName","target","iscircle","closest","classList","contains","isDescription","isPadlock","ispopupModule","isModHasURL","getAttribute","isCompletionButton","querySelector","click","modurl","location","href","singlesection","sectionurl","sectiontarget","open","sectionID","hash","id","substring","section","title","remove","setAttribute","content","add","scrollIntoView","THIS","fullContent","find","trimcontent","hasClass","sectionRestrictInfo","prev","restrictBlock","updateVideoTimeInstance","sectionId","sectionVideotimes","each","index","module","CreateInstance","args","cmid","call","methodname","then","data","template","JSON","parse","playertype","uniqueid","first","replace","render","templatename","html","js","runTemplateJS","fail","exception","sectionitem","iconBlock","layout","layouttext","text","val","parent","dataid","courseid","options","name","value","promises","when","apply","done","loadFragment","sr","replaceNode","catch","refreshSection","addIconToContainerRemoveOnCompletion","init"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"designer_section.min.js","sources":["../src/designer_section.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/**\n * Implemented designer format js.\n *\n * @module format_designer/designer_section\n * @copyright 2021 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n define(['jquery', 'core/fragment', 'core/templates', 'core/loadingicon', 'core/ajax',\n 'core_course/actions', 'core_message/toggle_contact_button', 'theme_boost/popover', 'core/notification',],\n function($, Fragment, Templates, Loadingicon, Ajax, Actions, Contact, Notification) {\n\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n SECTIONLI: 'li.section',\n ACTIVITYACTION: 'a.cm-edit-action',\n SECTIONACTIONMENU: '.section_action_menu.designer-menu',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n\n /**\n * Control designer format action\n * @param {int} courseId\n * @param {int} contextId\n * @param {array} popupActivities\n * @param {bool} videoTime\n * @param {bool} issubpanel\n * @param {int} sectionreturn\n */\n let DesignerSection = function(courseId, contextId, popupActivities, videoTime, issubpanel, sectionreturn) {\n var self = this;\n self.courseId = courseId;\n self.contextId = contextId;\n self.popupActivities = popupActivities;\n self.videoTime = videoTime;\n self.isSubpanel = issubpanel;\n self.sectionReturn = sectionreturn;\n\n $(\".course-info-block .carousel .carousel-item:nth-child(1)\").addClass('active');\n $(\".course-info-block #courseStaffinfoControls.carousel\").addClass('active');\n\n $('body').delegate(self.SectionController, 'click', self.sectionLayoutaction.bind(this));\n $('body').delegate(self.SectionSubmenuSwitcher, 'click', self.sectionLayoutaction.bind(this));\n\n $(\"body\").delegate(self.RestrictInfo, \"click\", self.moduleHandler.bind(this));\n $(\"body\").delegate(self.sectionRestricted, \"click\", this.sectionRestrictHandler.bind(this));\n $('body').delegate(self.fullDescription, \"click\", self.fullmodcontentHandler.bind(this));\n $('body').delegate(self.trimDescription, \"click\", self.trimmodcontentHandler.bind(this));\n $('body').delegate(self.goToURL, \"click\", self.redirectToModule.bind(this));\n $('body').delegate(self.goToSectionURL, \"click\", self.redirectToSection.bind(this));\n window.onhashchange = function() {\n self.expandSection();\n };\n this.expandSection();\n\n var contactModal = document.getElementsByClassName('toggle-contact-button');\n Array.from(contactModal).forEach(function(element) {\n element.addEventListener('click', function(e) {\n e.preventDefault();\n if (e.currentTarget.dataset.userid != undefined) {\n Contact.enhance(e.currentTarget);\n }\n });\n });\n\n $('.progress .progress-bar[data-toggle=\"popover\"]').popover();\n\n };\n\n /**\n * Selector section controller.\n */\n DesignerSection.prototype.goToURL = '.designer [data-action=\"go-to-url\"]';\n\n DesignerSection.prototype.goToSectionURL = '.designer [data-action=\"go-to-section-url\"]';\n\n DesignerSection.prototype.SectionController = \".designer #section-designer-action .dropdown-menu a\";\n\n DesignerSection.prototype.SectionSubmenuSwitcher\n = \".designer .section_action_menu .dropdown-subpanel a[data-value=section-designer-action] + .dropdown-menu a\";\n\n DesignerSection.prototype.RestrictInfo = \".designer .designer-section-content .call-action-block\";\n\n DesignerSection.prototype.moduleBlock = \".designer .designer-section-content li.activity\";\n\n DesignerSection.prototype.loadingElement = \".icon-loader-block\";\n\n DesignerSection.prototype.sectionRestricted = \".designer .availability-section-block .section-restricted-action\";\n\n DesignerSection.prototype.fullDescription = \".designer-section-content li .fullcontent-summary .mod-description-action\";\n\n DesignerSection.prototype.trimDescription = \".designer-section-content li .trim-summary .mod-description-action\";\n\n DesignerSection.prototype.modules = null;\n\n DesignerSection.prototype.redirectToModule = function(event) {\n let nodeName = event.target.nodeName;\n let preventionNodes = ['a', 'button', 'form'];\n let iscircle = event.target.closest('li.activity').classList.contains('circle-layout');\n let isDescription = event.target.classList.contains('mod-description-action');\n let isPadlock = event.target.classList.contains('fa-lock');\n let ispopupModule = event.target.closest('li.activity').classList.contains('popmodule');\n let isModHasURL = event.target.closest('li.activity div[data-action=\"go-to-url\"]').getAttribute('data-url');\n let isCompletionButton = event.target.closest('button[data-action=\"toggle-manual-completion\"]');\n if ((nodeName in preventionNodes)\n || document.body.classList.contains('editing') || iscircle || isDescription || isPadlock || ispopupModule\n || isModHasURL == '' || isCompletionButton) {\n if (ispopupModule && !document.body.classList.contains('editing')) {\n if (event.target.closest(\"button[data-action='toggle-manual-completion']\") === null &&\n event.target.closest(\".mod-description-action\") === null) {\n var li = event.target.closest('li.activity');\n li.querySelector('a[href]').click();\n }\n }\n return null;\n }\n var card = event.target.closest(\"[data-action=go-to-url]\");\n let modurl = card.getAttribute('data-url');\n window.location.href = modurl;\n return true;\n };\n\n DesignerSection.prototype.redirectToSection = function(event) {\n let isPadlock = event.target.classList.contains('fa-lock');\n if (document.body.classList.contains('editing') || isPadlock) {\n return null;\n }\n var singlesection = event.target.closest(\"[data-action=go-to-section-url]\");\n let sectionurl = singlesection.getAttribute('data-url');\n let sectiontarget = \"_self\";\n let target = singlesection.getAttribute('data-target');\n if (target) {\n sectiontarget = target;\n }\n window.open(sectionurl, sectiontarget);\n return true;\n };\n\n DesignerSection.prototype.expandSection = () => {\n var sectionID = window.location.hash;\n if (sectionID) {\n var id = sectionID.substring(1);\n var section = document.getElementById(id);\n if (section) {\n var title = section.querySelector('.section-header-content');\n if (title) {\n title.classList.remove('collapsed');\n title.setAttribute('aria-expanded', true);\n }\n var content = section.querySelector('.content');\n if (content) {\n content.classList.add('show');\n }\n if (document.getElementById('section-course-accordion') !== null) {\n document.getElementById('section-head-0').classList.add('collapsed');\n document.getElementById('section-content-0').classList.remove('show');\n }\n section.scrollIntoView();\n }\n }\n };\n\n DesignerSection.prototype.fullmodcontentHandler = function(event) {\n var THIS = $(event.currentTarget);\n let fullContent = $(THIS).closest('li.activity').find('.fullcontent-summary');\n let trimcontent = $(THIS).closest('li.activity').find('.trim-summary');\n if (trimcontent.hasClass('summary-hide')) {\n trimcontent.removeClass('summary-hide');\n fullContent.addClass('summary-hide');\n }\n };\n\n DesignerSection.prototype.trimmodcontentHandler = function(event) {\n var THIS = $(event.currentTarget);\n let fullContent = $(THIS).closest('li.activity').find('.fullcontent-summary');\n let trimcontent = $(THIS).closest('li.activity').find('.trim-summary');\n if (fullContent.hasClass('summary-hide')) {\n fullContent.removeClass('summary-hide');\n trimcontent.addClass('summary-hide');\n }\n };\n\n DesignerSection.prototype.sectionRestrictHandler = function(event) {\n var sectionRestrictInfo = $(event.currentTarget).prev();\n if (sectionRestrictInfo) {\n if (!sectionRestrictInfo.hasClass('show')) {\n sectionRestrictInfo.addClass('show');\n } else {\n sectionRestrictInfo.removeClass('show');\n }\n }\n };\n\n DesignerSection.prototype.moduleHandler = function(event) {\n event.preventDefault();\n var restrictBlock = $(event.currentTarget).parents('.restrict-block');\n if (restrictBlock.length) {\n if (!restrictBlock.hasClass('show')) {\n restrictBlock.addClass('show');\n } else {\n restrictBlock.removeClass('show');\n }\n }\n };\n\n DesignerSection.prototype.updateVideoTimeInstance = function(sectionId) {\n var section = \"#\" + sectionId;\n var sectionVideotimes = \"body \"+ section + \" .activity.videotime\";\n if ($(sectionVideotimes).length == 0) {\n return;\n }\n $(sectionVideotimes).each(function(index, module) {\n this.CreateInstance(module);\n }.bind(this));\n };\n\n DesignerSection.prototype.CreateInstance = function (module) {\n if (\n $(module).find('.instancename').length\n && ($(module).find('.vimeo-embed').length\n || $(module).find('.video-js').length)\n ) {\n var cmId = module.getAttribute(\"data-id\");\n var args = {cmid : cmId};\n // Get module instance.\n var promises = Ajax.call([{\n methodname: 'format_designer_get_videotime_instace',\n args: args\n }], true);\n promises[0].then(function(data) {\n var template = JSON.parse(data);\n if (template.playertype == 'videojs') {\n var uniqueid = $(module).find('.video-js').first().attr('id').replace('vimeo-embed-', '');\n } else {\n var uniqueid = $(module).find('.vimeo-embed').first().attr('id').replace('vimeo-embed-', '');\n }\n template.uniqueid = uniqueid;\n Templates.render(template.templatename, template).then(function(html, js) {\n Templates.runTemplateJS(js);\n return true;\n }).fail(Notification.exception);\n });\n }\n };\n\n /**\n * Implementaion swith the section layout.\n * @param {object} event\n */\n DesignerSection.prototype.sectionLayoutaction = function(event) {\n event.preventDefault();\n var self = this;\n let sectionId = event.target.closest('li.section').getAttribute('id');\n var sectionid = event.target.closest('li.section').getAttribute('data-id');\n var sectionitem = document.getElementById(sectionId);\n var iconBlock = \"#\" + sectionId + \" \" + self.loadingElement;\n var layout = $(event.currentTarget).data('value');\n var layouttext = $(event.currentTarget).text();\n $(event.target).parents(\".dropdown\").find(\".btn\").html(layouttext);\n $(event.target).parents(\".dropdown\").find(\".btn\").val(layout);\n $(event.target).parent().find(\"a.dropdown-item\").each(function() {\n $(this).removeClass('active');\n });\n $(event.target).addClass('active');\n let dataid = event.target.closest('li.section').getAttribute('data-id');\n var args = {\n courseid: self.courseId,\n sectionid: dataid,\n options: [{name: $(event.currentTarget).data('option'), value: layout}]\n };\n var promises = Ajax.call([{\n methodname: 'format_designer_set_section_options',\n args: args\n }], true);\n $.when.apply($, promises)\n .done(function() {\n if (self.isSubpanel) {\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'section',\n self.contextId,\n {\n id: sectionid,\n courseid: self.courseId,\n sr: self.sectionReturn,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(sectionitem, html, js);\n }).catch();\n } else {\n const sectionpromise = Actions.refreshSection('#' + sectionId, dataid, 0);\n sectionpromise.then(() => {\n return '';\n }).catch();\n }\n });\n Loadingicon.addIconToContainerRemoveOnCompletion(iconBlock, promises);\n // If videotime exist update the module.\n setTimeout(function() {\n if (self.videoTime) {\n self.updateVideoTimeInstance(sectionId);\n }\n }, 2000);\n };\n\n return {\n init: function(courseId, contextId, popupActivities, videoTime, issubpanel, sectionreturn) {\n return new DesignerSection(courseId, contextId, popupActivities, videoTime, issubpanel, sectionreturn);\n }\n };\n});\n"],"names":["define","$","Fragment","Templates","Loadingicon","Ajax","Actions","Contact","Notification","SELECTOR","ACTIVITYLI","SECTIONLI","ACTIVITYACTION","SECTIONACTIONMENU","Y","use","courseformatselector","M","course","format","get_section_selector","DesignerSection","courseId","contextId","popupActivities","videoTime","issubpanel","sectionreturn","self","this","isSubpanel","sectionReturn","addClass","delegate","SectionController","sectionLayoutaction","bind","SectionSubmenuSwitcher","RestrictInfo","moduleHandler","sectionRestricted","sectionRestrictHandler","fullDescription","fullmodcontentHandler","trimDescription","trimmodcontentHandler","goToURL","redirectToModule","goToSectionURL","redirectToSection","window","onhashchange","expandSection","contactModal","document","getElementsByClassName","Array","from","forEach","element","addEventListener","e","preventDefault","undefined","currentTarget","dataset","userid","enhance","popover","prototype","moduleBlock","loadingElement","modules","event","nodeName","target","iscircle","closest","classList","contains","isDescription","isPadlock","ispopupModule","isModHasURL","getAttribute","isCompletionButton","body","querySelector","click","modurl","location","href","singlesection","sectionurl","sectiontarget","open","sectionID","hash","id","substring","section","getElementById","title","remove","setAttribute","content","add","scrollIntoView","THIS","fullContent","find","trimcontent","hasClass","removeClass","sectionRestrictInfo","prev","restrictBlock","parents","length","updateVideoTimeInstance","sectionId","sectionVideotimes","each","index","module","CreateInstance","args","cmid","call","methodname","then","data","template","JSON","parse","playertype","uniqueid","first","attr","replace","render","templatename","html","js","runTemplateJS","fail","exception","sectionid","sectionitem","iconBlock","layout","layouttext","text","val","parent","dataid","courseid","options","name","value","promises","when","apply","done","loadFragment","sr","replaceNode","catch","refreshSection","addIconToContainerRemoveOnCompletion","setTimeout","init"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/designer_section.js b/amd/src/designer_section.js index dea6828..42d4f05 100644 --- a/amd/src/designer_section.js +++ b/amd/src/designer_section.js @@ -75,18 +75,6 @@ }; this.expandSection(); - if ($('.course-type-flow').length > 0) { - $('.collapse').on('show.bs.collapse', function() { - $(this).parents('li.section').addClass('stack-header-collapsing'); - var sectionid = $(this).parents('li.section').attr('id'); - var section = document.getElementById(sectionid); - var distance = section.offsetTop - document.body.scrollTop; - setTimeout(() => window.scroll(0, distance), 50); - }).on('shown.bs.collapse', function() { - $(this).parents('li.section').removeClass('stack-header-collapsing'); - }); - } - var contactModal = document.getElementsByClassName('toggle-contact-button'); Array.from(contactModal).forEach(function(element) { element.addEventListener('click', function(e) { diff --git a/backup/moodle2/restore_format_designer_plugin.class.php b/backup/moodle2/restore_format_designer_plugin.class.php index 2eaaf78..adc6d88 100644 --- a/backup/moodle2/restore_format_designer_plugin.class.php +++ b/backup/moodle2/restore_format_designer_plugin.class.php @@ -177,11 +177,13 @@ protected function after_restore_section() { } // Restore the courseheaderbgimage. - $this->add_related_files('local_designer', 'courseheaderbgimage', null, null, $this->step->get_task()->get_old_courseid()); + $this->add_related_files('local_designer', 'courseheaderbgimage', null, null, + $this->step->get_task()->get_old_courseid()); // Restore the coursebgimage. $this->add_related_files('local_designer', 'coursebgimage', null, null, $this->step->get_task()->get_old_courseid()); // Restore the additionalcontent. - $this->add_related_files('local_designer', 'additionalcontent', null, null, $this->step->get_task()->get_old_courseid()); + $this->add_related_files('local_designer', 'additionalcontent', null, null, + $this->step->get_task()->get_old_courseid()); // Restore the prerequisiteinfo. $this->add_related_files('local_designer', 'prerequisiteinfo', null, null, $this->step->get_task()->get_old_courseid()); } diff --git a/classes/cache/loader.php b/classes/cache/loader.php new file mode 100644 index 0000000..8dd7140 --- /dev/null +++ b/classes/cache/loader.php @@ -0,0 +1,108 @@ +. + +/** + * Format Designer - Custom cache loader for the smart menus. + * + * @package format_designer + * @copyright 2023 bdecent GmbH + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace format_designer\cache; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot.'/cache/classes/loaders.php'); + +/** + * Custom cache loader to handle the smart menus and items deletion. + */ +class loader extends \cache_application { + + /** + * Delete the cached menus or menu items for all of its users. + * + * Fetch the cache store, generate the keys with menu or item id and keyword of user cache. + * Get the list of cached files by their filename, filenames are stored in the format of "menuid/itemid_u_ userid". + * Generate the key with menu/item id and the keyword of "_u" to get list of all users cache file for this menu/item. + * + * Delete all the files using delete_many method. + * + * @param int $id ID of the menu or item. + * @return void + */ + public function delete_vaild_section_completed_cache($courseid, $sectionid = 0) { + $store = $this->get_store(); + $prefix = "v_s_c_c_{$courseid}"; + if ($list = $store->find_by_prefix($prefix)) { + $keys = array_map(function($key) { + $key = current(explode('-', $key)); + return $key; + }, $list); + $this->delete_many($keys); + } + } + + + public function delete_user_section_completed_cache($courseid, $sectionid = 0, $userid = 0) { + $prefix = "s_c_c_{$courseid}"; + $this->delete_prefix_cache($prefix); + } + + + public function delete_due_overdue_activities_count($courseid, $userid = 0) { + $prefix = "d_o_a_c_c{$courseid}"; + if ($userid) { + $prefix .= "_u{$userid}"; + } + $this->delete_prefix_cache($prefix); + } + + + public function delete_course_progress_uncompletion_criteria($courseid, $userid = 0) { + $prefix = "u_c_c_s{$courseid}"; + if ($userid) { + $prefix .= "_u{$userid}"; + } + $this->delete_prefix_cache($prefix); + } + + public function delete_criteria_progress($courseid, $userid = 0) { + $prefix = "c_p_c{$courseid}"; + if ($userid) { + $prefix .= "_u_{$userid}"; + } + $this->delete_prefix_cache($prefix); + } + + + public function delete_prerequisites_courses() { + $prefix = "data_prereq_main_c"; + $this->delete_prefix_cache($prefix); + } + + public function delete_prefix_cache($prefix) { + $store = $this->get_store(); + if ($list = $store->find_by_prefix($prefix)) { + $keys = array_map(function($key) { + $key = current(explode('-', $key)); + return $key; + }, $list); + $this->delete_many($keys); + } + } +} diff --git a/classes/events.php b/classes/events.php index 1a3b245..3cceeb8 100644 --- a/classes/events.php +++ b/classes/events.php @@ -24,6 +24,9 @@ namespace format_designer; +require_once($CFG->dirroot . "/course/format/designer/lib.php"); + + /** * Designer format event observer. */ @@ -42,14 +45,18 @@ public static function course_section_created($event) { $sectionid = $data['objectid']; $sectionnum = $data['other']['sectionnum']; $contextid = $data['contextid']; - $courseid = $data['courseid'];; + $courseid = $data['courseid']; $filearea = 'sectiondesignbackground'; $option = get_config('format_designer', 'sectiondesignerbackgroundimage'); $coursecontext = \context_course::instance($courseid); + if (course_get_format($courseid)->get_course()->format !== 'designer') { return true; } + // course_section_cache_updated. + self::course_section_cache_updated($courseid, $sectionid); + $format = course_get_format($courseid); $options = $format->section_format_options(); $sectiondata = ['id' => $sectionid]; @@ -58,8 +65,161 @@ public static function course_section_created($event) { $sectiondata[$name] = get_config('format_designer', $name); } } - if (!defined('NO_OUTPUT_BUFFERING') || (defined('NO_OUTPUT_BUFFERING') && !NO_OUTPUT_BUFFERING)) { + if (!defined('NO_OUTPUT_BUFFERING') || (defined('NO_OUTPUT_BUFFERING') && !NO_OUTPUT_BUFFERING) + && (!defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0')) { $format->update_section_format_options($sectiondata); } } + + /** + * After course deleted, deleted the format_designer_options data related to the format_designer options. + * + * @param object $event + * @return void + */ + public static function course_deleted($event) { + global $DB; + $courseid = $event->courseid; + $DB->delete_records('format_designer_options', ['courseid' => $courseid]); + $cache = format_designer_get_cache_object(); + $cache->delete_prerequisites_courses(); + self::course_cache_updated($courseid); + } + + public static function course_completion_updated($event) { + $data = $event->get_data(); + $courseid = $data['courseid']; + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + self::course_cache_updated($courseid); + } + + public static function course_updated($event) { + $courseid = $event->courseid; + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + self::course_cache_updated($courseid); + } + + public static function course_completed($event) { + $userid = $event->relateduserid; + $courseid = $event->courseid; + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + self::course_user_cache_updated($courseid, $userid); + } + + public static function course_module_completion_updated($event) { + $userid = $event->relateduserid; + $courseid = $event->courseid; + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + self::course_user_cache_updated($courseid, $userid); + } + + + public static function course_module_created($event) { + self::course_section_module_cache_updated($event->courseid, $event->objectid); + } + + /** + * After course module deleted, deleted the format_designer_options data related to the format_designer options. + * + * @param object $event + * @return void + */ + public static function course_module_deleted($event) { + global $DB; + $courseid = $event->courseid; + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + $cmid = $event->objectid; + $DB->delete_records('format_designer_options', ['courseid' => $courseid, 'cmid' => $cmid]); + + // Clear cache. + self::course_section_module_cache_updated($event->courseid, $event->objectid); + } + + public static function course_module_updated($event) { + self::course_section_module_cache_updated($event->courseid, $event->objectid); + } + + + public static function course_section_deleted($event) { + $data = $event->get_data(); + $sectionid = $data['objectid']; + $courseid = $data['courseid']; + self::course_section_cache_updated($courseid, $sectionid); + } + + public static function course_section_updated($event) { + $data = $event->get_data(); + $sectionid = $data['objectid']; + $courseid = $data['courseid']; + self::course_section_cache_updated($courseid, $sectionid); + } + + public static function course_cache_updated($courseid) { + $cache = format_designer_get_cache_object(); + $cache->delete_vaild_section_completed_cache($courseid); + $cache->delete_user_section_completed_cache($courseid); + $cache->delete_course_progress_uncompletion_criteria($courseid); + $cache->delete_due_overdue_activities_count($courseid); + $cache->delete_criteria_progress($courseid); + $cache->delete("g_c_a{$courseid}"); + $cache->delete("g_c_s_ic{$courseid}"); + } + + + public static function course_user_cache_updated($courseid , $userid) { + $cache = format_designer_get_cache_object(); + $cache->delete_vaild_section_completed_cache($courseid); + $cache->delete_user_section_completed_cache($courseid); + $cache->delete_course_progress_uncompletion_criteria($courseid, $userid); + $cache->delete_due_overdue_activities_count($courseid, $userid); + $cache->delete_criteria_progress($courseid, $userid); + $cache->delete("g_c_a{$courseid}"); + $cache->delete("g_c_s_ic{$courseid}"); + } + + + public static function course_section_module_cache_updated($courseid, $cmid, $sectionid = 0) { + global $DB; + + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + + $cm = $DB->get_record("course_modules", ['id' => $cmid]); + // Clear cache. + $cache = format_designer_get_cache_object(); + $cache->delete_vaild_section_completed_cache($courseid); + $cache->delete_user_section_completed_cache($courseid); + $cache->delete_course_progress_uncompletion_criteria($courseid); + $cache->delete_due_overdue_activities_count($courseid); + $cache->delete_criteria_progress($courseid); + $cache->delete("fdo_cm_j_{$courseid}"); + $cache->delete("g_c_a{$courseid}"); + $cache->delete("g_c_s_ic{$courseid}"); + } + + public static function course_section_cache_updated($courseid, $sectionid) { + if (course_get_format($courseid)->get_course()->format !== 'designer') { + return true; + } + // Clear cache. + $cache = format_designer_get_cache_object(); + $cache->delete_vaild_section_completed_cache($courseid, $sectionid); + $cache->delete_user_section_completed_cache($courseid, $sectionid); + $cache->delete_due_overdue_activities_count($courseid); + $cache->delete_course_progress_uncompletion_criteria($courseid); + $cache->delete_criteria_progress($courseid); + $cache->delete("g_c_a{$courseid}"); + $cache->delete("g_c_s_ic{$courseid}"); + } } diff --git a/classes/external/set_section_options.php b/classes/external/set_section_options.php index 516433b..0f20354 100644 --- a/classes/external/set_section_options.php +++ b/classes/external/set_section_options.php @@ -146,8 +146,7 @@ public static function get_module($id, $sectionid, $sectionreturn = null) { $renderer = $PAGE->get_renderer('format_designer'); $format = course_get_format($course); - $sectiontype = $format->get_section_option($sectionid, 'sectiontype') ?: - get_config('format_designer', 'sectiontype'); + $sectiontype = $format->get_section_option($sectionid, 'sectiontype') ?: get_config('format_designer', 'sectiontype'); $section = (object) ['sectiontype' => $sectiontype]; $cmlistdata = $renderer->render_course_module($cm, $sectionreturn, [], $section); diff --git a/classes/helper.php b/classes/helper.php index 7664852..c6f3e06 100644 --- a/classes/helper.php +++ b/classes/helper.php @@ -89,8 +89,10 @@ public function get_course_staff_users($course) { // Course context. $coursecontext = \context_course::instance($course->id); + // List of staff users ids for the course. $staffids = $this->get_staffs_users($course); + // Staff users are not found. if (empty($staffids)) { return []; @@ -108,6 +110,7 @@ public function get_course_staff_users($course) { } } } + $roles = get_user_roles($coursecontext, $userid, false); array_map(function($role) { $role->name = role_get_name($role); @@ -154,17 +157,12 @@ public function get_course_staff_users($course) { */ protected function get_staffs_users($course) { $staffids = []; - $staffroleids = explode(",", $course->coursestaff); - $enrolusers = enrol_get_course_users_roles($course->id); - if (!empty($enrolusers)) { - foreach ($enrolusers as $userid => $roles) { - foreach ($staffroleids as $staffid) { - if (isset($roles[$staffid])) { - $staffids[] = $userid; - } - } - } + if (!empty($course->coursestaff)) { + $staffroleids = explode(",", $course->coursestaff); + $coursecontext = \context_course::instance($course->id); + $staffids = get_role_users($staffroleids, $coursecontext, false, 'ra.id, u.lastname, u.firstname, u.id'); } - return array_unique($staffids); + return array_keys($staffids); + } } diff --git a/classes/options.php b/classes/options.php index 6a28304..8d0bc6f 100644 --- a/classes/options.php +++ b/classes/options.php @@ -133,24 +133,33 @@ public static function is_mod_completed($mod) { * @return boolean */ public static function is_vaild_section_completed($section, $course, $modinfo, $onlyrelative = false) { - - $completioninfo = new \completion_info($course); - $completionactivities = array_column($completioninfo->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY), 'moduleinstance'); - if (!empty($modinfo->sections[$section->section]) && $section->uservisible) { - foreach ($modinfo->sections[$section->section] as $modnumber) { - $mod = $modinfo->cms[$modnumber]; - if (!empty($mod)) { - if ($onlyrelative && !in_array($mod->id, $completionactivities)) { - continue; - } - $cmcompletion = new cm_completion($mod); - if ($mod->is_visible_on_course_page() && $cmcompletion->get_completion_mode() != COMPLETION_TRACKING_NONE) { - return true; + $cache = format_designer_get_cache_object(); + // vaild section completed c _courseid _sectionid_. + $key = "v_s_c_c_{$course->id}_s_{$section->id}"; + if (!$cache->get($key)) { + $completioninfo = new \completion_info($course); + $completionactivities = array_column($completioninfo->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY), 'moduleinstance'); + if (!empty($modinfo->sections[$section->section]) && $section->uservisible) { + foreach ($modinfo->sections[$section->section] as $modnumber) { + $mod = $modinfo->cms[$modnumber]; + if (!empty($mod)) { + if ($onlyrelative && !in_array($mod->id, $completionactivities)) { + continue; + } + $cmcompletion = new cm_completion($mod); + if ($mod->is_visible_on_course_page() && $cmcompletion->get_completion_mode() != COMPLETION_TRACKING_NONE) { + $cache->set($key, "true"); + break; + } } } } + + if (!$cache->get($key)) { + $cache->set($key, "false"); + } } - return false; + return $cache->get($key); } /** @@ -165,29 +174,41 @@ public static function is_vaild_section_completed($section, $course, $modinfo, $ * @return bool|array Result of section completion or Current progress data. */ public static function is_section_completed($section, $course, $modinfo, $result = false, $onlyrelative = false) { - + global $USER; $completioninfo = new \completion_info($course); $completionactivities = array_column($completioninfo->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY), 'moduleinstance'); $cmcompleted = 0; $totalmods = 0; $issectioncompletion = 0; - if (!empty($modinfo->sections[$section->section]) && $section->uservisible) { - foreach ($modinfo->sections[$section->section] as $modnumber) { - $mod = $modinfo->cms[$modnumber]; - if (!empty($mod)) { - if ($onlyrelative && !in_array($mod->id, $completionactivities)) { - continue; - } - $cmcompletion = new cm_completion($mod); - if ($mod->is_visible_on_course_page() && $cmcompletion->get_completion_mode() != COMPLETION_TRACKING_NONE) { - $totalmods++; - $cmcompletionstate = $cmcompletion->get_completion_state(); - if ($cmcompletionstate == COMPLETION_COMPLETE || $cmcompletionstate == COMPLETION_COMPLETE_PASS ) { - $cmcompleted++; + $cache = format_designer_get_cache_object(); + // vaild section completed c _courseid _sectionid_. + $cachekey = "s_c_c_{$course->id}_s_{$section->id}_u_{$USER->id}"; + if (!$cache->get($cachekey)) { + if (!empty($modinfo->sections[$section->section]) && $section->uservisible) { + foreach ($modinfo->sections[$section->section] as $modnumber) { + $mod = $modinfo->cms[$modnumber]; + if (!empty($mod)) { + if ($onlyrelative && !in_array($mod->id, $completionactivities)) { + continue; + } + $cmcompletion = new cm_completion($mod); + if ($mod->is_visible_on_course_page() && $cmcompletion->get_completion_mode() != COMPLETION_TRACKING_NONE) { + $totalmods++; + $cmcompletionstate = $cmcompletion->get_completion_state(); + if ($cmcompletionstate == COMPLETION_COMPLETE || $cmcompletionstate == COMPLETION_COMPLETE_PASS ) { + $cmcompleted++; + } } } } } + $cache->set($cachekey, ['totalmods' => $totalmods, 'cmcompleted' => $cmcompleted]); + } + + if ($cache->get($cachekey)) { + $cachedata = $cache->get($cachekey); + $totalmods = $cachedata['totalmods']; + $cmcompleted = $cachedata['cmcompleted']; } if ($totalmods) { diff --git a/classes/output/cm_completion.php b/classes/output/cm_completion.php index 483279d..610494f 100644 --- a/classes/output/cm_completion.php +++ b/classes/output/cm_completion.php @@ -151,7 +151,7 @@ final public function get_completion_mode(): int { * @return int */ final public function get_completion_state(): int { - return $this->get_completion_data()->completionstate; + return !is_null($this->get_completion_data()->completionstate) ? $this->get_completion_data()->completionstate : 0; } /** @@ -211,7 +211,7 @@ final public function is_overridden(): bool { * @return int */ final public function get_completion_date(): int { - return $this->get_completion_data()->timemodified; + return !is_null($this->get_completion_data()->timemodified) ? $this->get_completion_data()->timemodified : 0; } /** @@ -466,8 +466,8 @@ private function get_time_ago(int $timestamp): string { $ago = new \DateTime('@' . $timestamp); $diff = $now->diff($ago); - $diff->w = floor($diff->d / 7); - $diff->d -= $diff->w * 7; + $weeks = floor($diff->d / 7); + $diff->d -= $weeks * 7; $string = [ 'y' => get_string('timeagoyear', 'format_designer'), @@ -479,13 +479,13 @@ private function get_time_ago(int $timestamp): string { 's' => get_string('timeagosecond', 'format_designer'), ]; foreach ($string as $k => &$v) { - if ($diff->$k) { - $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); + $value = ($k == 'w') ? $weeks : $diff->$k; + if ($value) { + $v = $value . ' ' . $v . ($value > 1 ? 's' : ''); } else { unset($string[$k]); } } - $string = array_slice($string, 0, 1); return $string ? implode(', ', $string) . ' ' . get_string('timeago', 'format_designer') : get_string('timeagojustnow', 'format_designer'); diff --git a/classes/output/courseformat/content.php b/classes/output/courseformat/content.php index 5805637..bee06d5 100644 --- a/classes/output/courseformat/content.php +++ b/classes/output/courseformat/content.php @@ -43,16 +43,66 @@ class content extends content_base { */ protected $hasaddsection = true; + /** * Export this data so it can be used as the context for a mustache template (core/inplace_editable). * - * @param renderer_base $output typically, the renderer that's calling this function - * @return stdClass data context for a mustache template + * @param \renderer_base $output typically, the renderer that's calling this function + * @return \stdClass data context for a mustache template */ public function export_for_template(\renderer_base $output) { - global $PAGE; - $data = parent::export_for_template($output); - $data->course = $this->format->get_course(); + global $PAGE, $CFG; + $format = $this->format; + $course = $this->format->get_course(); + + $sections = $this->export_sections($output); + $initialsection = ''; + + $data = (object) [ + 'title' => $format->page_title(), // This method should be in the course_format class. + 'initialsection' => $initialsection, + 'sections' => $sections, + 'format' => $format->get_format(), + 'sectionreturn' => null, + ]; + + $singlesectionnum = $format->get_sectionnum(); + $singlesectionnumhandled = false; + if ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && optional_param('section', -1, PARAM_INT) >= 0) { + $singlesectionnumhandled = true; + } + + // The single section format has extra navigation. + if ($singlesectionnumhandled) { + $singlesectionnum = empty($singlesectionnum) ? 0 : $singlesectionnum; + if (!$PAGE->theme->usescourseindex) { + $sectionnavigation = new $this->sectionnavigationclass($format, $singlesectionnum); + $data->sectionnavigation = $sectionnavigation->export_for_template($output); + + $sectionselector = new $this->sectionselectorclass($format, $sectionnavigation); + $data->sectionselector = $sectionselector->export_for_template($output); + } + $data->hasnavigation = true; + $data->singlesection = array_shift($data->sections); + $data->sectionreturn = $singlesectionnum; + } + + if ($this->hasaddsection) { + $addsection = new $this->addsectionclass($format); + $data->numsections = $addsection->export_for_template($output); + } + + if ($format->show_editor()) { + $bulkedittools = new $this->bulkedittoolsclass($format); + $data->bulkedittools = $bulkedittools->export_for_template($output); + } + + $data->course = $course; + + if ($course->coursetype == DESIGNER_TYPE_KANBAN) { + $data->initialsection = array_shift($sections); + $data->sections = $sections; + } return $data; } @@ -73,6 +123,7 @@ protected function export_sections(\renderer_base $output): array { $sections = []; $stealthsections = []; $numsections = $format->get_last_section_number(); + foreach ($this->get_sections_to_display($modinfo) as $sectionnum => $thissection) { // The course/view.php check the section existence but the output can be called // from other parts so we need to check it. @@ -124,15 +175,17 @@ protected function export_sections(\renderer_base $output): array { * @return section_info[] an array of section_info to display */ private function get_sections_to_display(course_modinfo $modinfo): array { - $singlesection = $this->format->get_section_number(); - if ($singlesection) { - return [ - $modinfo->get_section_info(0), + global $CFG; + $singlesection = $this->format->get_sectionnum(); + $course = $this->format->get_course(); + if ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && optional_param('section', -1, PARAM_INT) >= 0) { + $singlesection = empty($singlesection) ? 0 : $singlesection; + $sections = [ $modinfo->get_section_info($singlesection), ]; + return $sections; } - - return $modinfo->get_section_info_all(); + return $modinfo->get_listed_section_info_all(); } } diff --git a/classes/output/courseformat/content/cm/controlmenu.php b/classes/output/courseformat/content/cm/controlmenu.php index 647bdea..39d4aca 100644 --- a/classes/output/courseformat/content/cm/controlmenu.php +++ b/classes/output/courseformat/content/cm/controlmenu.php @@ -61,18 +61,13 @@ public function get_action_menu(\renderer_base $output): ?action_menu { // Convert control array into an action_menu. $menu = new action_menu(); - if (method_exists($menu, 'set_kebab_trigger')) { - $menu->set_kebab_trigger(get_string('edit')); - } else { - $icon = $output->pix_icon('i/menu', get_string('edit')); - $menu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center'); - } + $menu->set_kebab_trigger(get_string('edit')); $menu->attributes['class'] .= ' section-cm-edit-actions commands'; // Prioritise the menu ahead of all other actions. $menu->prioritise = true; - $ownerselector = $displayoptions['ownerselector'] ?? '#module-' . $mod->id; + $ownerselector = $this->displayoptions['ownerselector'] ?? '#module-' . $mod->id; $menu->set_owner_selector($ownerselector); foreach ($controls as $control) { diff --git a/classes/output/courseformat/content/section.php b/classes/output/courseformat/content/section.php index 80a019b..ad18a34 100644 --- a/classes/output/courseformat/content/section.php +++ b/classes/output/courseformat/content/section.php @@ -26,6 +26,7 @@ use renderer_base; use stdClass; +use context_course; /** * Base class to render a course section. @@ -45,7 +46,7 @@ class section extends \core_courseformat\output\local\content\section { * @return bool if the cm has name data */ protected function add_format_data(stdClass &$data, array $haspartials, renderer_base $output): bool { - global $PAGE; + global $PAGE, $CFG; $section = $this->section; $format = $this->format; @@ -56,6 +57,8 @@ protected function add_format_data(stdClass &$data, array $haspartials, renderer $data->collapsemenu = true; } + $data->contentcollapsed = $this->is_section_collapsed(); + if ($format->is_section_current($section)) { $data->iscurrent = true; $data->currentlink = get_accesshide( @@ -64,15 +67,58 @@ protected function add_format_data(stdClass &$data, array $haspartials, renderer } $renderer = $this->format->get_renderer($PAGE); - if ($data->iscoursedisplaymultipage && !$format->get_section_number()) { - $formatdata = (array) $renderer->render_section_data($this->section, $this->format->get_course(), false, true); + $sectionnum = $format->get_sectionnum(); + + if ($data->iscoursedisplaymultipage && !$sectionnum) { + $pagesection = optional_param('section', -1, PARAM_INT); + $sectionnum = empty($sectionnum) && ($pagesection >= 0) ? 0 : false; + if ($pagesection >= 0) { + $formatdata = (array) $renderer->render_section_data($this->section, $this->format->get_course(), $sectionnum); + } else { + $formatdata = (array) $renderer->render_section_data($this->section, $this->format->get_course(), false, true); + } } else { $formatdata = (array) $renderer->render_section_data( - $this->section, $this->format->get_course(), $format->get_section_number() + $this->section, $this->format->get_course(), $sectionnum ); } $data = (object) array_merge((array) $data, $formatdata); return true; } + + /** + * Add the section editor attributes to the data structure. + * + * @param stdClass $data the current cm data reference + * @param renderer_base $output typically, the renderer that's calling this function + * @return bool if the cm has name data + */ + protected function add_editor_data(stdClass &$data, renderer_base $output): bool { + $course = $this->format->get_course(); + $coursecontext = context_course::instance($course->id); + $editcaps = []; + if (has_capability('moodle/course:sectionvisibility', $coursecontext)) { + $editcaps = ['moodle/course:sectionvisibility']; + } + if (!$this->format->show_editor($editcaps)) { + return false; + } + + // In a single section page the control menu is located in the page header. + if (empty($this->hidecontrols)) { + $controlmenu = new $this->controlmenuclass($this->format, $this->section); + $data->controlmenu = $controlmenu->export_for_template($output); + } + + $singlesection = $this->format->get_sectionnum(); + if (!$this->isstealth) { + $data->cmcontrols = $output->course_section_add_cm_control( + $course, + $this->section->section, + $singlesection + ); + } + return true; + } } diff --git a/classes/output/courseformat/content/section/cmitem.php b/classes/output/courseformat/content/section/cmitem.php index 3d7cd2e..263808a 100644 --- a/classes/output/courseformat/content/section/cmitem.php +++ b/classes/output/courseformat/content/section/cmitem.php @@ -69,6 +69,7 @@ public function export_for_template(\renderer_base $output): stdClass { 'cmformat' => $item->export_for_template($output), 'hasinfo' => $hasinfo, 'indent' => ($format->uses_indentation()) ? $mod->indent : 0, + 'groupmode' => $mod->groupmode, ]; $this->render_course_module($mod, $data, $output); return (object) $data; diff --git a/classes/output/courseformat/content/section/cmlist.php b/classes/output/courseformat/content/section/cmlist.php index 87ff73f..463e3cc 100644 --- a/classes/output/courseformat/content/section/cmlist.php +++ b/classes/output/courseformat/content/section/cmlist.php @@ -100,8 +100,7 @@ public function render_section_content($output, $htmlparse = false) { } $sectionlayoutclass = 'link-layout'; - $sectiontype = $this->format->get_section_option($section->id, 'sectiontype') ?: - get_config('format_designer', 'sectiontype'); + $sectiontype = $this->format->get_section_option($section->id, 'sectiontype') ?: 'default'; if ($sectiontype == 'list') { $sectionlayoutclass = "list-layout"; } else if ($sectiontype == 'cards') { @@ -119,7 +118,6 @@ public function render_section_content($output, $htmlparse = false) { $templatename = 'layouts_' . $sectiontype . '/layout/section_layout_' . $sectiontype; } } - $templatename = $output->is_template_exists($templatename); $data->sectionlayoutclass = $sectionlayoutclass; $cmscontent = $OUTPUT->render_from_template($templatename, $data); @@ -127,6 +125,7 @@ public function render_section_content($output, $htmlparse = false) { return $cmscontent; } $data->cmscontent = $cmscontent; + $data->groupmode = isset($mod->groupmode) ? $mod->groupmode : ''; return $data; } } diff --git a/classes/output/courseformat/content/section/controlmenu.php b/classes/output/courseformat/content/section/controlmenu.php index c0631d9..2a9a31d 100644 --- a/classes/output/courseformat/content/section/controlmenu.php +++ b/classes/output/courseformat/content/section/controlmenu.php @@ -45,22 +45,44 @@ */ class controlmenu extends controlmenu_base { - /** @var course_format the course format class */ - protected $format; - - /** @var section_info the course section class */ - protected $section; - /** - * Constructor. + * Generate the default section action menu. + * + * This method is public in case some block needs to modify the menu before output it. * - * @param course_format $format the course format - * @param section_info $section the section info + * @param \renderer_base $output typically, the renderer that's calling this function + * @return action_menu|null the activity action menu */ - public function __construct(course_format $format, section_info $section) { - $this->format = $format; - $this->course = $format->get_course(); - $this->section = $section; + public function get_default_action_menu(\renderer_base $output): ?action_menu { + $controls = $this->section_control_items(); + if (empty($controls)) { + return null; + } + + // Convert control array into an action_menu. + $menu = new action_menu(); + $menu->set_kebab_trigger(get_string('edit')); + $menu->attributes['class'] .= ' section-actions'; + + foreach ($controls as $value) { + $value = (array) $value; + $url = empty($value['url']) ? '' : $value['url']; + $icon = empty($value['icon']) ? '' : $value['icon']; + if ($icon instanceof pix_icon) { + $icon = $icon->pix; + } + $name = empty($value['name']) ? '' : $value['name']; + $attr = empty($value['attr']) ? [] : $value['attr']; + $class = empty($value['pixattr']['class']) ? '' : $value['pixattr']['class']; + $al = new action_menu_link_secondary( + new moodle_url($url), + new pix_icon($icon, '', null, ['class' => "smallicon " . $class]), + $name, + $attr + ); + $menu->add($al); + } + return $menu; } /** @@ -74,8 +96,13 @@ public function export_for_template(\renderer_base $output): stdClass { $section = $this->section; $hassectiontypes = true; - if (($this->course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && !$this->format->get_section_number()) - || $this->course->coursetype == DESIGNER_TYPE_FLOW) { + + $sectionnum = $this->format->get_sectionnum(); + + $course = $this->format->get_course(); + + if (($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && !$sectionnum) + || $course->coursetype == DESIGNER_TYPE_FLOW) { $hassectiontypes = false; } @@ -117,30 +144,30 @@ public function export_for_template(\renderer_base $output): stdClass { 'name' => get_string('link', 'format_designer'), 'active' => empty($this->format->get_section_option($section->id, 'sectiontype')) || $this->format->get_section_option($section->id, 'sectiontype') == 'default', - 'url' => new moodle_url('/course/view.php', ['id' => $this->course->id], 'section-' . $section->section), + 'url' => new moodle_url('/course/view.php', ['id' => $course->id], 'section-' . $section->section), ], [ 'type' => 'list', 'name' => get_string('list', 'format_designer'), 'active' => $this->format->get_section_option($section->id, 'sectiontype') == 'list', - 'url' => new moodle_url('/course/view.php', ['id' => $this->course->id], 'section-' . $section->section), + 'url' => new moodle_url('/course/view.php', ['id' => $course->id], 'section-' . $section->section), ], [ 'type' => 'cards', 'name' => get_string('cards', 'format_designer'), 'active' => $this->format->get_section_option($section->id, 'sectiontype') == 'cards', - 'url' => new moodle_url('/course/view.php', ['id' => $this->course->id], 'section-' . $section->section), + 'url' => new moodle_url('/course/view.php', ['id' => $course->id], 'section-' . $section->section), ], ]; if (format_designer_has_pro()) { - $prosectiontypes = \local_designer\info::get_layout_menu($this->format, $section, $this->course); + $prosectiontypes = \local_designer\info::get_layout_menu($this->format, $section, $course); $sectiontypes = array_merge($sectiontypes, $prosectiontypes); } } - $data = (object)[ + $data = (object) [ 'menu' => $output->render($menu), 'hasmenu' => true, 'id' => $section->id, @@ -148,7 +175,6 @@ public function export_for_template(\renderer_base $output): stdClass { 'is_subpanel' => format_designer_is_support_subpanel(), 'hassectiontypes' => $hassectiontypes, ]; - return $data; } @@ -162,12 +188,14 @@ public function export_for_template(\renderer_base $output): stdClass { * @return array of edit control items */ public function section_control_items() { - global $USER; + global $USER, $PAGE, $CFG; $format = $this->format; $section = $this->section; $course = $format->get_course(); - $sectionreturn = $format->get_section_number(); + + $sectionreturn = !is_null($format->get_sectionid()) ? $format->get_sectionnum() : null; + $user = $USER; $usecomponents = $format->supports_components(); @@ -178,16 +206,24 @@ public function section_control_items() { $baseurl = course_get_url($course, $sectionreturn); $baseurl->param('sesskey', sesskey()); + $course = $format->get_course(); + $controls = []; + // Only show the view link if we are not already in the section view page. + if ($PAGE->pagetype !== 'section-view-' . $course->format) { + $controls['view'] = [ + 'url' => new moodle_url('/course/view.php', ['id' => $course->id, 'section' => $section->section]), + 'icon' => 'i/viewsection', + 'name' => get_string('view'), + 'pixattr' => ['class' => ''], + 'attr' => ['class' => 'icon view'], + ]; + } + if (!$isstealth && has_capability('moodle/course:update', $coursecontext, $user)) { - if ($section->section > 0 - && get_string_manager()->string_exists('editsection', 'format_'.$format->get_format())) { - $streditsection = get_string('editsection', 'format_'.$format->get_format()); - } else { - $streditsection = get_string('editsection'); - } + $streditsection = get_string('editsection', 'format_'.$format->get_format()); $controls['edit'] = [ 'url' => new moodle_url('/course/editsection.php', ['id' => $section->id, 'sr' => $sectionreturn]), 'icon' => 'i/settings', @@ -197,8 +233,8 @@ public function section_control_items() { ]; $hassectiontypes = true; - if (($this->course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && !$this->format->get_section_number()) - || $this->course->coursetype == DESIGNER_TYPE_FLOW) { + if (($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && !$sectionreturn) + || $course->coursetype == DESIGNER_TYPE_FLOW) { $hassectiontypes = false; } @@ -214,6 +250,11 @@ public function section_control_items() { $duplicatesectionurl = clone($baseurl); $duplicatesectionurl->param('section', $section->section); $duplicatesectionurl->param('duplicatesection', $section->section); + + if (!is_null($sectionreturn)) { + $duplicatesectionurl->param('sr', $sectionreturn); + } + $controls['duplicate'] = [ 'url' => $duplicatesectionurl, 'icon' => 't/copy', @@ -225,6 +266,11 @@ public function section_control_items() { if ($section->section) { $url = clone($baseurl); + + if (!is_null($sectionreturn)) { + $url->param('sectionid', $format->get_sectionid()); + } + if (!$isstealth) { if (has_capability('moodle/course:sectionvisibility', $coursecontext, $user)) { $strhidefromothers = get_string('hidefromothers', 'format_' . $course->format); @@ -319,14 +365,21 @@ public function section_control_items() { } else { $strdelete = get_string('deletesection'); } + + $params = [ + 'id' => $section->id, + 'delete' => 1, + 'sesskey' => sesskey(), + ]; + + if (!is_null($sectionreturn)) { + + $params['sr'] = $sectionreturn; + + } $url = new moodle_url( '/course/editsection.php', - [ - 'id' => $section->id, - 'sr' => $sectionreturn, - 'delete' => 1, - 'sesskey' => sesskey(), - ] + $params, ); $controls['delete'] = [ 'url' => $url, @@ -374,7 +427,6 @@ public function section_control_items() { * @return choicelist */ public function get_choice_list($section): choicelist { - $sectiontype = $this->format->get_section_option($section->id, 'sectiontype'); $sectiontype = $sectiontype ? $sectiontype : get_config('format_designer', 'sectiontype'); $choice = $this->create_choice_list($section); @@ -399,7 +451,7 @@ protected function create_choice_list($section): choicelist { 'cards' => get_string('cards', 'format_designer'), ]; if (format_designer_has_pro()) { - $prosectiontypes = \local_designer\info::get_layout_menu($this->format, $section, $this->course); + $prosectiontypes = \local_designer\info::get_layout_menu($this->format, $section, $this->format->get_course()); $lists = array_merge($lists, array_column($prosectiontypes, 'name', 'type')); } diff --git a/classes/output/courseformat/content/section/header.php b/classes/output/courseformat/content/section/header.php index 8c12ec5..a0cdff3 100644 --- a/classes/output/courseformat/content/section/header.php +++ b/classes/output/courseformat/content/section/header.php @@ -96,13 +96,12 @@ public function export_for_template(\renderer_base $output): stdClass { } } $data->name = get_section_name($course, $section); - if (method_exists($format, 'get_format_string')) { - $data->selecttext = $format->get_format_string('selectsection', $data->name); - } + $data->selecttext = $format->get_format_string('selectsection', $data->name); $bodyclasses = explode(" ", $PAGE->bodyclasses); - if ((!$format->get_section_number() || !in_array('format-designer-single-section', $bodyclasses)) + $sectionreturn = $format->get_sectionnum(); + if ((!$sectionreturn || !in_array('format-designer-single-section', $bodyclasses)) && class_exists('core_courseformat\output\local\content\bulkedittoggler')) { $data->sectionbulk = true; } diff --git a/classes/output/courseformat/state/cm.php b/classes/output/courseformat/state/cm.php index 5bccf0e..4d12ed0 100644 --- a/classes/output/courseformat/state/cm.php +++ b/classes/output/courseformat/state/cm.php @@ -38,53 +38,22 @@ public function export_for_template(\renderer_base $output): stdClass { global $USER, $CFG; $format = $this->format; - $section = $this->section; - $cm = $this->cm; $course = $format->get_course(); - if (class_exists("\core_external\util")) { - $name = \core_external\util::format_string($cm->name, $cm->context, true); - } else { - $name = external_format_string($cm->name, $cm->context, true); - } - $data = (object)[ - 'id' => $cm->id, - 'anchor' => "module-{$cm->id}", - 'name' => $name, - 'visible' => !empty($cm->visible), - 'sectionid' => $section->id, - 'sectionnumber' => $section->section, - 'uservisible' => $cm->uservisible, - 'hascmrestrictions' => $this->get_has_restrictions(), - ]; - + $format = $this->format; + $cm = $this->cm; + $data = parent::export_for_template($output); $usecourseindexcustom = \format_designer\options::get_option($cm->id, 'customtitleusecourseindex'); if ($usecourseindexcustom) { $data->designercmname = $format->get_cm_secondary_title($cm); } - // Check the user access type to this cm. - $info = new info_module($cm); - $data->accessvisible = ($data->visible && $info->is_available_for_all()); - - // Add url if the activity is compatible. - $url = $cm->url; - if ($url) { - $data->url = $url->out(); - } - - if ($this->exportcontent) { - $data->content = $output->course_section_updated_cm_item($format, $section, $cm); - } // Completion status. $completioninfo = new completion_info($course); - $data->istrackeduser = $completioninfo->is_tracked_user($USER->id); if ($data->istrackeduser && $completioninfo->is_enabled($cm)) { $completiondata = $completioninfo->get_data($cm); $data->completionstate = $completiondata->completionstate; } - return $data; } - } diff --git a/classes/output/courseformat/state/section.php b/classes/output/courseformat/state/section.php new file mode 100644 index 0000000..b7e96d9 --- /dev/null +++ b/classes/output/courseformat/state/section.php @@ -0,0 +1,95 @@ +. + +namespace format_designer\output\courseformat\state; + +use moodle_url; +use stdClass; + +/** + * Contains the ajax update section structure. + * + * @package format_designer + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class section extends \core_courseformat\output\local\state\section { + + /** + * Export this data so it can be used as state object in the course editor. + * + * @param \renderer_base $output typically, the renderer that's calling this function + * @return array data context for a mustache template + */ + public function export_for_template(\renderer_base $output): stdClass { + global $CFG; + $format = $this->format; + $course = $format->get_course(); + $section = $this->section; + $modinfo = $format->get_modinfo(); + + $indexcollapsed = false; + $contentcollapsed = false; + $preferences = $format->get_sections_preferences(); + if (isset($preferences[$section->id])) { + $sectionpreferences = $preferences[$section->id]; + if (!empty($sectionpreferences->contentcollapsed)) { + $contentcollapsed = true; + } + if (!empty($sectionpreferences->indexcollapsed)) { + $indexcollapsed = true; + } + } + $sectionurlinfo = course_get_url($course, $section->section, ['navigation' => false]); + $sectionurl = ''; + if ($sectionurlinfo instanceof moodle_url) { + $sectionurl = $sectionurlinfo->out(false); + } + + $data = [ + 'id' => $section->id, + 'section' => $section->section, + 'number' => $section->section, + 'title' => $format->get_section_name($section), + 'hassummary' => !empty($section->summary), + 'rawtitle' => $section->name, + 'cmlist' => [], + 'visible' => !empty($section->visible), + 'sectionurl' => $sectionurl, + 'current' => $format->is_section_current($section), + 'indexcollapsed' => $indexcollapsed, + 'contentcollapsed' => $contentcollapsed, + 'hasrestrictions' => $this->get_has_restrictions(), + 'bulkeditable' => $this->is_bulk_editable(), + 'component' => $section->component, + 'itemid' => $section->itemid, + ]; + + $data = (object) $data; + + if (empty($modinfo->sections[$section->section])) { + return $data; + } + + foreach ($modinfo->sections[$section->section] as $modnumber) { + $mod = $modinfo->cms[$modnumber]; + if ($section->uservisible && $mod->is_visible_on_course_page()) { + $data->cmlist[] = $mod->id; + } + } + return $data; + } +} diff --git a/classes/output/renderer.php b/classes/output/renderer.php index 6244619..10b3695 100644 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -40,7 +40,6 @@ use format_designer\output\call_to_action; use format_designer\output\cm_completion; -require_once($CFG->dirroot.'/course/format/renderer.php'); require_once($CFG->dirroot.'/course/format/designer/lib.php'); /** @@ -100,13 +99,19 @@ public function render_content($widget) { $startclass[] = 'kanban-board'; $data->kanbanmode = true; } + $data->startid = $startid; - $data->issectionpageclass = ($data->initialsection->sectionreturnid != 0) ? 'section-page-layout' : ''; + $format = course_get_format($course); + $singlesection = $format->get_sectionnum(); + + $data->issectionpageclass = $singlesection || ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) + ? 'section-page-layout' : ''; if (!format_designer_has_pro()) { $data->headermetadata = $this->course_header_metadata_details($course); } + if (format_designer_has_pro()) { $startclass[] = ($course->activitydisplaymode == 'bypurpose') ? 'activity-purpose-mode' : 'activity-default-mode'; } @@ -455,7 +460,6 @@ public function course_header_metadata_details(\stdClass $course, bool $dataonly // Get course staffs to show on course header. $coursestaffs = helper::create()->get_course_staff_users($course); - $data = [ 'course' => $course, 'enrolmentstartdate' => ($enrolmentstartdate) ? $enrolstartdate : '', @@ -515,25 +519,29 @@ public function course_header_metadata_details(\stdClass $course, bool $dataonly */ public function due_overdue_activities_count(): array { global $USER, $DB; - $duecount = $overduecount = 0; - $modinfo = $this->modinfo; - $completion = new \completion_info($this->modinfo->get_course()); - - foreach ($modinfo->sections as $modnumbers) { - foreach ($modnumbers as $modnumber) { - $mod = $modinfo->cms[$modnumber]; - if (!empty($mod) && $DB->record_exists('course_modules', ['id' => $mod->id, 'deletioninprogress' => 0]) - && $mod->uservisible) { - $data = $completion->get_data($mod, true, $USER->id); - if ($data->completionstate != COMPLETION_COMPLETE) { - $cmcompletion = new cm_completion($mod); - $overduecount = ($cmcompletion->is_overdue()) ? $overduecount + 1 : $overduecount; - $duecount = ($cmcompletion->is_due_today()) ? $duecount + 1 : $duecount; + $cache = format_designer_get_cache_object(); + $cachekey = "d_o_a_c_c{$this->modinfo->get_course()->id}_u{$USER->id}"; + if (!$cache->get($cachekey)) { + $duecount = $overduecount = 0; + $modinfo = $this->modinfo; + $completion = new \completion_info($this->modinfo->get_course()); + foreach ($modinfo->sections as $modnumbers) { + foreach ($modnumbers as $modnumber) { + $mod = $modinfo->cms[$modnumber]; + if (!empty($mod) && $DB->record_exists('course_modules', ['id' => $mod->id, 'deletioninprogress' => 0]) + && $mod->uservisible) { + $data = $completion->get_data($mod, true, $USER->id); + if ($data->completionstate != COMPLETION_COMPLETE) { + $cmcompletion = new cm_completion($mod); + $overduecount = ($cmcompletion->is_overdue()) ? $overduecount + 1 : $overduecount; + $duecount = ($cmcompletion->is_due_today()) ? $duecount + 1 : $duecount; + } } } } + $cache->set($cachekey, ['dues' => $duecount, 'overdues' => $overduecount]); } - return ['dues' => $duecount, 'overdues' => $overduecount]; + return $cache->get($cachekey); } /** @@ -542,15 +550,20 @@ public function due_overdue_activities_count(): array { * @return cm_info[] Array from $cmid => $cm of all activities with completion enabled, */ public static function get_completion_activities($course) { - $modinfo = get_fast_modinfo($course); - $result = []; - foreach ($modinfo->get_cms() as $cm) { - if ($cm->completion != COMPLETION_TRACKING_NONE && !$cm->deletioninprogress - && $cm->is_visible_on_course_page()) { - $result[$cm->id] = $cm; + $cache = format_designer_get_cache_object(); + $key = "g_c_a{$course->id}"; + if (!$cache->get($key)) { + $modinfo = get_fast_modinfo($course); + $result = []; + foreach ($modinfo->get_cms() as $cm) { + if ($cm->completion != COMPLETION_TRACKING_NONE && !$cm->deletioninprogress + && $cm->is_visible_on_course_page()) { + $result[$cm->id] = $cm; + } } + $cache->set($key, $result); } - return $result; + return $cache->get($key); } /** @@ -560,18 +573,23 @@ public static function get_completion_activities($course) { * @return int */ public static function get_count_sections_incourse($course) { - $sections = 0; - $modinfo = get_fast_modinfo($course); - $realtiveactivities = isset($course->calsectionprogress) && - ($course->calsectionprogress == DESIGNER_PROGRESS_RELEVANTACTIVITIES) ? true : false; - - foreach ($modinfo->sections as $sectionno => $modnumbers) { - $section = course_get_format($course)->get_section($sectionno); - if (\format_designer\options::is_vaild_section_completed($section, $course, $modinfo, $realtiveactivities)) { - $sections += 1; + $cache = format_designer_get_cache_object(); + $key = "g_c_s_ic{$course->id}"; + if (!$cache->get($key)) { + $sections = 0; + $modinfo = get_fast_modinfo($course); + $realtiveactivities = isset($course->calsectionprogress) && + ($course->calsectionprogress == DESIGNER_PROGRESS_RELEVANTACTIVITIES) ? true : false; + + foreach ($modinfo->sections as $sectionno => $modnumbers) { + $section = course_get_format($course)->get_section($sectionno); + if (\format_designer\options::is_vaild_section_completed($section, $course, $modinfo, $realtiveactivities) == "true") { + $sections += 1; + } } + $cache->set($key, $sections); } - return $sections; + return $cache->get($key); } /** @@ -583,153 +601,166 @@ public static function get_count_sections_incourse($course) { */ public static function criteria_progress($course, $userid) { global $USER; - $completion = new \completion_info($course); - $modinfo = get_fast_modinfo($course); - $context = \context_course::instance($course->id); - // First, let's make sure completion is enabled. - if (!$completion->is_enabled()) { - return null; - } + $cache = format_designer_get_cache_object(); + $cachekey = "c_p_c{$course->id}_u_{$userid}"; + if ($cache->get($cachekey) === false) { - $result = []; - $completedcriteria = []; - $uncompletedcriteria = []; + $completion = new \completion_info($course); + if (!$completion->is_enabled()) { + $cache->set($cachekey, null); + } - // Get the number of modules that support completion. - $modules = self::get_completion_activities($course); - $completionactivities = $completion->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY); - $complteioncourses = $completion->get_criteria(COMPLETION_CRITERIA_TYPE_COURSE); + $modinfo = get_fast_modinfo($course); + $context = \context_course::instance($course->id); + // First, let's make sure completion is enabled. - $count = count($completionactivities); + $result = []; + $completedcriteria = []; + $uncompletedcriteria = []; - $isapplycompletioncourses = false; - if (!isset($course->calcourseprogress)) { - $isapplycompletioncourses = true; - } else if ($course->calcourseprogress == DESIGNER_PROGRESS_CRITERIA) { - $isapplycompletioncourses = true; - } + // Get the number of modules that support completion. + $modules = self::get_completion_activities($course); + $completionactivities = $completion->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY); + $complteioncourses = $completion->get_criteria(COMPLETION_CRITERIA_TYPE_COURSE); - if ($isapplycompletioncourses) { - $count += count($complteioncourses); - } - $cmidentifier = "moduleinstance"; + $count = count($completionactivities); - if (format_designer_has_pro()) { - $format = course_get_format($course); - $course = $format->get_course(); - if ($course->calcourseprogress == DESIGNER_PROGRESS_ALLACTIVITIES) { - $count = count($modules); - $completionactivities = $modules; - $cmidentifier = "id"; - } else if ($course->calcourseprogress == DESIGNER_PROGRESS_SECTIONS) { - $completionactivities = []; - $count = self::get_count_sections_incourse($course); + $isapplycompletioncourses = false; + if (!isset($course->calcourseprogress)) { + $isapplycompletioncourses = true; + } else if ($course->calcourseprogress == DESIGNER_PROGRESS_CRITERIA) { + $isapplycompletioncourses = true; } - } - if (!$count) { - return null; - } - // Get the number of modules that have been completed. - $completed = 0; - if ($completionactivities) { - foreach ($completionactivities as $activity) { - $cmid = $activity->{$cmidentifier}; - if (isset($modules[$cmid])) { - $data = $completion->get_data($modules[$cmid], true, $userid); - $completed += ($data->completionstate == COMPLETION_COMPLETE || - $data->completionstate == COMPLETION_COMPLETE_PASS) ? 1 : 0; - $modtooltiplink = html_writer::link($modules[$cmid]->url, - get_string('stractivity', 'format_designer') . ": " . $modules[$cmid]->name); - if ($data->completionstate == COMPLETION_COMPLETE || - $data->completionstate == COMPLETION_COMPLETE_PASS) { - $completedcriteria[] = $modtooltiplink; - } else { - $uncompletedcriteria[] = $modtooltiplink; - } - } + if ($isapplycompletioncourses) { + $count += count($complteioncourses); } - } + $cmidentifier = "moduleinstance"; - if ($isapplycompletioncourses && $complteioncourses) { - foreach ($complteioncourses as $coursecriteria) { - $courseid = $coursecriteria->courseinstance; - $course = get_course($courseid); - $completion = new \completion_info($course); - $coursetooltiplink = html_writer::link(new moodle_url('/course/view.php', - ['id' => $course->id]), get_string('strcourse', 'format_designer') . ": " . $course->fullname); - if ($completion->is_course_complete($userid)) { - $completed += 1; - $completedcriteria[] = $coursetooltiplink; - } else { - $uncompletedcriteria[] = $coursetooltiplink; + if (format_designer_has_pro()) { + $format = course_get_format($course); + $course = $format->get_course(); + if ($course->calcourseprogress == DESIGNER_PROGRESS_ALLACTIVITIES) { + $count = count($modules); + $completionactivities = $modules; + $cmidentifier = "id"; + } else if ($course->calcourseprogress == DESIGNER_PROGRESS_SECTIONS) { + $completionactivities = []; + $count = self::get_count_sections_incourse($course); } } - } - if (format_designer_has_pro()) { + if (!$count) { + $cache->set($cachekey, null); + } - if (isset($course->calcourseprogress) && $course->calcourseprogress == DESIGNER_PROGRESS_SECTIONS - && !empty($modinfo->sections)) { - foreach ($modinfo->sections as $sectionno => $modnumbers) { - $section = course_get_format($course)->get_section($sectionno); - if ($section->visible) { - $sectionname = get_section_name($course, $section); - $sectionurl = new moodle_url('/course/view.php', ['id' => $course->id, 'section' => $sectionno]); - $sectiontooltiplink = html_writer::link($sectionurl, - get_string('strsection', 'format_designer') . ": ". $sectionname); - $realtiveactivities = isset($course->calsectionprogress) && - ($course->calsectionprogress == DESIGNER_PROGRESS_RELEVANTACTIVITIES) ? true : false; - if (\format_designer\options::is_section_completed($section, $course, $modinfo, - true, $realtiveactivities)) { - $completed += 1; - $completedcriteria[] = $sectiontooltiplink; + // Get the number of modules that have been completed. + $completed = 0; + if ($completionactivities) { + foreach ($completionactivities as $activity) { + $cmid = $activity->{$cmidentifier}; + if (isset($modules[$cmid])) { + $data = $completion->get_data($modules[$cmid], true, $userid); + $completed += ($data->completionstate == COMPLETION_COMPLETE || + $data->completionstate == COMPLETION_COMPLETE_PASS) ? 1 : 0; + $modtooltiplink = html_writer::link($modules[$cmid]->url, + get_string('stractivity', 'format_designer') . ": " . $modules[$cmid]->name); + if ($data->completionstate == COMPLETION_COMPLETE || + $data->completionstate == COMPLETION_COMPLETE_PASS) { + $completedcriteria[] = $modtooltiplink; } else { - if (\format_designer\options::is_vaild_section_completed($section, $course, - $modinfo, $realtiveactivities)) { - $uncompletedcriteria[] = $sectiontooltiplink; + $uncompletedcriteria[] = $modtooltiplink; + } + } + } + } + + if ($isapplycompletioncourses && $complteioncourses) { + foreach ($complteioncourses as $coursecriteria) { + $courseid = $coursecriteria->courseinstance; + $course = get_course($courseid); + $completion = new \completion_info($course); + $coursetooltiplink = html_writer::link(new moodle_url('/course/view.php', + ['id' => $course->id]), get_string('strcourse', 'format_designer') . ": " . $course->fullname); + if ($completion->is_course_complete($userid)) { + $completed += 1; + $completedcriteria[] = $coursetooltiplink; + } else { + $uncompletedcriteria[] = $coursetooltiplink; + } + } + } + + if (format_designer_has_pro()) { + $sectiontooltiplink = ''; + if (isset($course->calcourseprogress) && $course->calcourseprogress == DESIGNER_PROGRESS_SECTIONS + && !empty($modinfo->sections)) { + foreach ($modinfo->sections as $sectionno => $modnumbers) { + $section = course_get_format($course)->get_section($sectionno); + if ($section->visible) { + $sectionname = get_section_name($course, $section); + $sectionurl = new moodle_url('/course/view.php', ['id' => $course->id, 'section' => $sectionno]); + $sectiontooltiplink = html_writer::link($sectionurl, + get_string('strsection', 'format_designer') . ": ". $sectionname); + $realtiveactivities = isset($course->calsectionprogress) && + ($course->calsectionprogress == DESIGNER_PROGRESS_RELEVANTACTIVITIES) ? true : false; + if (\format_designer\options::is_section_completed($section, $course, $modinfo, + true, $realtiveactivities)) { + $completed += 1; + $completedcriteria[] = $sectiontooltiplink; + } else { + if (\format_designer\options::is_vaild_section_completed($section, $course, + $modinfo, $realtiveactivities) == "true") { + $uncompletedcriteria[] = $sectiontooltiplink; + } } } } } + $completedcriteria[] = $sectiontooltiplink; } - } - $percent = ($completed / $count) * 100; - $completioncriteriahtml = ''; - $uncompletioncriteriahtml = ''; + $percent = ($count > 0) ? (($completed / $count) * 100) : 0; + $completioncriteriahtml = ''; + $uncompletioncriteriahtml = ''; + + if (!empty($completedcriteria)) { + $completioncriteriahtml = html_writer::start_div('completion-criteria-toolblock designer-criteria-tooltip'); + $completioncriteriahtml .= html_writer::start_div('head-block'); + $completioncriteriahtml .= get_string('struppercompleted', 'format_designer'); + $completioncriteriahtml .= html_writer::end_div(); + $completioncriteriahtml .= html_writer::start_div('info-block'); + $completioncriteriahtml .= implode("
", $completedcriteria); + $completioncriteriahtml .= html_writer::end_div(); - if (!empty($completedcriteria)) { - $completioncriteriahtml = html_writer::start_div('completion-criteria-toolblock designer-criteria-tooltip'); - $completioncriteriahtml .= html_writer::start_div('head-block'); - $completioncriteriahtml .= get_string('struppercompleted', 'format_designer'); - $completioncriteriahtml .= html_writer::end_div(); - $completioncriteriahtml .= html_writer::start_div('info-block'); - $completioncriteriahtml .= implode("
", $completedcriteria); $completioncriteriahtml .= html_writer::end_div(); + } - $completioncriteriahtml .= html_writer::end_div(); - } + if (!empty($uncompletedcriteria)) { + $uncompletioncriteriahtml = html_writer::start_div('uncompletion-criteria-toolblock designer-criteria-tooltip'); + $uncompletioncriteriahtml .= html_writer::start_div('head-block'); + $uncompletioncriteriahtml .= get_string('strtodo', 'format_designer'); + $uncompletioncriteriahtml .= html_writer::end_div(); + $uncompletioncriteriahtml .= html_writer::start_div('info-block'); + $uncompletioncriteriahtml .= implode("
", $uncompletedcriteria); + $uncompletioncriteriahtml .= html_writer::end_div(); + $uncompletioncriteriahtml .= html_writer::end_div(); + } - if (!empty($uncompletedcriteria)) { - $uncompletioncriteriahtml = html_writer::start_div('uncompletion-criteria-toolblock designer-criteria-tooltip'); - $uncompletioncriteriahtml .= html_writer::start_div('head-block'); - $uncompletioncriteriahtml .= get_string('strtodo', 'format_designer'); - $uncompletioncriteriahtml .= html_writer::end_div(); - $uncompletioncriteriahtml .= html_writer::start_div('info-block'); - $uncompletioncriteriahtml .= implode("
", $uncompletedcriteria); - $uncompletioncriteriahtml .= html_writer::end_div(); - $uncompletioncriteriahtml .= html_writer::end_div(); - } + $cachedata = [ + 'count' => $count, + 'completed' => $completed, + 'percent' => round($percent), + 'remain' => 100 - $percent, + 'completioncriteriahtml' => $completioncriteriahtml, + 'uncompletioncriteriahtml' => $uncompletioncriteriahtml, + ]; - return [ - 'count' => $count, - 'completed' => $completed, - 'percent' => round($percent), - 'remain' => 100 - $percent, - 'completioncriteriahtml' => $completioncriteriahtml, - 'uncompletioncriteriahtml' => $uncompletioncriteriahtml, - ]; + if (!$cache->get($cachekey)) { + $cache->set($cachekey, $cachedata); + } + } + return $cache->get($cachekey); } @@ -875,7 +906,7 @@ public function render_section_data(section_info $section, stdClass $course, $on $sectionrestrict = (!$section->uservisible && $section->availableinfo) ? true : false; if ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE && $sectionheader - && $section->section > 0 && $format->is_section_visible($section, false)) { + && $format->is_section_visible($section, false)) { $gotosection = true; } @@ -931,7 +962,7 @@ public function render_section_data(section_info $section, stdClass $course, $on $sectioncollapsestatus = 'show'; } // Calculate section width for single section format. - $section->widthclass = ($course->coursedisplay && !$this->page->user_is_editing() && !$onsectionpage && $sectionheader) + $sectionwidthclass = ($course->coursedisplay && !$this->page->user_is_editing() && !$onsectionpage && $sectionheader) ? $this->generate_section_widthclass($section) : ''; if ($course->coursedisplay && !$onsectionpage) { @@ -941,6 +972,8 @@ public function render_section_data(section_info $section, stdClass $course, $on $sectionstylerules = ($course->coursetype == DESIGNER_TYPE_KANBAN) ? (isset($course->listwidth) && $section->section != 0 ? sprintf('width: %s;', $course->listwidth) : '') : ''; + + $showprerequisites = ($section->section == 0) || $format->get_sectionid() ? true : false; $templatecontext = [ 'section' => $section, 'sectionvisible' => $format->is_section_visible($section, false), @@ -959,7 +992,7 @@ public function render_section_data(section_info $section, stdClass $course, $on 'sectioncontainerwidth' => $sectioncontainerwidth, 'sectioncontentwidth' => $sectioncontentwidth, 'sectiondesignwhole' => $sectiondesignwhole, - 'showprerequisites' => ($section->section == 0) ? true : false, + 'showprerequisites' => $showprerequisites, 'prerequisitesnewtab' => isset($course->prerequisitesnewtab) ? $course->prerequisitesnewtab : false, 'sectiondesignheader' => $sectiondesignheader, 'sectiondesigntextcolor' => $sectiondesigntextcolor, @@ -1029,7 +1062,7 @@ public function render_section_data(section_info $section, stdClass $course, $on $templatecontext['sectionmodcount'] = array_values($mods); $templatecontext['sectionsingle'] = true; } - if (format_designer_has_pro() && $section->section == 0) { + if (format_designer_has_pro() && $showprerequisites) { require_once($CFG->dirroot. "/local/designer/lib.php"); if ($course->displaycourseprerequisites == DESIGNER_PREREQUISITES_ABOVECOURSE && function_exists('local_designer_import_prerequisites_courses')) { @@ -1053,7 +1086,7 @@ public function render_section_data(section_info $section, stdClass $course, $on } $sectionclass = ' section-type-'.$sectiontype; $sectionclass .= ($sectionrestrict) ? 'restricted' : ''; - $sectionclass .= $section->widthclass; + $sectionclass .= $sectionwidthclass; $sectionclass .= ($templatecontext['sectionstyle']) ?? ' '.$templatecontext['sectionstyle']; $sectionclass .= isset($templatecontext['onlysummary']) && $templatecontext['onlysummary'] ? ' section-summary ' : ''; $sectionclass .= isset($templatecontext['ishidden']) && $templatecontext['ishidden'] ? ' hidden ' : ''; @@ -1114,7 +1147,7 @@ public function render_course_module($mod, $sectionreturn, $displayoptions = [], $modstyle .= sprintf('animation-duration: %ss;', ($duration) ? $duration : '1'); $this->flowdelay = $this->flowdelay + 0.5; } - $modclasses .= isset($course->flowsize) ? $this->get_flow_size($course) : ''; + $modclasses .= isset($course->flowsize) ? $this->get_flow_size($course) : ''; } $ispopupactivities = isset($course->popupactivities) && $course->popupactivities; @@ -1484,11 +1517,11 @@ public function course_section_updated_cm_item( public function get_flow_size($course) { $sizeclass = ''; if ($course->flowsize == 1) { - $sizeclass = 'flow-card-medium'; + $sizeclass = 'flow-card-medium '; } else if ($course->flowsize == 2) { - $sizeclass = 'flow-card-large'; + $sizeclass = 'flow-card-large '; } else { - $sizeclass = 'flow-card-small'; + $sizeclass = 'flow-card-small '; } $flowsizeclass = isset($course->flowsize) ? $sizeclass : ''; return $flowsizeclass; diff --git a/db/caches.php b/db/caches.php new file mode 100644 index 0000000..705d7e9 --- /dev/null +++ b/db/caches.php @@ -0,0 +1,34 @@ +. + +/** + * Code to be executed after the plugin's database scheme has been installed is defined here. + * + * @package report_lmsace_reports + * @copyright Lmsace dev team + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$definitions = array( + 'designeroptions' => array( + 'mode' => cache_store::MODE_APPLICATION, + 'simplekeys' => true, + 'simpledata' => false, + 'overrideclass' => '\format_designer\cache\loader', + ), +); diff --git a/db/events.php b/db/events.php index e587ca1..e1d3afe 100644 --- a/db/events.php +++ b/db/events.php @@ -33,4 +33,48 @@ 'eventname' => 'core\event\course_section_created', 'callback' => '\format_designer\events::course_section_created', ], + [ + 'eventname' => 'core\event\course_module_deleted', + 'callback' => '\format_designer\events::course_module_deleted', + ], + [ + 'eventname' => 'core\event\course_completed', + 'callback' => '\format_designer\events::course_completed', + ], + [ + 'eventname' => 'core\event\course_module_completion_updated', + 'callback' => '\format_designer\events::course_module_completion_updated', + ], + [ + 'eventname' => 'core\event\course_deleted', + 'callback' => '\format_designer\events::course_deleted', + ], + [ + 'eventname' => 'core\event\course_completion_updated', + 'callback' => '\format_designer\events::course_completion_updated', + ], + [ + 'eventname' => 'core\event\course_updated', + 'callback' => '\format_designer\events::course_updated', + ], + [ + 'eventname' => 'core\event\course_module_created', + 'callback' => '\format_designer\events::course_module_created', + ], + [ + 'eventname' => 'core\event\course_module_updated', + 'callback' => '\format_designer\events::course_module_updated', + ], + [ + 'eventname' => 'core\event\course_module_deleted', + 'callback' => '\format_designer\events::course_module_deleted', + ], + [ + 'eventname' => 'core\event\course_section_deleted', + 'callback' => '\format_designer\events::course_section_deleted', + ], + [ + 'eventname' => 'core\event\course_section_updated', + 'callback' => '\format_designer\events::course_section_updated', + ], ]; diff --git a/db/upgrade.php b/db/upgrade.php index ca278ff..9b115ef 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -106,5 +106,17 @@ function xmldb_format_designer_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2023040601, 'format', 'designer'); } + + if ($oldversion < 2024073000) { + $deletesql = <<delete_records_subquery('format_designer_options', 'id', 'optionid', $deletesql); + upgrade_plugin_savepoint(true, 2024073000, 'format', 'designer'); + } + return true; } diff --git a/editsection_form.php b/editsection_form.php index 0481788..788c217 100644 --- a/editsection_form.php +++ b/editsection_form.php @@ -47,16 +47,22 @@ public function definition() { $mform->addElement('header', 'generalhdr', get_string('general')); - $mform->addElement('defaultcustom', 'name', get_string('sectionname'), [ - 'defaultvalue' => $this->_customdata['defaultsectionname'], - 'customvalue' => $sectioninfo->name, - ], ['size' => 30, 'maxlength' => 255]); - $mform->setDefault('name', false); - $mform->addGroupRule('name', ['name' => [[get_string('maximumchars', '', 255), 'maxlength', 255]]]); + $mform->addElement( + 'text', + 'name', + get_string('sectionname'), + [ + 'placeholder' => $this->_customdata['defaultsectionname'], + 'size' => 30, + 'maxlength' => 255, + ], + ); + $mform->setType('name', PARAM_RAW); + $mform->setDefault('name', $sectioninfo->name); + $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); // Prepare course and the editor. $mform->addElement('editor', 'summary_editor', get_string('summary'), null, $this->_customdata['editoroptions']); - $mform->addHelpButton('summary_editor', 'summary'); $mform->setType('summary_editor', PARAM_RAW); $mform->addElement('hidden', 'id'); @@ -68,8 +74,9 @@ public function definition() { if (!empty($formatoptions)) { $elements = $courseformat->create_edit_form_elements($mform, true); } + // Check the moodle 4.3 higher. - if ($CFG->version >= 2023092300 && !empty($CFG->enableavailability)) { + if (!empty($CFG->enableavailability)) { $mform->addElement('header', 'availabilityconditions', get_string('restrictaccess', 'availability')); @@ -104,18 +111,6 @@ public function definition_after_data() { $course = $this->_customdata['course']; if (!empty($CFG->enableavailability)) { - // Check the moodle 4.3 lower. - if ($CFG->version < 2023092300) { - $mform->addElement('header', 'availabilityconditions', - get_string('restrictaccess', 'availability')); - $mform->setExpanded('availabilityconditions', false); - - // Availability field. This is just a textarea; the user interface - // interaction is all implemented in JavaScript. The field is named - // availabilityconditionsjson for consistency with moodleform_mod. - $mform->addElement('textarea', 'availabilityconditionsjson', - get_string('accessrestrictions', 'availability')); - } \core_availability\frontend::include_all_javascript($course, null, $this->_customdata['cs']); } @@ -129,6 +124,7 @@ public function definition_after_data() { * @param stdClass|array $defaultvalues object or array of default values */ public function set_data($defaultvalues) { + global $CFG; if (!is_object($defaultvalues)) { // We need object for file_prepare_standard_editor. $defaultvalues = (object)$defaultvalues; @@ -143,10 +139,6 @@ public function set_data($defaultvalues) { $defaultvalues = \local_designer\options::prepare_sectioncardcta_editor_files($defaultvalues, $this->_customdata['course']); } - - if (strval($defaultvalues->name) === '') { - $defaultvalues->name = false; - } parent::set_data($defaultvalues); } diff --git a/format.php b/format.php index e5b3e2b..5d305ec 100644 --- a/format.php +++ b/format.php @@ -52,7 +52,7 @@ $renderer = $PAGE->get_renderer('format_designer'); if (!empty($displaysection)) { - $format->set_section_number($displaysection); + $format->set_sectionnum($displaysection); } $outputclass = $format->get_output_classname('content'); diff --git a/lang/en/format_designer.php b/lang/en/format_designer.php index cf79d7a..c0421c8 100644 --- a/lang/en/format_designer.php +++ b/lang/en/format_designer.php @@ -273,7 +273,6 @@ $string['courseheader'] = 'Course header'; $string['popupactivitiesnotinstalled'] = 'Popup format must be installed in order to display the activities in popup.'; $string['listwidth'] = 'List width'; - $string['general'] = 'General'; $string['general_settings'] = "General settings"; $string['flowanimationduration'] = 'Flow animation duration'; @@ -539,6 +538,7 @@ $string['purposecontent'] = 'Content'; $string['purposeinterface'] = 'Interface'; $string['purposeother'] = 'Other'; +$string['purposeinteractivecontent'] = "Interactive Content"; $string['purpose_created'] = 'Purpose successfully created'; $string['edit_purpose'] = 'Edit purposes'; @@ -556,6 +556,8 @@ $string['courseindicator'] = "Course status: "; $string['strsectionlayout'] = "Section Layout"; $string['completioncriteria'] = "Completion criteria"; +$string['plugin_description'] = 'Mix and match layouts to create unique and visually appealing course designs.'; $string['strsecondarymenucourse'] = "Course"; + $string['section_layout'] = "Section layout"; $string['section_layout_desc'] = ""; diff --git a/lib.php b/lib.php index 88eb267..67f94bd 100644 --- a/lib.php +++ b/lib.php @@ -447,6 +447,7 @@ public function course_format_options($foreditform = false) { return $this->designer_course_format_options($foreditform); } + /** * Designer course format options list. * @@ -458,6 +459,7 @@ public static function course_format_options_list($foreditform = false) { static $courseformatoptions = false; $teacher = get_archetype_roles('editingteacher'); $teacher = reset($teacher); + $courseformatoptionsedit = []; if ($courseformatoptions === false) { $courseconfig = get_config('moodlecourse'); $courseformatoptions = [ @@ -569,6 +571,12 @@ public static function course_format_options_list($foreditform = false) { 'default' => 0, 'type' => PARAM_INT, ]; + $courseformatoptionsedit[$field->inputname] = [ + 'label' => $field->field->name, + 'element_type' => 'advcheckbox', + 'help' => 'profilefieditem', + 'help_component' => 'format_designer', + ]; } } } @@ -600,7 +608,7 @@ public static function course_format_options_list($foreditform = false) { } if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) { - $courseformatoptionsedit = [ + $courseformatoptionsedit += [ 'hiddensections' => [ 'label' => new lang_string('hiddensections'), 'help' => 'hiddensections', @@ -825,7 +833,7 @@ public static function course_format_options_list($foreditform = false) { 'help_component' => 'format_designer', ]; - if (format_designer_has_pro() != 1 ) { + /* if (format_designer_has_pro() != 1 ) { $userprofilefields = profile_get_user_fields_with_data(0); if (!empty($userprofilefields)) { foreach ($userprofilefields as $field) { @@ -837,7 +845,9 @@ public static function course_format_options_list($foreditform = false) { ]; } } - } + } */ + + $courseformatoptionsedit['courseheroactivityheader'] = [ 'label' => new lang_string('heroactivity', 'format_designer'), @@ -951,6 +961,7 @@ public function create_edit_form_elements(&$mform, $forsection = false) { } array_unshift($elements, $element); } + if ($forsection) { $options = $this->section_format_options(true); } else { @@ -1155,6 +1166,81 @@ public static function section_format_options_list($foreditform) { return $sectionoptions; } + + /** + * Duplicate a section + * + * @param section_info $originalsection The section to be duplicated + * @return section_info The new duplicated section + * @since Moodle 4.2 + */ + public function duplicate_section(section_info $originalsection): section_info { + global $USER, $CFG; + $course = $this->get_course(); + + $fileareasections = [ + 'sectiondesignerbackgroundimage' => [ + 'filearea' => 'sectiondesignbackground', + 'component' => 'format_designer', + ], + 'sectiondesignercompletionbg' => [ + 'filearea' => 'sectiondesigncompletionbackground', + 'component' => 'format_designer', + ], + 'sectioncardcta' => [ + 'filearea' => 'sectioncardcta', + 'component' => 'local_designer', + ], + ]; + $sectioninfo = parent::duplicate_section($originalsection); + $oldsection = get_fast_modinfo($course)->get_section_info($originalsection->section); + $oldsectionoptions = $this->get_section_options($oldsection->id); + $coursecontext = \context_course::instance($course->id); + $fs = get_file_storage(); + if (!empty($oldsectionoptions)) { + foreach ($oldsectionoptions as $option => $value) { + if ($value) { + $this->set_section_option($sectioninfo->id, $option, $value); + } + + if (in_array($option, array_keys($fileareasections))) { + $files = $fs->get_area_files($coursecontext->id, $fileareasections[$option]['component'], + $fileareasections[$option]['filearea'], $oldsection->id, 'itemid, filepath, filename', false); + $file = current($files); + if ($file) { + $userdraft = [ + 'contextid' => $coursecontext->id, + 'component' => $fileareasections[$option]['component'], + 'filearea' => $fileareasections[$option]['filearea'], + 'itemid' => $sectioninfo->id, + 'filepath' => '/', + 'filename' => $file->get_filename(), + ]; + $fs->create_file_from_storedfile($userdraft, $file); + } + } + } + } + + // Prepare the section summary. + $files = $fs->get_area_files( + $coursecontext->id, 'course', 'section', $oldsection->id, 'itemid, filepath, filename', false); + $file = current($files); + if ($file) { + $userdraft = [ + 'contextid' => $coursecontext->id, + 'component' => 'course', + 'filearea' => 'section', + 'itemid' => $sectioninfo->id, + 'filepath' => '/', + 'filename' => $file->get_filename(), + ]; + $fs->create_file_from_storedfile($userdraft, $file); + } + return $sectioninfo; + } + + /** * Updates format options for a section * @@ -1582,7 +1668,6 @@ public function get_cm_secondary_title($cm) { */ public function get_section_options(int $sectionid): array { global $DB; - return $DB->get_records_menu('course_format_options', [ 'courseid' => $this->courseid, 'format' => 'designer', @@ -1704,6 +1789,21 @@ function format_designer_get_pro_layouts() { return $layouts; } +/** + * Get the designer format custom layouts + * @return array + */ +function format_designer_get_all_layouts() { + $layouts = [ + 'default' => get_string('link', 'format_designer'), + 'list' => get_string('list', 'format_designer'), + 'cards' => get_string('cards', 'format_designer') + ]; + $prolayouts = array_keys(core_component::get_plugin_list('layouts')); + $prolayouts = (array) get_strings($prolayouts, 'format_designer'); + return array_merge($layouts, $prolayouts); +} + /** * Get section background image url. * @@ -1971,26 +2071,9 @@ function format_designer_timemanagement_installed() { function format_designer_editsetting_style($page) { if ($page->user_is_editing()) { // Fixed the overlapping issue by make this css rule as important. Moodle CI doesn't allow important. - $style = '.format-designer .course-content ul.designer li.section .right .dropdown .dropdown-menu {'; - $style .= 'top: -50px !important;left: auto !important;right: 40px !important;transform: none !important;'; - $style .= '}'; - $style .= '.format-designer .designer .section .activity .actions .menubar .dropdown .dropdown-menu {'; - $style .= 'top: -50px !important;left: auto !important;right: 40px !important;transform: none !important;'; - $style .= '}'; - $style .= '.format-designer .course-content ul.designer li.section .right .dropdown.designer-menu .dropdown-menu {'; - $style .= 'top: -90px !important;'; - $style .= '}'; - $style .= '.format-designer .designer .section .activity .actions .menubar .dropdown .dropdown-menu .dropdown-subpanel - .dropdown-menu {'; - $style .= 'right: 100% !important;'; - $style .= '}'; - $style .= '.format-designer .course-content ul.designer li.section .right .dropdown .dropdown-menu .dropdown-subpanel - .dropdown-menu {'; - $style .= 'right: 100% !important;'; - $style .= '}'; - $style .= '.format-designer .course-content ul.designer.kanban-board li.section#section-1 .right .dropdown + $style = '.format-designer .course-content ul.designer .kanban-board-activities li.section:first-child .right .dropdown .dropdown-menu .dropdown-subpanel .dropdown-menu {'; - $style .= 'right: 40px !important;'; + $style .= 'left: 100% !important;'; $style .= '}'; echo html_writer::tag('style', $style, []); } @@ -2087,7 +2170,6 @@ function format_designer_course_has_videotime($course) { */ function format_designer_extend_navigation_course($navigation, $course, $context) { global $DB, $PAGE, $COURSE; - if ($course->format != 'designer') { return; } @@ -2154,7 +2236,8 @@ function format_designer_extend_navigation_course($navigation, $course, $context "class" => "nav-item", "role" => "none", "data-forceintomoremenu" => "true", ] ); $secondarymenutocoursecontent .= html_writer::link(new moodle_url('/course/view.php', ['id' => $course->id]), - get_string('strsecondarymenucourse', 'format_designer'), ['role' => 'menuitem', 'class' => 'designercoursehome', "tabindex" => "-1" ]); + get_string('strsecondarymenucourse', 'format_designer'), ['role' => 'menuitem', + 'class' => 'designercoursehome', "tabindex" => "-1" ]); $secondarymenutocoursecontent .= html_writer::end_tag("li"); if (format_designer_has_pro() && $course->prerequisitesbackmain @@ -2167,12 +2250,11 @@ function format_designer_extend_navigation_course($navigation, $course, $context $modbacktomain .= html_writer::end_tag("li"); } } - $sql = "SELECT fd.* FROM - {format_designer_options} fd - JOIN {course_modules} cm ON fd.cmid = cm.id - WHERE courseid = :courseid AND cm.deletioninprogress = 0 AND - (fd.name='heroactivity' OR fd.name='heroactivitypos') ORDER BY fd.cmid DESC"; + {format_designer_options} fd + JOIN {course_modules} cm ON fd.cmid = cm.id + WHERE courseid = :courseid AND cm.deletioninprogress = 0 AND + (fd.name='heroactivity' OR fd.name='heroactivitypos') ORDER BY fd.cmid DESC"; $records = $DB->get_records_sql($sql, ['courseid' => $course->id]); $neg = []; $pos = []; @@ -2183,9 +2265,11 @@ function format_designer_extend_navigation_course($navigation, $course, $context $reports[$record->cmid]['cmid'] = $record->cmid; } } + $neg = []; $pos = []; $reports = format_designer_section_zero_tomake_hero($reports, $course); + if ($reports) { foreach ($reports as $report) { if ($report['heroactivitypos'] < 0) { @@ -2507,18 +2591,7 @@ function format_designer_is_support_subpanel() { return false; } -/** - * Get the list of all layouts. - * - * @return array list. - */ -function format_designer_get_all_layouts() { - $layouts = [ - 'default' => get_string('link', 'format_designer'), - 'list' => get_string('list', 'format_designer'), - 'cards' => get_string('cards', 'format_designer') - ]; - $prolayouts = array_keys(core_component::get_plugin_list('layouts')); - $prolayouts = (array) get_strings($prolayouts, 'format_designer'); - return array_merge($layouts, $prolayouts); + +function format_designer_get_cache_object() { + return cache::make('format_designer', 'designeroptions'); } diff --git a/settings.php b/settings.php index 7318abd..72b11eb 100644 --- a/settings.php +++ b/settings.php @@ -109,20 +109,24 @@ $settingspage->add($settings); $sectionpage = new admin_settingpage('format_designer_section', get_string('sectionsettings', 'format_designer')); + // Section mask images. $name = 'formaty_designer_sectiongeneral'; $heading = get_string('general', 'format_designer'); $information = ''; $setting = new admin_setting_heading($name, $heading, $information); $sectionpage->add($setting); + + // Section layout - Global setting - DES-866. $name = 'format_designer/sectiontype'; $title = get_string('strsectionlayout', 'format_designer'); $description = get_string('section_layout_desc', 'format_designer'); $layouts = []; - $setting = new admin_setting_configselect($name , $title, $description, 'link', format_designer_get_all_layouts()); + $setting = new admin_setting_configselect($name , $title, $description, 'default', format_designer_get_all_layouts()); $sectionpage->add($setting); + $activitypage = new admin_settingpage('format_designer_activity', get_string('stractivity', 'format_designer')); // Activity description length. @@ -143,6 +147,36 @@ get_string('modtrimlength_desc', 'format_designer'), 23, PARAM_INT); $activitypage->add($setting); + + // Activity elements list to manage the visibility - Activity page continue. + $elements = [ + 'icon' => 1, + 'visits' => 4, + 'calltoaction' => 4, + 'title' => 1, + 'description' => 1, + 'modname' => 4, + 'completionbadge' => 1, + ]; + + + $choice = [ + 1 => get_string('show'), + 0 => get_string('hide'), + 2 => get_string('showonhover', 'format_designer'), + 3 => get_string('hideonhover', 'format_designer'), + 4 => get_string('remove'), + ]; + + foreach ($elements as $element => $defaultvalue) { + $name = 'format_designer/activityelements_'.$element; + $title = get_string('activity:'.$element, 'format_designer'); + $desc = ''; + $default = ['value' => $defaultvalue, 'fix' => 0]; + $setting = new admin_setting_configselect_with_advanced($name, $title, $desc, $default, $choice); + $activitypage->add($setting); + } + if (format_designer_has_pro() && file_exists($CFG->dirroot.'/local/designer/setting.php')) { require_once($CFG->dirroot.'/local/designer/setting.php'); diff --git a/styles.css b/styles.css index 752af89..0b0435d 100644 --- a/styles.css +++ b/styles.css @@ -38,6 +38,7 @@ padding-right: 0; } } + .format-designer.path-mod .header-maxwidth, .format-designer.path-admin .header-maxwidth { padding: 0 3rem; @@ -189,7 +190,6 @@ margin-top: 10px; } .format-designer .course-content ul.designer.course-type-flow li.section .availability-section-block + div { - max-height: 66%; overflow: hidden; text-overflow: ellipsis; -webkit-box-orient: vertical; @@ -250,7 +250,8 @@ li.section.main .section-content-wrapper { } /* Section goto link */ -.format-designer .course-content ul.designer:not(.course-type-flow) li.section .section-header-content .goto-section { +.format-designer .course-content ul.designer:not(.course-type-flow) li.section +.section-header-content:not(.flow-stack) .goto-section { float: right; clear: both; } @@ -526,8 +527,9 @@ body.format-designer.editing ul.designer .section .circles-layout .activity .edi border: 0; background-size: cover; background-repeat: no-repeat; +} +.format-designer .course-content ul.designer li.section.main .section-content-wrapper { position: relative; - z-index: 0; } .format-designer .course-content ul.designer li.section.main .section-background-style.section-design-whole { width: 100%; @@ -704,12 +706,12 @@ span.section-collapse-icon:before { vertical-align: middle; } .format-designer.path-course-view.pagelayout-course #page { + height: 100%; background-color: transparent; } .format-designer #page #page-content section#region-main { padding: 15px 10px; border-radius: 5px; - overflow: auto; } .format-designer.path-course-view.pagelayout-course #page #page-content section#region-main { padding: 0; @@ -757,6 +759,7 @@ span.section-collapse-icon:before { margin-left: 0; } } + .format-designer .course-content ul.designer li.section .left, .format-designer .course-content ul.designer li.section .right { width: auto; @@ -765,9 +768,9 @@ span.section-collapse-icon:before { } .format-designer .course-content ul.designer li.section .right { position: relative; - z-index: 1; + z-index: 2; } -.format-designer .course-content ul.designer li.section .right .dropdown { +.format-designer .course-content ul.designer li.section .right .dropdown:not(.dropdown-subpanel) { display: inline-block; margin-right: 10px; } @@ -787,6 +790,7 @@ span.section-collapse-icon:before { display: none; } } + .format-designer .designer .section.img-text .activity .row > div:nth-child(2) .pull-right { text-align: center; float: none; @@ -995,8 +999,6 @@ span.section-collapse-icon:before { padding: 0 0 5px 0; } .format-designer .designer .section.img-text .activity .activityinstance .img-block img { - width: 20px; - height: 20px; margin: 0 auto; } .format-designer .designer .section.img-text .activity .activityinstance .inplaceeditable { @@ -1065,6 +1067,10 @@ span.section-collapse-icon:before { .format-designer .designer .section.img-text.card-layout .activity .activityinstance .inplaceeditable { width: 90%; } +.format-designer.editing .designer .section.img-text.card-layout .activity .activityinstance +.inplaceeditable.inplaceeditingon input { + width: auto; +} /*v-4.0+*/ .format-designer .designer .section.img-text.card-layout .activity .activityinstance .inplaceeditable { @@ -1094,6 +1100,51 @@ span.section-collapse-icon:before { .format-designer .designer .section.card-layout .activity .actions { right: 10px; } +/* Course Edit dropdown menu */ +@media (min-width: 576px) and (max-width: 1690px) { + .format-designer .designer .section.desktop-five-column .section.link-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-five-column .section.card-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-five-column .section.circles-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu { + left: 0; + } +} + +@media (min-width: 576px) and (max-width: 1570px) { + .format-designer .designer .section.desktop-four-column .section.link-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-four-column .section.card-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-four-column .section.circles-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu { + left: 0; + } +} + +@media (min-width: 576px) and (max-width: 1380px) { + .format-designer .designer .section.desktop-three-column .section.link-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-three-column .section.card-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-three-column .section.circles-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu { + left: 0; + } +} + +@media (min-width: 576px) and (max-width: 991px) { + .format-designer .designer .section.desktop-two-column .section.link-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-two-column .section.card-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu, + .format-designer .designer .section.desktop-two-column .section.circles-layout .activity .action-menu .menubar .dropdown + .dropdown-menu .dropdown-subpanel .dropdown-menu { + left: 0; + } +} +/* End of Course Edit dropdown menu */ .format-designer .designer .section.list-layout .activity .aalink img.activityicon, .format-designer .designer .section.card-layout .activity .aalink img.activityicon { width: 40px; @@ -1187,22 +1238,23 @@ span.section-collapse-icon:before { .format-designer.editing .course-content ul.designer li.section .section-header-content .section-head-text .bulkselect { position: absolute; top: 15px; + left: -2rem; } .format-designer .designer .section .content .section.link-layout li.activity .activity-block .bulkselect { top: 10px; left: 10px; - z-index: 0; + z-index: 1; } .format-designer .designer .section .content .section.list-layout li.activity .activity-block .bulkselect { top: 2rem; - z-index: 0; + z-index: 1; } .format-designer .designer .section .content .section.card-layout li.activity .bulkselect { position: absolute; top: 20px; left: 20px; - z-index: 0; + z-index: 1; } .format-designer .designer.kanban-board .section .content .section.card-layout li.activity .bulkselect { left: 20px; @@ -1214,7 +1266,7 @@ span.section-collapse-icon:before { .format-designer .designer .section .content .section.circles-layout li.activity .activity-block .bulkselect { top: 10px; left: 10px; - z-index: 0; + z-index: 1; } .format-designer .designer .section .content .section li.activity .activity-block.designer { height: 100%; @@ -1223,6 +1275,10 @@ span.section-collapse-icon:before { display: flex; position: relative; } +.format-designer.editing .designer .section .content .section li.activity .activity-block.designer:hover { + background: none; + box-shadow: none; +} .format-designer .designer .section .content .section li.activity .activity-block.designer .activity-background-style { width: 100%; height: 100%; @@ -1230,7 +1286,7 @@ span.section-collapse-icon:before { position: absolute; top: 0; left: 0; - z-index: -1; + z-index: 0; } .format-designer .designer .section .content .section li.activity .activity-block.designer .contentwithoutlink { padding: 0; @@ -1282,8 +1338,9 @@ span.section-collapse-icon:before { .format-designer.editing .designer .section .content .section li.activity .mod-indent-outer > .icon { position: absolute; } -.format-designer.editing .activity-item:hover { - background: none; +.format-designer .activity-item, +.format-designer .activity-item.hiddenactivity { + background-color: transparent; } .format-designer .designer .section .content .section li.activity .badge-restricted { background: #ced4da; @@ -2416,7 +2473,7 @@ body.format-designer.editing .designer .section .content .section.card-layout li .format-designer .course-content ul.designer.kanban-board li.section .section-progress-info { margin-top: 10px; } -.format-designer .course-content ul.designer.kanban-board li.section .section-progress-info .progress { +.format-designer .course-content ul.designer.kanban-board .kanban-board-activities li.section .section-progress-info .progress { height: 12px; background: #fff; } @@ -2630,6 +2687,7 @@ body.format-designer.editing .designer .section .content .section.card-layout li } .format-designer .designer .section .content .section.horizontal-circles-layout li.activity .card { border: 0; + background-color: transparent; box-shadow: none; } .format-designer .designer .section .content .section.horizontal-circles-layout li.activity .card .card-body { @@ -2835,6 +2893,21 @@ body.format-designer.editing .designer .section .content .section.card-layout li .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-stack.collapsed { width: 400px; } + +/* Single section flow layout */ +.format-designer .course-content .single-section-layout ul.course-type-flow.designer li.section .right, +.format-designer .course-content .single-section-layout ul.course-type-flow.designer li.section .content { + display: none; +} +.format-designer .course-content .single-section-layout ul.course-type-flow.designer +.section.main + .section.main.section-flow-none .section-header-content, +.format-designer .course-content .single-section-layout ul.course-type-flow.designer +.section.main.section-flow-none + .section.main.section-flow-none .section-header-content { + margin-right: 0; +} +/* End of Single section flow layout */ + + .format-designer .designer.course-type-flow .section .content .section.card-layout li.activity .card.card-list { min-height: 400px; } @@ -3011,6 +3084,8 @@ body.format-designer.editing .designer .section .content .section.card-layout li } .format-designer .course-content ul.designer.course-type-flow { margin-top: 30px; + display: flex; + flex-wrap: wrap; } .format-designer .course-content ul.course-type-flow.designer .section.main .section-content-wrapper { display: contents; @@ -3051,6 +3126,20 @@ body.format-designer.editing .designer .section .content .section.card-layout li left: 10px; } .format-designer .course-content ul.designer.course-type-flow li.section .section-header-content +.section-progress-info + .goto-section { + margin-bottom: 20px; +} +.format-designer.editing .course-content ul.designer li.section .section-header-content.flow-stack .section-progress-info { + max-width: none; + float: none; +} +.format-designer.editing .course-content ul.designer li.section .section-header-content.flow-stack +.section-progress-info .progress-bar { + max-width: 300px; + margin-left: auto; + margin-right: 15px; +} +.format-designer .course-content ul.designer.course-type-flow li.section .section-header-content .section-progress-info > div:not(.progress-donut) .progress { height: 12px; background: #fff; @@ -3242,12 +3331,15 @@ li.section.main.section-background-color:not(.section-header-image) } .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-small { width: 320px; + max-width: 320px; } .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-medium { width: 360px; + max-width: 360px; } .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-large { width: 400px; + max-width: 400px; } .format-designer .designer.course-type-flow .section .content .section.card-layout li.activity.flow-card-small .card.card-list { min-height: 320px; @@ -3264,34 +3356,45 @@ li.section.main.section-background-color:not(.section-header-image) @media (min-width: 768px) and (max-width: 991px) { .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-small.flow-stack, .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-medium.flow-stack, - .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-large.flow-stack { - width: 46%; + .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-large.flow-stack, + .format-designer .course-content .single-section-layout ul.course-type-flow.designer + .section.main + .section.main.section-flow-none .section-header-content, + .format-designer .course-content .single-section-layout ul.course-type-flow.designer + .section.main.section-flow-none + .section.main.section-flow-none .section-header-content { + width: 47%; } .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-small { - width: 46%; + width: 47%; } .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-medium { - width: 46%; + width: 47%; } .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-large { - width: 46%; + width: 47%; } } @media (max-width: 767px) { .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-small.flow-stack, .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-medium.flow-stack, - .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-large.flow-stack { - width: 94%; - } - .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-small { - width: 94%; - } - .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-medium { - width: 94%; - } - .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-large { - width: 94%; + .format-designer .course-content ul.course-type-flow.designer .section.main .section-header-content.flow-card-large.flow-stack, + .format-designer .course-content .single-section-layout ul.course-type-flow.designer + .section.main + .section.main.section-flow-none .section-header-content, + .format-designer .course-content .single-section-layout ul.course-type-flow.designer + .section.main.section-flow-none + .section.main.section-flow-none .section-header-content { + width: 97%; + } + .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-small + .activity-block { + width: 97%; + } + .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-medium + .activity-block { + width: 97%; + } + .format-designer .course-content ul.designer.course-type-flow li.section ul.section.img-text li.activity.flow-card-large + .activity-block { + width: 97%; } } diff --git a/templates/cm_completion.mustache b/templates/cm_completion.mustache index a0c84fa..e34232c 100644 --- a/templates/cm_completion.mustache +++ b/templates/cm_completion.mustache @@ -38,7 +38,7 @@ {{^ispreview}} {{! Show completion info for users/students }} {{#completiontrackingmanual}} -
+ {{#completioncheckbox}} {{{completioncheckbox.inputfield}}} {{/completioncheckbox}} {{#completionincomplete}}