From 3d0a1bc0865da38d7e0f47b95632c786e253ec94 Mon Sep 17 00:00:00 2001 From: Anders Rasmussen Date: Tue, 13 Feb 2024 14:32:03 +0100 Subject: [PATCH 01/43] #102 - Fixed issue with activity copy button --- README.md | 2 ++ block_sharing_cart.php | 29 +++++++++++++++++++++++------ version.php | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 07fc63f..cea8f16 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Change Log * 4.3, release 1 2023.11.01 * Adapted Sharing Cart to new core Moodle 4.3 Backup feature which allows backup without editing the backup. * 4.2, release skipped. +* 4.1, release 4 2024.01.19 + * Fixed issue with activity copy button, where only activities from section 0 would be shown * 4.1, release 3 2023.09.20 * Added activity copy button, if user has capability to back up activities, but not to manage activities * 4.1, release 2 2023.07.05 diff --git a/block_sharing_cart.php b/block_sharing_cart.php index 47ddd0e..cab1a70 100644 --- a/block_sharing_cart.php +++ b/block_sharing_cart.php @@ -70,7 +70,8 @@ public function has_config(): bool { public function get_content() { global $USER, $COURSE, $PAGE; - $section_id = optional_param('section', 0, PARAM_INT); + $section_id = optional_param('sectionid', null, PARAM_INT); + $section_section = optional_param('section', null, PARAM_INT); $context = context_course::instance($this->page->course->id); @@ -167,13 +168,13 @@ public function get_content() { } } - $footer .= $this->insert_copy_section_in_footer($section_id, $sections_dropdown); + $footer .= $this->insert_copy_section_in_footer($section_section, $sections_dropdown); if (!has_capability('moodle/course:manageactivities', $context)) { $activities_dropdown = ''; /** @var cm_info $activity */ foreach ($activities as $activity) { - if ($this->is_activity_not_in_section($section_id, $activity)) { + if (!$this->is_activity_in_section($section_id, $section_section, $activity)) { continue; } @@ -205,15 +206,31 @@ public function get_content() { return $this->content = (object) array('text' => $html, 'footer' => $footer); } - private function is_activity_not_in_section(int $section_id, cm_info $activity): bool { - return $section_id !== $activity->get_section_info()->section; + private function is_activity_in_section(?int $section_id, ?int $section, cm_info $activity): bool { + $activity_section = $activity->get_section_info()->section; + $activity_id = $activity->get_section_info()->id; + + if ($section === null && $section_id === null) { + return true; + } + + return $this->is_activity_section_set($section, $activity_section) + || $this->is_activity_section_id_set($section_id, $activity_id); + } + + private function is_activity_section_set(?int $section, int $activity_section): bool { + return $section === $activity_section; + } + + private function is_activity_section_id_set(?int $section_id, int $activity_section_id): bool { + return $section_id == $activity_section_id; } private function is_activity_deletion_in_progress(cm_info $activity): bool { return $activity->deletioninprogress == 1; } - private function insert_copy_section_in_footer(int $section_id, string $sections_dropdown): string { + private function insert_copy_section_in_footer(?int $section_id, string $sections_dropdown): string { if (!get_config('block_sharing_cart', 'show_copy_section_in_block')) { return ""; } diff --git a/version.php b/version.php index e589c7a..9a43575 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ /** @var object $plugin */ $plugin->component = 'block_sharing_cart'; -$plugin->version = 2024011801; +$plugin->version = 2024011900; $plugin->requires = 2023042400; // Moodle 4.2.0 $plugin->release = '4.4, release 2'; $plugin->maturity = MATURITY_STABLE; From a8b97f79878949028c66c63f98acd542ef0f4228 Mon Sep 17 00:00:00 2001 From: Frederik Milling Pytlick Date: Mon, 11 Mar 2024 15:48:54 +0100 Subject: [PATCH 02/43] #133 - Removed everything and started working on the initial block content --- amd/build/async_copy_item.min.js | 3 - amd/build/async_copy_item.min.js.map | 1 - amd/build/bulkdelete.min.js | 3 - amd/build/bulkdelete.min.js.map | 1 - amd/build/script.min.js | 10 - amd/build/script.min.js.map | 1 - amd/src/bulkdelete.js | 117 -- amd/src/script.js | 1404 ----------------- .../moodle2/restore_fix_missing_questions.php | 171 -- .../restore_root_task_fix_missings.php | 47 - .../helper/restore_fix_missings_helper.php | 67 - block_sharing_cart.php | 312 +--- bulkdelete.php | 175 -- .../multi_checkbox_mod_types.php | 23 + .../admin_settings/multi_checkbox_q_types.php | 32 + .../multi_checkbox_with_icon.php | 68 + classes/app/collection.php | 291 ++++ classes/app/factory.php | 21 + classes/app/items/factory.php | 21 + classes/app/items/repository.php | 21 + classes/app/repository.php | 33 + classes/backup_task_options.php | 85 - classes/controller.php | 1091 ------------- classes/event/backup_activity_created.php | 39 - classes/event/backup_activity_started.php | 44 - classes/event/base.php | 104 -- classes/event/restore_activity_created.php | 33 - classes/event/restore_activity_started.php | 52 - classes/event/section_backedup.php | 23 - classes/event/section_deleted.php | 19 - classes/event/section_restored.php | 27 - classes/event/sharing_cart_item_deleted.php | 54 - classes/exception.php | 67 - .../exceptions/cannot_find_file_exception.php | 77 - .../no_backup_support_exception.php | 21 - .../get_my_sharing_cart_items_status.php | 61 - classes/files/cleaner.php | 93 -- classes/files/file.php | 73 - classes/module.php | 39 - classes/observers.php | 54 - classes/output/block/content.php | 71 + classes/privacy/provider.php | 244 --- classes/record.php | 211 --- classes/renderer.php | 270 ---- classes/repositories/backup_options.php | 90 -- classes/repositories/backup_repository.php | 602 ------- .../repositories/course_module_repository.php | 101 -- classes/repositories/course_repository.php | 97 -- classes/repositories/task_repository.php | 159 -- classes/required_capabilities.php | 95 -- classes/restore_task_options.php | 70 - classes/scoped.php | 51 - classes/section.php | 63 - classes/section_title_form.php | 97 -- classes/storage.php | 131 -- classes/task/async_backup_course_module.php | 95 -- classes/task/async_restore_course_module.php | 122 -- classes/tests/sharing_chart_testcase.php | 249 --- custom.css | 251 --- db/access.php | 38 +- db/events.php | 24 +- db/install.xml | 58 +- db/services.php | 32 +- db/upgrade.php | 278 +--- lang/en/block_sharing_cart.php | 41 +- lang/es/block_sharing_cart.php | 90 -- lang/ja/block_sharing_cart.php | 89 -- lib.php | 34 +- lib/settingslib.php | 173 -- pix/bulkdelete.gif | Bin 204 -> 0 bytes pix/bulkdelete.png | Bin 275 -> 0 bytes rest.php | 156 -- restore.php | 126 -- settings.php | 38 +- style/scss/block/content.scss | 44 + style/scss/style.scss | 1 + style/style.css | 1 + style/style.css.map | 1 + styles.css | 159 -- templates/block/content.mustache | 5 + templates/block/item.mustache | 16 + .../controller/controller_test.php | 437 ----- .../integration/db/upgrade_database_test.php | 272 ---- tests/integration/observers_test.php | 30 +- tests/integration/privacy/privacy_test.php | 489 ------ .../repositories/course_repository_test.php | 72 - tests/integration/storage/storage_test.php | 122 -- version.php | 26 +- 88 files changed, 717 insertions(+), 10082 deletions(-) delete mode 100644 amd/build/async_copy_item.min.js delete mode 100644 amd/build/async_copy_item.min.js.map delete mode 100644 amd/build/bulkdelete.min.js delete mode 100644 amd/build/bulkdelete.min.js.map delete mode 100644 amd/build/script.min.js delete mode 100644 amd/build/script.min.js.map delete mode 100644 amd/src/bulkdelete.js delete mode 100644 amd/src/script.js delete mode 100644 backup/moodle2/restore_fix_missing_questions.php delete mode 100644 backup/moodle2/restore_root_task_fix_missings.php delete mode 100644 backup/util/helper/restore_fix_missings_helper.php delete mode 100644 bulkdelete.php create mode 100644 classes/admin_settings/multi_checkbox_mod_types.php create mode 100644 classes/admin_settings/multi_checkbox_q_types.php create mode 100644 classes/admin_settings/multi_checkbox_with_icon.php create mode 100644 classes/app/collection.php create mode 100644 classes/app/factory.php create mode 100644 classes/app/items/factory.php create mode 100644 classes/app/items/repository.php create mode 100644 classes/app/repository.php delete mode 100644 classes/backup_task_options.php delete mode 100644 classes/controller.php delete mode 100644 classes/event/backup_activity_created.php delete mode 100644 classes/event/backup_activity_started.php delete mode 100644 classes/event/base.php delete mode 100644 classes/event/restore_activity_created.php delete mode 100644 classes/event/restore_activity_started.php delete mode 100644 classes/event/section_backedup.php delete mode 100644 classes/event/section_deleted.php delete mode 100644 classes/event/section_restored.php delete mode 100644 classes/event/sharing_cart_item_deleted.php delete mode 100644 classes/exception.php delete mode 100644 classes/exceptions/cannot_find_file_exception.php delete mode 100644 classes/exceptions/no_backup_support_exception.php delete mode 100644 classes/external/get_my_sharing_cart_items_status.php delete mode 100644 classes/files/cleaner.php delete mode 100644 classes/files/file.php delete mode 100644 classes/module.php delete mode 100644 classes/observers.php create mode 100644 classes/output/block/content.php delete mode 100644 classes/privacy/provider.php delete mode 100644 classes/record.php delete mode 100644 classes/renderer.php delete mode 100644 classes/repositories/backup_options.php delete mode 100644 classes/repositories/backup_repository.php delete mode 100644 classes/repositories/course_module_repository.php delete mode 100644 classes/repositories/course_repository.php delete mode 100644 classes/repositories/task_repository.php delete mode 100644 classes/required_capabilities.php delete mode 100644 classes/restore_task_options.php delete mode 100644 classes/scoped.php delete mode 100644 classes/section.php delete mode 100644 classes/section_title_form.php delete mode 100644 classes/storage.php delete mode 100644 classes/task/async_backup_course_module.php delete mode 100644 classes/task/async_restore_course_module.php delete mode 100644 classes/tests/sharing_chart_testcase.php delete mode 100644 custom.css delete mode 100644 lang/es/block_sharing_cart.php delete mode 100644 lang/ja/block_sharing_cart.php delete mode 100644 lib/settingslib.php delete mode 100644 pix/bulkdelete.gif delete mode 100644 pix/bulkdelete.png delete mode 100644 rest.php delete mode 100644 restore.php create mode 100644 style/scss/block/content.scss create mode 100644 style/scss/style.scss create mode 100644 style/style.css create mode 100644 style/style.css.map delete mode 100644 styles.css create mode 100644 templates/block/content.mustache create mode 100644 templates/block/item.mustache delete mode 100644 tests/integration/controller/controller_test.php delete mode 100644 tests/integration/db/upgrade_database_test.php delete mode 100644 tests/integration/privacy/privacy_test.php delete mode 100644 tests/integration/repositories/course_repository_test.php delete mode 100644 tests/integration/storage/storage_test.php diff --git a/amd/build/async_copy_item.min.js b/amd/build/async_copy_item.min.js deleted file mode 100644 index 09f5a99..0000000 --- a/amd/build/async_copy_item.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("block_sharing_cart/async_copy_item",["exports","core/ajax"],(function(_exports,_ajax){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.init=()=>{}})); - -//# sourceMappingURL=async_copy_item.min.js.map \ No newline at end of file diff --git a/amd/build/async_copy_item.min.js.map b/amd/build/async_copy_item.min.js.map deleted file mode 100644 index b06a84a..0000000 --- a/amd/build/async_copy_item.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"async_copy_item.min.js","sources":["../src/async_copy_item.js"],"sourcesContent":["import Ajax from 'core/ajax';\n\nexport const init = () => {\n \n};\n"],"names":[],"mappings":"qPAEoB"} \ No newline at end of file diff --git a/amd/build/bulkdelete.min.js b/amd/build/bulkdelete.min.js deleted file mode 100644 index c48cde5..0000000 --- a/amd/build/bulkdelete.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("block_sharing_cart/bulkdelete",["jquery","core/modal_factory","core/modal_events"],(function($,ModalFactory,ModalEvents){return{init:function(){function str(identifier){return M.str.block_sharing_cart[identifier]||M.str.moodle[identifier]}function get_checks(){return $('form :checkbox[name^="delete"]')}$(".bulk-delete-item [id^=delete]").on("click",(function(){var delete_checked,checks,checked_checkbox;delete_checked=$('form :button[name^="delete_checked"]'),checks=get_checks(),checked_checkbox=!1,$(checks).each((function(i,val){if($(val).prop("checked"))return checked_checkbox=!0,!1})),delete_checked.prop("disabled",!checked_checkbox),$(".bulk-delete-select-all :checkbox").prop("checked",checked_checkbox)})),$(".bulk-delete-select-all input").on("click",(function(){!function(check){var checks=get_checks();$(checks).prop("checked",check.checked),$('form :button[name ="delete_checked"]').prop("disabled",!check.checked)}(this)})),$(".form_submit").on("click",(function(){var object,trigger,modal_body="",object={title:str("modal_bulkdelete_title"),body:modal_body,save_button:str("modal_bulkdelete_confirm"),next:function(){$("#form").submit()}},trigger=$("#create-modal"),ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL,title:object.title,body:object.body},trigger).done((function(modal){modal.setSaveButtonText(object.save_button),modal.getRoot().on(ModalEvents.save,(function(){object.next()})),modal.getRoot().on(ModalEvents.hidden,(function(){$(".modal.moodle-has-zindex").remove()})),modal.show()}))}))}}})); - -//# sourceMappingURL=bulkdelete.min.js.map \ No newline at end of file diff --git a/amd/build/bulkdelete.min.js.map b/amd/build/bulkdelete.min.js.map deleted file mode 100644 index 7259e2b..0000000 --- a/amd/build/bulkdelete.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bulkdelete.min.js","sources":["../src/bulkdelete.js"],"sourcesContent":["define(['jquery', 'core/modal_factory', 'core/modal_events'], function($, ModalFactory, ModalEvents) {\n\n return {\n init: function() {\n\n /**\n * Returns a localized string\n *\n * @param {String} identifier\n * @return {String}\n */\n function str(identifier) {\n return M.str.block_sharing_cart[identifier] || M.str.moodle[identifier];\n }\n\n /**\n *\n * @param object\n */\n function confirm_modal(object) {\n var trigger = $('#create-modal');\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: object.title,\n body: object.body,\n }, trigger).done(function(modal) {\n modal.setSaveButtonText(object.save_button);\n\n modal.getRoot().on(ModalEvents.save, function() {\n object.next();\n });\n\n // Remove modal from html.\n modal.getRoot().on(ModalEvents.hidden, function() {\n $('.modal.moodle-has-zindex').remove();\n });\n modal.show();\n });\n }\n\n /**\n *\n * @returns {any[]}\n */\n function get_checks() {\n var elements = $('form :checkbox[name^=\"delete\"]');\n return elements;\n }\n\n /**\n *\n * @param check\n */\n function check_all(check) {\n var checks = get_checks();\n $(checks).prop('checked', check.checked);\n\n $('form :button[name =\"delete_checked\"]').prop('disabled', !check.checked);\n }\n\n /**\n *\n */\n function check() {\n var delete_checked = $('form :button[name^=\"delete_checked\"]');\n var checks = get_checks();\n var checked_checkbox = false;\n\n $(checks).each(function(i, val) {\n if ($(val).prop('checked')) {\n checked_checkbox = true;\n return false;\n }\n });\n\n delete_checked.prop('disabled', !checked_checkbox);\n $('.bulk-delete-select-all :checkbox').prop('checked', checked_checkbox);\n }\n\n /**\n * Check activity button\n */\n $('.bulk-delete-item [id^=delete]').on('click', function() {\n check();\n });\n\n /**\n * Select all checkbox.\n */\n $('.bulk-delete-select-all input').on('click', function() {\n check_all(this);\n });\n\n /**\n * Delete selected, opens modal for confirmation.\n */\n $('.form_submit').on('click', function() {\n var modal_body = '';\n\n confirm_modal({\n 'title': str('modal_bulkdelete_title'),\n 'body': modal_body,\n 'save_button': str('modal_bulkdelete_confirm'),\n 'next': function() {\n $('#form').submit();\n }\n });\n });\n }\n };\n});\n"],"names":["define","$","ModalFactory","ModalEvents","init","str","identifier","M","block_sharing_cart","moodle","get_checks","on","delete_checked","checks","checked_checkbox","each","i","val","prop","check","checked","check_all","this","object","trigger","modal_body","selected_input","label","id","text","submit","create","type","types","SAVE_CANCEL","title","body","done","modal","setSaveButtonText","save_button","getRoot","save","next","hidden","remove","show"],"mappings":"AAAAA,uCAAO,CAAC,SAAU,qBAAsB,sBAAsB,SAASC,EAAGC,aAAcC,mBAE7E,CACHC,KAAM,oBAQOC,IAAIC,mBACFC,EAAEF,IAAIG,mBAAmBF,aAAeC,EAAEF,IAAII,OAAOH,qBAgCvDI,oBACUT,EAAE,kCAqCrBA,EAAE,kCAAkCU,GAAG,SAAS,eAlBxCC,eACAC,OACAC,iBAFAF,eAAiBX,EAAE,wCACnBY,OAASH,aACTI,kBAAmB,EAEvBb,EAAEY,QAAQE,MAAK,SAASC,EAAGC,QACnBhB,EAAEgB,KAAKC,KAAK,kBACZJ,kBAAmB,GACZ,KAIfF,eAAeM,KAAK,YAAaJ,kBACjCb,EAAE,qCAAqCiB,KAAK,UAAWJ,qBAa3Db,EAAE,iCAAiCU,GAAG,SAAS,qBApC5BQ,WACXN,OAASH,aACbT,EAAEY,QAAQK,KAAK,UAAWC,MAAMC,SAEhCnB,EAAE,wCAAwCiB,KAAK,YAAaC,MAAMC,SAiClEC,CAAUC,SAMdrB,EAAE,gBAAgBU,GAAG,SAAS,eA7EPY,OACfC,QA6EAC,WAAa,OACbC,eAAiBzB,EAAE,mCACvBA,EAAEyB,gBAAgBX,MAAK,eACfY,MAAQ1B,EAAE,cAAgBqB,KAAKM,GAAK,MACxCH,YAAc,OAASE,MAAME,OAAS,WAE1CJ,YAAc,QApFKF,OAsFL,OACDlB,IAAI,+BACLoB,uBACOpB,IAAI,iCACX,WACJJ,EAAE,SAAS6B,WA1FfN,QAAUvB,EAAE,iBAChBC,aAAa6B,OAAO,CAChBC,KAAM9B,aAAa+B,MAAMC,YACzBC,MAAOZ,OAAOY,MACdC,KAAMb,OAAOa,MACdZ,SAASa,MAAK,SAASC,OACtBA,MAAMC,kBAAkBhB,OAAOiB,aAE/BF,MAAMG,UAAU9B,GAAGR,YAAYuC,MAAM,WACjCnB,OAAOoB,UAIXL,MAAMG,UAAU9B,GAAGR,YAAYyC,QAAQ,WACnC3C,EAAE,4BAA4B4C,YAElCP,MAAMQ"} \ No newline at end of file diff --git a/amd/build/script.min.js b/amd/build/script.min.js deleted file mode 100644 index 02b72d2..0000000 --- a/amd/build/script.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("block_sharing_cart/script",["exports","jquery","core/modal_factory","core/modal_events"],(function(_exports,_jquery,_modal_factory,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events);_exports.init=function(addMethod){(0,_jquery.default)(document).ready((function(){let isDragging=!1;function str(identifier){return M.str.block_sharing_cart[identifier]||M.str.moodle[identifier]}function get_action_url(name,args){let url=M.cfg.wwwroot+"/blocks/sharing_cart/"+name+".php";if(args){const q=[];for(let k in args)q.push(k+"="+encodeURIComponent(args[k]));url+="?"+q.join("&")}return url}function remove_basket(){if("drag_and_drop"===addMethod&&!isDragging){const footerIconContainer=document.getElementById("page-footer").querySelector('div[data-region="footer-container-popover"]'),sharingCartBasket=document.querySelector("button.sharing_cart_basket");sharingCartBasket&&(null==footerIconContainer||footerIconContainer.removeChild(sharingCartBasket),sharingCartBasket.classList.remove("shake_basket"))}}function confirm_modal(obj){obj.checkbox&&(obj.body+='"),_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,title:obj.title,body:obj.body}).done((function(modal){let is_submitted=!1;modal.setSaveButtonText(obj.save_button);const userdata_checkbox=(0,_jquery.default)(modal.getRoot()).find("#modal-userdata-checkbox"),anonymize_checkbox=(0,_jquery.default)(modal.getRoot()).find("#modal-anonymize-checkbox");userdata_checkbox.on("change",(e=>{e.currentTarget.checked?anonymize_checkbox.attr("disabled",null):anonymize_checkbox.prop("checked",!1).attr("disabled",!0)})),modal.getRoot().on(_modal_events.default.save,(function(e){const response={userdata:userdata_checkbox.is(":checked"),anonymize:anonymize_checkbox.is(":checked")};obj.next(response),is_submitted=!0})),modal.getRoot().on(_modal_events.default.cancel,(function(){remove_basket()})),modal.getRoot().on(_modal_events.default.hidden,(function(){(0,_jquery.default)("body").removeClass("modal-open"),is_submitted||remove_basket()})),modal.show()}))}function in_place_edit_section_name($section){let sectionName="";const $inPlaceEditable=$section.find("h3.sectionname .inplaceeditable");return $inPlaceEditable.length&&(sectionName=$inPlaceEditable.data("value")),sectionName}function on_backup_modal(post_data,title_str,body_str,isSection){_jquery.default.post(get_action_url("rest"),post_data,(function(response){!function(response){let checkbox=!1;"1"===response&&(checkbox=!0),confirm_modal({title:title_str,body:body_str,save_button:str("modal_confirm_backup"),checkbox:checkbox,next:function(data){!0===isSection?function(sectionId,sectionNumber,courseId,userdata,anonymize){const $commands=(0,_jquery.default)("span.inplaceeditable[data-itemtype=sectionname][data-itemid="+sectionId+"]"),$section=$commands.closest("li.section.main");let sectionName=$section.attr("aria-label")||$section.find(".sectionname").text().trim();null===sectionName&&(sectionName=String((0,_jquery.default)("#region-main .section_action_menu[data-sectionid='"+sectionId+"']").parent().parent().find("h3.sectionname").text()));const inPlaceEditSectionName=in_place_edit_section_name($section);sectionName=""!==inPlaceEditSectionName?inPlaceEditSectionName:sectionName;const $spinner=add_spinner(),$node_spinner=add_node_spinner($commands);_jquery.default.post(get_action_url("rest"),{action:"backup_section",sectionid:sectionId,sectionnumber:sectionNumber,courseid:courseId,sectionname:sectionName,userdata:userdata,anonymize:anonymize,sesskey:M.cfg.sesskey},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide(),$node_spinner.hide(),remove_basket()}))}(post_data.sectionid,post_data.sectionnumber,post_data.courseid,data.userdata,data.anonymize):function(cmid,userdata,anonymize){let $commands=(0,_jquery.default)("#module-"+cmid+" .actions");$commands.length||($commands=(0,_jquery.default)('[data-owner="#module-'+cmid+'"]'));const $spinner=add_spinner(),$node_spinner=add_node_spinner($commands);_jquery.default.post(get_action_url("rest"),{action:"backup",cmid:cmid,userdata:userdata,anonymize:anonymize,sesskey:M.cfg.sesskey,courseid:course.id},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$node_spinner.hide(),$spinner.hide(),remove_basket()}))}(post_data.cmid,data.userdata,data.anonymize),function(){if("drag_and_drop"===addMethod){const sharingCartBasket=document.querySelector("button.sharing_cart_basket");null==sharingCartBasket||sharingCartBasket.classList.add("shake_basket")}}()}})}(response)}),"text").fail((function(response){show_error(response)}))}const icon={backup:{css:"editing_backup",iconClass:"fa fa-frown-o"},movedir:{css:"editing_right",iconClass:"fa fa-arrow-right"},move:{css:"editing_move_",iconClass:"fa fa-arrows-v"},edit:{css:"editing_update",iconClass:"fa fa-pencil"},cancel:{css:"editing_cancel",iconClass:"fa fa-ban"},delete:{css:"editing_update",iconClass:"fa fa-trash"},restore:{css:"editing_restore",iconClass:"fa fa-clone"},"dir-open":{iconClass:"fa fa-folder-open-o"},"dir-closed":{iconClass:"fa fa-folder-o"}},$block=(0,_jquery.default)(".block_sharing_cart"),course=new function(){const body=(0,_jquery.default)("body");this.id=body.attr("class").match(/course-(\d+)/)[1],this.is_frontpage=body.hasClass("pagelayout-frontpage")};function show_error(response){try{const ex=JSON.parse(response.responseText);new M.core.exception({name:str("pluginname")+" - "+str("error"),message:ex.message})}catch(e){new M.core.exception({name:str("pluginname")+" - "+str("error"),message:response.responseText})}}function create_command(name){const iconElement=(0,_jquery.default)("").attr("alt",str(name)).attr("class",icon[name].iconClass);return(0,_jquery.default)('').addClass(icon[name].css).attr("title",str(name)).append(iconElement)}function add_spinner(){const $spinner=(0,_jquery.default)('
');return(0,_jquery.default)("section.block_sharing_cart").append($spinner),$spinner}function add_node_spinner($node){const $node_spinner=(0,_jquery.default)('');return $node.append($node_spinner),$node_spinner}function reload_tree(){_jquery.default.post(get_action_url("rest"),{action:"render_tree",courseid:course.id},(function(response){$block.find(".tree").replaceWith((0,_jquery.default)(response)),_jquery.default.init_item_tree()}),"html").fail((function(response){show_error(response)}))}(0,_jquery.default)(document).on("click","a.restore",(function(){add_spinner()}));const directories=new function(){const KEY="block_sharing_cart-dirs";let opens=function(param){const readCookie=document.cookie.match("(^|;)\\s*"+param+"\\s*=\\s*([^;]+)");return readCookie?readCookie.pop():""}(KEY).split(",").map((function(v){return parseInt(v)}));function save(){const expires=new Date;expires.setDate(expires.getDate()+30),function(name,value,expireTimeInMillisecond){const d=new Date;d.setTime(d.getTime()+expireTimeInMillisecond);const expires="expires="+d.toUTCString();document.cookie=name+"="+value+";"+expires}(KEY,opens.join(","),expires)}function open($dir,visible){const iconElement=icon[visible?"dir-open":"dir-closed"].iconClass;$dir.find("> div i.icon").attr("class","icon "+iconElement),$dir.find("> ul.list")[visible?"show":"hide"]()}this.init=function(){let i=0;$block.find("li.directory").each((function(index,dir){const $dir=(0,_jquery.default)(dir);$dir.attr("id","block_sharing_cart-dir-"+i),i>=opens.length?opens.push(0):opens[i]&&open($dir,!0),$dir.find("> div div.toggle-wrapper").css("cursor","pointer").on("click",(function(e){!function(e){const $dir=(0,_jquery.default)(e.target).closest("li.directory"),i=$dir.attr("id").match(/(\d+)$/)[1],v="none"===$dir.find("> ul.list").css("display");open($dir,v),opens[i]=v?1:0,save()}(e)})),i++}))},this.reset=function(){opens=[],this.init(),save()}},move_targets=new function(){let $cancel=null,targets=[];this.hide=function(){if(null!==$cancel){const $commands=$cancel.closest(".commands");$cancel.remove(),$cancel=null,$commands.closest("li.activity").css("opacity",1),$commands.find("a").each((function(){(0,_jquery.default)(this).show()})),_jquery.default.each(targets,(function(index,$target){$target.remove()})),targets=[]}},this.show=function(item_id){this.hide();const $current=$block.find("#block_sharing_cart-item-"+item_id),$next=$current.next(),$list=$current.closest("ul");let next_id=0;function create_target(item_id,area_to){const $anchor=(0,_jquery.default)('
').addClass("move-"+item_id+"-to-"+area_to).attr("title",str("movehere")).append((0,_jquery.default)("

"+str("clicktomove")+"

").attr("alt",str("movehere"))),$target=(0,_jquery.default)('
  • ').append($anchor);return $anchor.on("click",(function(e){!function(e){const m=(0,_jquery.default)(e.target).closest("a").attr("class").match(/move-(\d+)-to-(\d+)/),item_id=m[1],area_to=m[2],$spinner=add_spinner();_jquery.default.post(get_action_url("rest"),{action:"move",item_id:item_id,area_to:area_to,sesskey:M.cfg.sesskey},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()}))}(e)})),$target}if($next.length&&(next_id=$next.attr("id").match(/item-(\d+)$/)[1]),$list.find("> li.activity").each((function(index,item){const $item=(0,_jquery.default)(item),to=$item.attr("id").match(/item-(\d+)$/)[1];if(to===item_id){$cancel=create_command("cancel"),$cancel.on("click",(function(){move_targets.hide()}));const $commands=$item.find(".commands");$commands.find("a").each((function(){(0,_jquery.default)(this).hide()})),$commands.append($cancel),$item.css("opacity",.5)}else if(to!==next_id){const $target=create_target(item_id,to);$item.before($target),targets.push($target)}}),this),$next){var $target=create_target(item_id,0);$list.append($target),targets.push($target)}}},restore_targets=new function(){this.is_directory=null;let $clipboard=null,targets=[];function create_target(id,section){const href=get_action_url("restore",{directory:!0===restore_targets.is_directory,target:id,course:course.id,section:section,in_section:(0,_jquery.default)("#copy-section-form").data("in-section"),sesskey:M.cfg.sesskey,returnurl:document.URL});let $target=(0,_jquery.default)("").attr("href",href).attr("title",str("copyhere")).append((0,_jquery.default)('').attr("alt",str("copyhere")).attr("src",M.util.image_url("dropzone_arrow","block_sharing_cart")));return targets.push($target),$target}this.hide=function(){null!==$clipboard&&($clipboard.remove(),$clipboard=null,_jquery.default.each(targets,(function(index,$target){$target.remove()})),targets=[])},this.show=function(id){this.hide();let $view=(0,_jquery.default)("");if(this.is_directory)$view.html(id).css("display","inline"),$view.prepend((0,_jquery.default)("").addClass("icon").attr("alt",id));else{const $item=$block.find("#block_sharing_cart-item-"+id);$view=(0,_jquery.default)($item.find("div")[0].cloneNode(!0)).css("display","inline"),$view.attr("class",$view.attr("class").replace(/mod-indent-\d+/,"")),$view.find(".commands").remove()}const $cancel=create_command("cancel");if($cancel.on("click",this.hide),$clipboard=(0,_jquery.default)('
    '),$clipboard.append(str("clipboard")+": ").append($view).append($cancel),course.is_frontpage){const $sitetopic=(0,_jquery.default)(".sitetopic"),$mainmenu=(0,_jquery.default)(".block_site_main_menu");$sitetopic?$sitetopic.find("*").before($clipboard):$mainmenu&&$mainmenu.find(".content").before($clipboard),$mainmenu&&$mainmenu.find(".footer").before(create_target(id,0)),$sitetopic&&$sitetopic.find("ul.section").append(create_target(id,1))}else{const $container=(0,_jquery.default)(".course-content");$container.prepend($clipboard),$container.find(M.course.format.get_section_wrapper(null)).each((function(index,sectionDOM){const $section=(0,_jquery.default)(sectionDOM),section=$section.attr("id").match(/(\d+)$/)[1];$section.find("ul.section").first().append(create_target(id,section))}),this)}}};function init_footer_basket(){let currentDragging;const activities=document.querySelectorAll(".activity.activity-wrapper"),sections=document.querySelectorAll(".course-section-header"),sharingCartBlock=document.querySelector('section[data-block="sharing_cart"]');!function(){var _document$getElements;const courseSectionHeader=null!==(_document$getElements=document.getElementsByClassName("course-section-header")[0])&&void 0!==_document$getElements?_document$getElements:null;courseSectionHeader instanceof HTMLElement&&(courseSectionHeader.classList.add("draggable"),courseSectionHeader.setAttribute("draggable",!0))}();const footerIconContainer=document.getElementById("page-footer").querySelector('div[data-region="footer-container-popover"]'),basket=document.createElement("i");basket.setAttribute("class","fa fa-shopping-basket");const basketButton=document.createElement("button");basketButton.setAttribute("class","btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket"),basketButton.setAttribute("style","z-index: 1001;"),basketButton.append(basket);const dropAreaText=document.createElement("p");dropAreaText.setAttribute("class","font-weight-bold text-white"),dropAreaText.innerText=str("drop_here");const dropArea=document.createElement("div");function drag_event_listeners(draggable){draggable.addEventListener("dragstart",(e=>{basketButton.classList.remove("shake_basket"),null==footerIconContainer||footerIconContainer.prepend(basketButton),sharingCartBlock.children[0].classList.add("dragging_item"),sharingCartBlock.append(dropArea),currentDragging=e.target,isDragging=!0})),draggable.addEventListener("dragend",(()=>{currentDragging instanceof HTMLElement&&(null==footerIconContainer||footerIconContainer.removeChild(basketButton)),sharingCartBlock.children[0].classList.remove("dragging_item"),sharingCartBlock.removeChild(dropArea),isDragging=!1}))}dropArea.setAttribute("class","h-100 w-100 position-absolute d-flex justify-content-center align-items-center"),dropArea.append(dropAreaText),sections.forEach((section=>{drag_event_listeners(section)})),activities.forEach((activity=>{drag_event_listeners(activity)})),[basketButton,sharingCartBlock].forEach((dropzone=>{dropzone.addEventListener("dragover",(e=>{e.preventDefault(),dropzone.classList.add("drag_over")})),dropzone.addEventListener("dragenter",(e=>{e.preventDefault(),dropzone.classList.add("drag_over")})),dropzone.addEventListener("dragleave",(()=>{dropzone.classList.remove("drag_over")})),dropzone.addEventListener("drop",(()=>{currentDragging instanceof HTMLElement&¤tDragging.querySelector(".add-to-sharing-cart").click(),dropzone.classList.remove("drag_over"),currentDragging=void 0,isDragging=!1}))}))}_jquery.default.get_plugin_name=function(){let $blockheader=$block.find("h2");return $blockheader.length?$blockheader.html():($blockheader=$block.find("h3"),$blockheader.length?$blockheader.html():"")},_jquery.default.on_backup=function(e,activityName){let cmId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;0===cmId&&(cmId=function($backup){const $activity=$backup.closest("li.activity");if($activity.length)return $activity.attr("id").match(/(\d+)$/)[1];const $commands=$backup.closest(".commands"),dataowner=$commands.attr("data-owner");return dataowner.length?dataowner.match(/(\d+)$/)[1]:$commands.find("a.editing_delete").attr("href").match(/delete=(\d+)/)[1]}((0,_jquery.default)(e.target)));const data={action:"is_userdata_copyable",cmid:cmId};on_backup_modal(data,activityName,str("confirm_backup"),!1)},_jquery.default.on_movedir=function(e){const $commands=(0,_jquery.default)(e.target).closest(".commands"),$current_dir=$commands.closest("li.directory"),current_path=$current_dir.length?$current_dir.attr("directory-path"):"/",item_id=(0,_jquery.default)(e.target).closest("li.activity").attr("id").match(/(\d+)$/)[1],dirs=[];$block.find("li.directory").each((function(){dirs.push((0,_jquery.default)(this).attr("directory-path"))}));const $form=(0,_jquery.default)("
    ");function submit(){const folder_to=$form.find('[name="to"]').val(),$spinner=add_spinner();_jquery.default.post(get_action_url("rest"),{action:"movedir",item_id:item_id,folder_to:folder_to,sesskey:M.cfg.sesskey},(function(){reload_tree(),directories.reset()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()}))}if($form.attr("action","javascript:void(0)"),$form.submit(submit),0===dirs.length){var $input=(0,_jquery.default)('').val(current_path);setTimeout((function(){$input.focus()}),1),$form.append($input)}else{dirs.unshift("/");const $select=(0,_jquery.default)('').val(current_path);$select.remove(),$edit.replaceWith($input),$input.focus()})),$form.append($edit)}const $cancel=create_command("cancel");$cancel.on("click",(function(){$form.remove(),$commands.find("a").show()})),$form.append($cancel),$commands.find("a").each((function(){(0,_jquery.default)(this).hide()})),$commands.append($form)},_jquery.default.on_move=function(e){const id=(0,_jquery.default)(e.target).closest("li.activity").attr("id").match(/(\d+)$/)[1];move_targets.show(id)},_jquery.default.on_delete=function(e){const $item=(0,_jquery.default)(e.target).closest("li"),liText=$item[0].innerText;let modalBody,item,isDirectory=!1,description_text="";$item.hasClass("directory")?(isDirectory=!0,item=str("folder_string"),description_text=str("delete_folder")):item=str("activity_string"),modalBody='

    '+item+" "+liText+description_text+"

    ",confirm_modal({title:str("confirm_delete"),body:modalBody,save_button:str("modal_confirm_delete"),checkbox:!1,next:function(){let data={};!0===isDirectory?data={action:"delete_directory",path:$item.attr("directory-path"),sesskey:M.cfg.sesskey}:$item.hasClass("activity")&&(data={action:"delete",id:$item.attr("id").match(/(\d+)$/)[1],sesskey:M.cfg.sesskey});const $spinner=add_spinner();_jquery.default.post(get_action_url("rest"),data,(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()})),e.stopPropagation()}})},_jquery.default.on_restore=function(e){const $item=(0,_jquery.default)(e.target).closest("li");let id=null;$item.hasClass("directory")?(id=$item.attr("directory-path"),restore_targets.is_directory=!0):$item.hasClass("activity")&&(id=$item.attr("id").match(/(\d+)$/)[1],restore_targets.is_directory=!1),restore_targets.show(id)},_jquery.default.on_section_backup=function(sectionId,sectionNumber,courseId,sectionName){on_backup_modal({action:"is_userdata_copyable_section",sectionid:sectionId,sectionnumber:sectionNumber,courseid:courseId},sectionName,'

    '+str("backup_heavy_load_warning_message")+"

    "+str("confirm_backup_section"),!0)},_jquery.default.init_bulk_delete=function(isspeciallayout){const bulkdelete=$block.find(".editing_bulkdelete");bulkdelete.length&&(isspeciallayout?(bulkdelete.attr("role","menuitem").addClass("dropdown-item menu-action"),bulkdelete.append((0,_jquery.default)("").append(bulkdelete.attr("title"))),$block.find(".menubar .dropdown .dropdown-menu").append(bulkdelete)):$block.find(".header .commands").append(bulkdelete))},_jquery.default.init_help_icon=function(isspeciallayout){const helpicon=$block.find(".header-commands > .help-icon");isspeciallayout?$block.find(".header-commands").parent().css("display","block"):$block.find(".header .commands").append(helpicon)},_jquery.default.init_block_header=function(){const isspeciallayout=$block.find(".menubar .dropdown .dropdown-menu").length;_jquery.default.init_bulk_delete(isspeciallayout),_jquery.default.init_help_icon(isspeciallayout)},_jquery.default.init_item_tree=function(){function add_actions(item,actions){const $item=(0,_jquery.default)(item),isCopying="1"===$item.attr("data-is-copying"),$commands=$item.find(".commands").first();_jquery.default.each(actions,(function(index,action){if("restore"===action&&isCopying)return;const $command=create_command(action);$command.on("click",(function(e){_jquery.default["on_"+action](e)})),$commands.append($command)}),this)}const activity_actions=["movedir","move","delete"];course&&activity_actions.push("restore");const directory_actions=["delete","restore"];$block.find("li.activity").each((function(index,item){1!=(0,_jquery.default)(item).attr("data-disable-copy")?add_actions(item,activity_actions):add_actions(item,["movedir","move","delete"])})),$block.find("li.directory").each((function(index,item){add_actions(item,directory_actions)})),directories.init()},_jquery.default.init_activity_commands=function(){function create_backup_icon(){const $backupIcon=(0,_jquery.default)('
    ').append((0,_jquery.default)('')).attr("title",str("backup"));return"click_to_add"!==addMethod&&$backupIcon.addClass("d-none"),$backupIcon}function add_activity_backup_control($activity){const activityClass=$activity[0].className,modtype=activityClass.substr(activityClass.indexOf("modtype_")+8);let activityName=str("activity_string");"label"!==modtype&&(activityName=(0,_jquery.default)(".activity#"+$activity[0].id).find(".mod-indent-outer .activityinstance span.instancename").html());const $backupIcon=create_backup_icon();$backupIcon.on("click",(function(e){_jquery.default.on_backup(e,activityName)}));const $actionMenuItem=$activity.find(".action-menu.section-cm-edit-actions").parent(".actions");$actionMenuItem.find(".add-to-sharing-cart").length||$actionMenuItem.append($backupIcon)}(0,_jquery.default)(document).ajaxComplete((function(event,xhr,settings){const url=settings.url,lastslashindex=url.lastIndexOf("="),result=url.substring(lastslashindex+1);if("core_course_edit_module"===result||"core_course_get_module"===result){const data=JSON.parse(settings.data),action=data[0].args.action;if("delete"===action)return;setTimeout((function(){const activity_id=data[0].args.id,activity=(0,_jquery.default)("#module-"+activity_id);if(add_activity_backup_control(activity),"duplicate"===action){add_activity_backup_control(activity.next())}}),1)}})),(0,_jquery.default)("body.editing .course-content li.section").each((function(){!function($section){let sectionId=$section.find(".section_action_menu").data("sectionid");const sectionNumber=parseInt(String($section.attr("id")).match(/\d+/)[0]);let sectionName=$section.attr("aria-label")||$section.find(".sectionname").text().trim();const isFlexibleCourseFormat=(0,_jquery.default)("body[id$=flexsections]").length;isFlexibleCourseFormat&&null==sectionId&&(sectionId=$section.data("section-id"));const courseId=parseInt(String((0,_jquery.default)("body").attr("class")).match(/course-([0-9]*)( |$)/)[1]),$backupIcon=create_backup_icon();$backupIcon.on("click",(function(){const inPlaceEditSectionName=in_place_edit_section_name($section);sectionName=""!==inPlaceEditSectionName?inPlaceEditSectionName:sectionName,_jquery.default.on_section_backup(sectionId,sectionNumber,courseId,sectionName)}));let $sectionTitle=$section.find("h3.sectionname").first().find("a").last();const $inPlaceEditable=$section.find("h3.sectionname .inplaceeditable").first();$inPlaceEditable.length&&($sectionTitle=$inPlaceEditable),isFlexibleCourseFormat&&0===sectionNumber?($sectionTitle=$section.find("> .controls"),$sectionTitle.prepend($backupIcon)):$backupIcon.insertAfter($sectionTitle);const $activities=$section.find("li.activity");(0,_jquery.default)($activities).each((function(){add_activity_backup_control((0,_jquery.default)(this))}))}((0,_jquery.default)(this))}))},_jquery.default.init=function(){M.str.block_sharing_cart.pluginname=this.get_plugin_name(),_jquery.default.init_block_header(),_jquery.default.init_item_tree(),_jquery.default.init_activity_commands(),"drag_and_drop"===addMethod&&init_footer_basket()};var $spinner=(0,_jquery.default)("").addClass("spinner fa fa-3x fa-circle-o-notch fa-spin");(0,_jquery.default)("div#sharing-cart-spinner-modal div.spinner-container").prepend($spinner),_jquery.default.init()})),(0,_jquery.default)(".copy_section").on("click",(function(){const $section_selected=(0,_jquery.default)(".section-dropdown option:selected"),sectionId=$section_selected.data("section-id"),sectionNumber=$section_selected.data("section-number"),courseId=$section_selected.data("course-id"),sectionName=$section_selected.data("section-name");_jquery.default.on_section_backup(sectionId,sectionNumber,courseId,sectionName)})),(0,_jquery.default)(".copy_activity").on("click",(function(e){const activitySelected=(0,_jquery.default)(".activity-dropdown option:selected"),activityId=activitySelected.data("activity-id"),activityName=activitySelected.data("activity-name");_jquery.default.on_backup(e,activityName,activityId)}))}})); - -//# sourceMappingURL=script.min.js.map \ No newline at end of file diff --git a/amd/build/script.min.js.map b/amd/build/script.min.js.map deleted file mode 100644 index f1010f0..0000000 --- a/amd/build/script.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"script.min.js","sources":["../src/script.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 * Sharing Cart\n *\n * @package block_sharing_cart\n * @copyright 2017 (C) VERSION2, INC.\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\n\n/**\n * @param {string} addMethod\n */\nexport const init = function(addMethod) {\n $(document).ready(function() {\n let isDragging = false;\n\n /**\n * Returns a localized string\n *\n * @param {String} identifier\n * @return {String}\n */\n function str(identifier) {\n return M.str.block_sharing_cart[identifier] || M.str.moodle[identifier];\n }\n\n /**\n * Get an action URL\n *\n * @param {String} name The action name\n * @param {Object} [args] The action parameters\n * @return {String}\n */\n function get_action_url(name, args) {\n let url = M.cfg.wwwroot + '/blocks/sharing_cart/' + name + '.php';\n if (args) {\n const q = [];\n for (let k in args) {\n q.push(k + '=' + encodeURIComponent(args[k]));\n }\n url += '?' + q.join('&');\n }\n return url;\n }\n\n /**\n * Shake the basket to indicate cancel/submit\n */\n function shake_basket() {\n if (addMethod === 'drag_and_drop') {\n const sharingCartBasket = document.querySelector('button.sharing_cart_basket');\n sharingCartBasket?.classList.add('shake_basket');\n }\n }\n\n /**\n * Remove the shake effect and basket icon\n */\n function remove_basket() {\n if (addMethod === 'drag_and_drop' && !isDragging) {\n const footer = document.getElementById('page-footer');\n const footerIconContainer = footer.querySelector('div[data-region=\"footer-container-popover\"]');\n const sharingCartBasket = document.querySelector('button.sharing_cart_basket');\n\n if (sharingCartBasket) {\n footerIconContainer?.removeChild(sharingCartBasket);\n sharingCartBasket.classList.remove('shake_basket');\n }\n }\n }\n\n /**\n * Modal called when confirming an action.\n *\n * @param obj\n */\n function confirm_modal(obj) {\n\n // Checkbox for copying userdata confirmation.\n if (obj.checkbox) {\n obj.body +=\n '
    ' +\n '
    ' +\n '' +\n '' +\n '
    ' +\n '
    ' +\n '' +\n '' +\n '
    ' +\n '
    ';\n }\n\n\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: obj.title,\n body: obj.body,\n }).done(function(modal) {\n let is_submitted = false;\n modal.setSaveButtonText(obj.save_button);\n\n const userdata_checkbox = $(modal.getRoot()).find('#modal-userdata-checkbox');\n const anonymize_checkbox = $(modal.getRoot()).find('#modal-anonymize-checkbox');\n\n userdata_checkbox.on('change', (e) => {\n if (e.currentTarget.checked) {\n anonymize_checkbox.attr('disabled', null);\n } else {\n anonymize_checkbox.prop('checked', false).attr('disabled', true);\n }\n });\n\n // On save save check - if checkbox is checked.\n modal.getRoot().on(ModalEvents.save, function(e) {\n const response = {\n 'userdata': userdata_checkbox.is(':checked'),\n 'anonymize': anonymize_checkbox.is(':checked'),\n };\n\n obj.next(response);\n is_submitted = true;\n });\n\n modal.getRoot().on(ModalEvents.cancel, function() {\n remove_basket();\n });\n\n // Remove modal from html.\n modal.getRoot().on(ModalEvents.hidden, function() {\n $('body').removeClass('modal-open');\n\n if (!is_submitted) {\n remove_basket();\n }\n });\n\n modal.show();\n });\n }\n\n /**\n * Get the section name from the section when\n * it's changed with the in place editor\n *\n * @param $section\n * @returns {*}\n */\n function in_place_edit_section_name($section) {\n let sectionName = '';\n const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable');\n if ($inPlaceEditable.length) {\n sectionName = $inPlaceEditable.data('value');\n }\n return sectionName;\n }\n\n /**\n * @param post_data\n * @param title_str\n * @param body_str\n * @param isSection\n */\n function on_backup_modal(post_data, title_str, body_str, isSection) {\n (function(on_success) {\n $.post(get_action_url('rest'), post_data,\n function(response) {\n on_success(response);\n }, \"text\")\n .fail(function(response) {\n show_error(response);\n });\n })(function(response) {\n const copyable = response === '1';\n let checkbox = false;\n\n if (copyable) {\n checkbox = true;\n }\n\n confirm_modal({\n 'title': title_str,\n 'body': body_str,\n 'save_button': str('modal_confirm_backup'),\n 'checkbox': checkbox,\n 'next': function(data) {\n if (isSection === true) {\n backup_section(post_data.sectionid, post_data.sectionnumber, post_data.courseid, data.userdata, data.anonymize);\n } else {\n backup(post_data.cmid, data.userdata, data.anonymize);\n }\n\n shake_basket();\n }\n });\n });\n }\n\n /** @var {Object} The icon configurations */\n const icon = {\n // Actions\n 'backup': {\n css: 'editing_backup',\n iconClass: 'fa fa-frown-o',\n },\n 'movedir': {\n css: 'editing_right',\n iconClass: 'fa fa-arrow-right',\n },\n 'move': {\n css: 'editing_move_',\n iconClass: 'fa fa-arrows-v',\n },\n 'edit': {\n css: 'editing_update',\n iconClass: 'fa fa-pencil',\n },\n 'cancel': {\n css: 'editing_cancel',\n iconClass: 'fa fa-ban',\n },\n 'delete': {\n css: 'editing_update',\n iconClass: 'fa fa-trash',\n },\n 'restore': {\n css: 'editing_restore',\n iconClass: 'fa fa-clone',\n },\n // Directories\n 'dir-open': {\n iconClass: 'fa fa-folder-open-o'\n },\n 'dir-closed': {\n iconClass: 'fa fa-folder-o'\n },\n };\n\n /** @var {Node} The Sharing Cart block container node */\n const $block = $('.block_sharing_cart');\n\n /** @var {Object} The current course */\n const course = new function () {\n const body = $('body');\n this.id = body.attr('class').match(/course-(\\d+)/)[1];\n this.is_frontpage = body.hasClass('pagelayout-frontpage');\n }();\n\n /**\n * Shows an error message with given Ajax error\n *\n * @param {Object} response The Ajax response\n */\n function show_error(response) {\n try {\n const ex = JSON.parse(response.responseText);\n new M.core.exception({\n name: str('pluginname') + ' - ' + str('error'),\n message: ex.message\n });\n } catch (e) {\n new M.core.exception({\n name: str('pluginname') + ' - ' + str('error'),\n message: response.responseText\n });\n }\n }\n\n /**\n * Check special layout (theme boost)\n *\n * @return {Boolean}\n */\n function verify_layout() {\n const menuelement = $block.find('.menubar .dropdown .dropdown-menu');\n return (menuelement.length);\n }\n\n /**\n * Set Cookie\n * @param name\n * @param value\n * @param expireTimeInMillisecond\n */\n function setCookie(name, value, expireTimeInMillisecond) {\n const d = new Date();\n d.setTime(d.getTime() + expireTimeInMillisecond);\n const expires = 'expires=' + d.toUTCString();\n document.cookie = name + '=' + value + ';' + expires + '';\n }\n\n /**\n * Get Cookie Value\n * @param param\n * @returns {*}\n */\n function getCookieValue(param) {\n const readCookie = document.cookie.match('(^|;)\\\\s*' + param + '\\\\s*=\\\\s*([^;]+)');\n return readCookie ? readCookie.pop() : '';\n }\n\n /**\n * Create a command icon\n *\n * @param {String} name The command name, predefined in icon\n * @param {String} [pix] The icon pix name to override\n */\n function create_command(name) {\n const iconElement = $('')\n .attr('alt', str(name))\n .attr('class', icon[name].iconClass);\n // If (verify_layout()) {\n // iconElement.addClass('iconcustom');\n // }\n\n return $('
    ')\n .addClass(icon[name].css)\n .attr('title', str(name))\n .append(iconElement);\n }\n\n /**\n * Create a spinner\n * @param $node\n * @returns {*|jQuery}\n */\n function add_spinner() {\n const $spinner = ($('
    '));\n $('section.block_sharing_cart').append($spinner);\n return $spinner;\n }\n\n /**\n *\n * @param $node\n * @returns {jQuery.fn.init}\n */\n function add_node_spinner($node) {\n const $node_spinner = $('');\n $node.append($node_spinner);\n return $node_spinner;\n }\n\n $(document).on('click', 'a.restore', function() {\n add_spinner();\n });\n\n /**\n *\n * Reload the Sharing Cart item tree\n */\n function reload_tree() {\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"render_tree\",\n \"courseid\": course.id\n },\n function(response) {\n $block.find(\".tree\").replaceWith($(response));\n $.init_item_tree();\n }, \"html\")\n .fail(function(response) {\n show_error(response);\n });\n }\n\n /**\n * Backup an activity\n *\n * @param {int} cmid\n * @param {Boolean} userdata\n * @param {Boolean} anonymize\n */\n function backup(cmid, userdata, anonymize) {\n let $commands = $('#module-' + cmid + ' .actions');\n if (!$commands.length) {\n $commands = $('[data-owner=\"#module-' + cmid + '\"]');\n }\n\n const $spinner = add_spinner();\n const $node_spinner = add_node_spinner($commands);\n\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"backup\",\n \"cmid\": cmid,\n \"userdata\": userdata,\n \"anonymize\": anonymize,\n \"sesskey\": M.cfg.sesskey,\n \"courseid\": course.id\n },\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $node_spinner.hide();\n $spinner.hide();\n remove_basket();\n });\n }\n\n /**\n * Backup an activities in a section\n *\n * @param {int} sectionId\n * @param {int} sectionNumber\n * @param {int} courseId\n * @param {Boolean} userdata\n * @param {Boolean} anonymize\n */\n function backup_section(sectionId, sectionNumber, courseId, userdata, anonymize) {\n const $commands = $('span.inplaceeditable[data-itemtype=sectionname][data-itemid=' + sectionId + ']');\n const $section = $commands.closest(\"li.section.main\");\n let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim();\n\n if (sectionName === null) {\n sectionName = String($('#region-main .section_action_menu[data-sectionid=\\'' + sectionId + '\\']')\n .parent().parent().find('h3.sectionname').text());\n }\n\n const inPlaceEditSectionName = in_place_edit_section_name($section);\n sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName;\n\n const $spinner = add_spinner();\n const $node_spinner = add_node_spinner($commands);\n\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"backup_section\",\n \"sectionid\": sectionId,\n \"sectionnumber\": sectionNumber,\n \"courseid\": courseId,\n \"sectionname\": sectionName,\n \"userdata\": userdata,\n \"anonymize\": anonymize,\n \"sesskey\": M.cfg.sesskey\n },\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n $node_spinner.hide();\n remove_basket();\n });\n }\n\n\n // /////// CLASSES /////////\n\n /**\n * @class Directory states manager\n */\n const directories = new function () {\n const KEY = 'block_sharing_cart-dirs';\n\n let opens = getCookieValue(KEY).split(',').map(function (v) {\n return parseInt(v);\n });\n\n function save() {\n const expires = new Date();\n expires.setDate(expires.getDate() + 30);\n setCookie(KEY, opens.join(','), expires);\n }\n\n function open($dir, visible) {\n const iconIndex = visible ? 'dir-open' : 'dir-closed';\n const iconElement = icon[iconIndex].iconClass;\n $dir.find('> div i.icon').attr('class', 'icon ' + iconElement);\n $dir.find('> ul.list')[visible ? 'show' : 'hide']();\n }\n\n function toggle(e) {\n const $dir = $(e.target).closest('li.directory');\n const i = $dir.attr('id').match(/(\\d+)$/)[1];\n const v = $dir.find('> ul.list').css('display') === 'none';\n\n open($dir, v);\n opens[i] = v ? 1 : 0;\n save();\n }\n\n /**\n * Initialize directory states\n */\n this.init = function () {\n let i = 0;\n $block.find('li.directory').each(function (index, dir) {\n const $dir = $(dir);\n $dir.attr('id', 'block_sharing_cart-dir-' + i);\n if (i >= opens.length) {\n opens.push(0);\n } else if (opens[i]) {\n open($dir, true);\n }\n $dir.find('> div div.toggle-wrapper').css('cursor', 'pointer').on('click', function (e) {\n toggle(e);\n });\n i++;\n });\n };\n\n /**\n * Reset directory states\n */\n this.reset = function () {\n opens = [];\n this.init();\n save();\n };\n }();\n\n /**\n * @class Targets for moving an item directory\n */\n const move_targets = new function () {\n let $cancel = null,\n targets = [];\n\n /**\n * Hide move targets\n */\n this.hide = function () {\n if ($cancel !== null) {\n const $commands = $cancel.closest('.commands');\n $cancel.remove();\n $cancel = null;\n $commands.closest('li.activity').css('opacity', 1.0);\n $commands.find('a').each(function () {\n $(this).show();\n });\n $.each(targets, function (index, $target) {\n $target.remove();\n });\n targets = [];\n }\n };\n\n /**\n * Show move targets for a given item\n *\n * @param {int} id The item ID\n */\n this.show = function (item_id) {\n this.hide();\n\n function move(e) {\n\n const m = $(e.target).closest('a').attr('class').match(/move-(\\d+)-to-(\\d+)/);\n const item_id = m[1],\n area_to = m[2];\n\n const $spinner = add_spinner();\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"move\",\n \"item_id\": item_id,\n \"area_to\": area_to,\n \"sesskey\": M.cfg.sesskey\n },\n function () {\n reload_tree();\n })\n .fail(function (response) {\n show_error(response);\n })\n .always(function () {\n $spinner.hide();\n });\n }\n\n const $current = $block.find('#block_sharing_cart-item-' + item_id);\n const $next = $current.next();\n const $list = $current.closest('ul');\n\n let next_id = 0;\n if ($next.length) {\n next_id = $next.attr('id').match(/item-(\\d+)$/)[1];\n }\n\n /**\n *\n * @param item_id\n * @param area_to\n * @returns {jQuery}\n */\n function create_target(item_id, area_to) {\n const $anchor = $('
    ')\n .addClass('move-' + item_id + '-to-' + area_to)\n .attr('title', str('movehere'))\n .append(\n $('

    ' + str('clicktomove') + '

    ')\n .attr('alt', str('movehere'))\n );\n\n const $target = $('
  • ')\n .append($anchor);\n $anchor.on('click', function (e) {\n move(e);\n });\n\n return $target;\n }\n\n $list.find('> li.activity').each(function (index, item) {\n const $item = $(item);\n const to = $item.attr('id').match(/item-(\\d+)$/)[1];\n if (to === item_id) {\n $cancel = create_command('cancel', 't/left');\n $cancel.on('click', function () {\n move_targets.hide();\n });\n const $commands = $item.find('.commands');\n $commands.find('a').each(function () {\n $(this).hide();\n });\n $commands.append($cancel);\n $item.css('opacity', 0.5);\n } else if (to !== next_id) {\n const $target = create_target(item_id, to);\n $item.before($target);\n targets.push($target);\n }\n }, this);\n\n if ($next) {\n var $target = create_target(item_id, 0);\n $list.append($target);\n targets.push($target);\n }\n };\n }();\n\n /**\n * @class Targets for restoring an item\n */\n const restore_targets = new function () {\n this.is_directory = null;\n let $clipboard = null,\n targets = [];\n\n /**\n *\n * @param id\n * @param section\n * @returns {jQuery}\n */\n\n function create_target(id, section) {\n const href = get_action_url('restore', {\n 'directory': (restore_targets.is_directory === true),\n 'target': id,\n 'course': course.id,\n 'section': section,\n 'in_section': $('#copy-section-form').data('in-section'),\n 'sesskey': M.cfg.sesskey,\n 'returnurl': document.URL,\n });\n\n let $target = $('').attr('href', href).attr('title', str('copyhere')).append(\n $('').attr('alt', str('copyhere')).attr('src', M.util.image_url('dropzone_arrow', 'block_sharing_cart'))\n );\n\n targets.push($target);\n\n return $target;\n }\n\n /**\n * Hide restore targets\n */\n this.hide = function () {\n if ($clipboard !== null) {\n $clipboard.remove();\n $clipboard = null;\n $.each(targets, function (index, $target) {\n $target.remove();\n });\n targets = [];\n }\n };\n\n /**\n *\n *\n * @param {int} id The item ID\n */\n this.show = function (id) {\n this.hide();\n\n let $view = $(\"\");\n\n if (this.is_directory) {\n $view.html(id).css('display', 'inline');\n $view.prepend(\n $(\"\").addClass(\"icon\")\n .attr(\"alt\", id)\n // .attr(\"src\", M.util.image_url(icon['dir-closed'].pix, null))\n );\n } else {\n const $item = $block.find('#block_sharing_cart-item-' + id);\n $view = $($item.find('div')[0].cloneNode(true)).css('display', 'inline');\n $view.attr('class', $view.attr('class').replace(/mod-indent-\\d+/, ''));\n $view.find('.commands').remove();\n }\n\n const $cancel = create_command('cancel');\n\n $cancel.on('click', this.hide);\n\n $clipboard = $('
    ');\n $clipboard.append(str('clipboard') + \": \").append($view).append($cancel);\n\n if (course.is_frontpage) {\n const $sitetopic = $('.sitetopic');\n const $mainmenu = $('.block_site_main_menu');\n if ($sitetopic) {\n $sitetopic.find('*').before($clipboard);\n } else if ($mainmenu) {\n $mainmenu.find('.content').before($clipboard);\n }\n\n // Mainmenu = section #0, sitetopic = section #1\n if ($mainmenu) {\n $mainmenu.find('.footer').before(create_target(id, 0));\n }\n if ($sitetopic) {\n $sitetopic.find('ul.section').append(create_target(id, 1));\n }\n } else {\n const $container = $('.course-content');\n $container.prepend($clipboard);\n $container.find(M.course.format.get_section_wrapper(null)).each(function (index, sectionDOM) {\n const $section = $(sectionDOM);\n const section = $section.attr('id').match(/(\\d+)$/)[1];\n $section.find('ul.section').first().append(create_target(id, section));\n }, this);\n }\n };\n }();\n\n // /////// INITIALIZATION /////////\n\n /**\n *\n * @returns {string|*}\n */\n $.get_plugin_name = function() {\n let $blockheader = $block.find(\"h2\");\n\n if (!$blockheader.length) {\n $blockheader = $block.find(\"h3\");\n\n if ($blockheader.length) {\n return $blockheader.html();\n }\n } else {\n return $blockheader.html();\n }\n\n return \"\";\n };\n\n /**\n *\n * @param e\n * @param activityName\n * @param {int} cmId\n */\n $.on_backup = function(e, activityName, cmId = 0) {\n if (cmId === 0) {\n cmId = (function ($backup) {\n const $activity = $backup.closest('li.activity');\n if ($activity.length) {\n return $activity.attr('id').match(/(\\d+)$/)[1];\n }\n const $commands = $backup.closest('.commands');\n const dataowner = $commands.attr('data-owner');\n if (dataowner.length) {\n return dataowner.match(/(\\d+)$/)[1];\n }\n return $commands.find('a.editing_delete').attr('href').match(/delete=(\\d+)/)[1];\n })($(e.target));\n }\n\n const data =\n {\n \"action\": \"is_userdata_copyable\",\n \"cmid\": cmId\n };\n\n on_backup_modal(data, activityName, str('confirm_backup'), false);\n };\n\n /**\n * On movedir command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_movedir = function(e) {\n const $commands = $(e.target).closest('.commands');\n\n const $current_dir = $commands.closest('li.directory');\n const current_path = $current_dir.length ? $current_dir.attr('directory-path') : '/';\n\n const item_id = $(e.target).closest('li.activity').attr('id').match(/(\\d+)$/)[1];\n\n const dirs = [];\n $block.find('li.directory').each(function() {\n dirs.push($(this).attr('directory-path'));\n });\n\n const $form = $('');\n // eslint-disable-next-line no-script-url\n $form.attr('action', 'javascript:void(0)');\n\n function submit() {\n const folder_to = $form.find('[name=\"to\"]').val();\n const $spinner = add_spinner();\n $.post(get_action_url('rest'),\n {\n \"action\": \"movedir\",\n \"item_id\": item_id,\n \"folder_to\": folder_to,\n \"sesskey\": M.cfg.sesskey\n },\n function() {\n reload_tree();\n directories.reset();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n });\n }\n\n $form.submit(submit);\n\n if (dirs.length === 0) {\n var $input = $('').val(current_path);\n setTimeout(function() {\n $input.focus();\n }, 1);\n $form.append($input);\n } else {\n dirs.unshift('/');\n\n const $select = $('').val(current_path);\n $select.remove();\n $edit.replaceWith($input);\n $input.focus();\n });\n\n $form.append($edit);\n }\n\n const $cancel = create_command('cancel');\n $cancel.on('click', function() {\n $form.remove();\n $commands.find('a').show();\n });\n $form.append($cancel);\n\n $commands.find('a').each(function() {\n $(this).hide();\n });\n $commands.append($form);\n };\n\n /**\n * On move command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_move = function(e) {\n const $item = $(e.target).closest('li.activity');\n const id = $item.attr('id').match(/(\\d+)$/)[1];\n\n move_targets.show(id);\n };\n\n /**\n * On delete command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_delete = function(e) {\n const $item = $(e.target).closest('li');\n const liText = $item[0].innerText;\n\n let isDirectory = false;\n let modalBody;\n let item;\n let description_text = '';\n\n if ($item.hasClass(\"directory\")) {\n isDirectory = true;\n item = str('folder_string');\n description_text = str('delete_folder');\n } else {\n item = str('activity_string');\n }\n\n modalBody = '

    ' + item + ' ' + liText + description_text + '

    ';\n\n confirm_modal({\n 'title': str('confirm_delete'),\n 'body': modalBody,\n 'save_button': str('modal_confirm_delete'),\n 'checkbox': false,\n 'next': function() {\n\n let data = {};\n\n if (isDirectory === true) {\n data = {\n \"action\": \"delete_directory\",\n \"path\": $item.attr(\"directory-path\"),\n \"sesskey\": M.cfg.sesskey\n };\n } else if ($item.hasClass(\"activity\")) {\n data = {\n \"action\": \"delete\",\n \"id\": $item.attr('id').match(/(\\d+)$/)[1],\n \"sesskey\": M.cfg.sesskey\n };\n }\n\n const $spinner = add_spinner();\n\n $.post(get_action_url(\"rest\"), data,\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n });\n\n e.stopPropagation();\n }\n });\n };\n\n /**\n * On restore command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_restore = function(e) {\n const $item = $(e.target).closest('li');\n let id = null;\n\n if ($item.hasClass(\"directory\")) {\n id = $item.attr(\"directory-path\");\n restore_targets.is_directory = true;\n } else if ($item.hasClass(\"activity\")) {\n id = $item.attr('id').match(/(\\d+)$/)[1];\n restore_targets.is_directory = false;\n }\n\n restore_targets.show(id);\n };\n\n /**\n * On backup the whole section as a folder\n *\n * @param {int} sectionId\n * @param {int} sectionNumber\n * @param {int} courseId\n * @param {string} sectionName\n */\n $.on_section_backup = function(sectionId, sectionNumber, courseId, sectionName) {\n const data =\n {\n \"action\": \"is_userdata_copyable_section\",\n \"sectionid\": sectionId,\n \"sectionnumber\": sectionNumber,\n \"courseid\": courseId,\n };\n\n const body_html = '

    ' + str('backup_heavy_load_warning_message') +\n '

    ' + str('confirm_backup_section');\n\n on_backup_modal(data, sectionName, body_html, true);\n };\n\n /**\n * Initialize the delete bulk\n */\n $.init_bulk_delete = function(isspeciallayout) {\n const bulkdelete = $block.find('.editing_bulkdelete');\n if (bulkdelete.length) {\n if (isspeciallayout) {\n bulkdelete.attr('role', 'menuitem').addClass('dropdown-item menu-action');\n bulkdelete.append($(\"\").append(bulkdelete.attr('title')));\n\n $block.find('.menubar .dropdown .dropdown-menu').append(bulkdelete);\n } else {\n $block.find('.header .commands').append(bulkdelete);\n }\n }\n };\n\n /**\n * Initialize the help icon\n */\n $.init_help_icon = function(isspeciallayout) {\n const helpicon = $block.find('.header-commands > .help-icon');\n\n if (isspeciallayout) {\n $block.find('.header-commands').parent().css('display', 'block');\n } else {\n $block.find('.header .commands').append(helpicon);\n }\n };\n\n /**\n * Initialize the Sharing Cart block header\n */\n $.init_block_header = function() {\n const isspeciallayout = verify_layout();\n $.init_bulk_delete(isspeciallayout);\n $.init_help_icon(isspeciallayout);\n };\n\n /**\n * Initialize the Sharing Cart item tree\n */\n $.init_item_tree = function() {\n function add_actions(item, actions) {\n const $item = $(item);\n const isCopying = $item.attr('data-is-copying') === '1';\n const $commands = $item.find('.commands').first();\n\n $.each(actions, function(index, action) {\n if (action === 'restore' && isCopying) {\n return;\n }\n const $command = create_command(action);\n $command.on('click', function(e) {\n $['on_' + action](e);\n });\n $commands.append($command);\n }, this);\n }\n\n const activity_actions = ['movedir', 'move', 'delete'];\n if (course) {\n activity_actions.push('restore');\n }\n\n const directory_actions = ['delete', 'restore'];\n\n // Initialize items\n $block.find('li.activity').each(function(index, item) {\n if($(item).attr('data-disable-copy') == 1) {\n add_actions(item, ['movedir', 'move', 'delete']);\n return;\n }\n add_actions(item, activity_actions);\n });\n\n // Initialize directory items\n $block.find('li.directory').each(function(index, item) {\n add_actions(item, directory_actions);\n });\n\n // Initialize directories\n directories.init();\n };\n\n /**\n * Extract html object from area where moodle ajax was called.\n *\n * Call add_activity_backup_control to re append sharing cart icon.\n */\n $.init_activity_commands = function() {\n $(document).ajaxComplete(function(event, xhr, settings) {\n\n const url = settings.url;\n const lastslashindex = url.lastIndexOf('=');\n const result = url.substring(lastslashindex + 1);\n\n if (result === 'core_course_edit_module' || result === 'core_course_get_module') {\n\n const data = JSON.parse(settings.data);\n const action = data[0].args.action;\n\n // Don't try to add icon if activity has been deleted.\n if (action === 'delete') {\n return;\n }\n\n setTimeout(function() {\n const activity_id = data[0].args.id;\n const activity = $('#module-' + activity_id);\n add_activity_backup_control(activity);\n\n if (action === 'duplicate') {\n const duplicated = activity.next();\n add_activity_backup_control(duplicated);\n }\n }, 1);\n }\n });\n\n /**\n * Create the backup icon\n *\n * @returns $backupIcon\n */\n function create_backup_icon() {\n\n const $backupIcon = $('
    ')\n .append($(''))\n .attr('title', str('backup'));\n\n if (addMethod !== 'click_to_add') {\n $backupIcon.addClass('d-none');\n }\n\n return $backupIcon;\n }\n\n /**\n * Add backup control with a click event to an activity\n * Added fix for copying an activity without backup routine\n *\n * @param $activity\n */\n function add_activity_backup_control($activity) {\n\n const activityClass = $activity[0].className;\n\n // Selecting modtype without prefix.\n const modtype = activityClass.substr(activityClass.indexOf('modtype_') + 8);\n\n // Default activity name.\n let activityName = str('activity_string');\n\n // Label is using a different html / css layout, so it's needed to get the name by using another $find.\n if (modtype !== 'label') {\n activityName = $('.activity#' + $activity[0].id)\n .find('.mod-indent-outer .activityinstance span.instancename')\n .html();\n }\n\n const $backupIcon = create_backup_icon();\n\n $backupIcon.on('click', function(e) {\n $.on_backup(e, activityName);\n });\n\n const $actionMenuItem = $activity.find('.action-menu.section-cm-edit-actions').parent('.actions');\n\n if (!$actionMenuItem.find('.add-to-sharing-cart').length) {\n $actionMenuItem.append($backupIcon);\n }\n }\n\n /**\n * Add backup control with a click event to a section\n *\n * @param $section\n */\n function add_section_backup_control($section) {\n\n let sectionId = $section.find('.section_action_menu').data('sectionid');\n const sectionNumber = parseInt(String($section.attr('id')).match(/\\d+/)[0]);\n let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim();\n\n const isFlexibleCourseFormat = $('body[id$=flexsections]').length;\n\n // Extract the section ID from the section if this is a Flexible\n // course format (since this format doesn't have an action menu)\n if (isFlexibleCourseFormat && (typeof sectionId === 'undefined' || sectionId === null)) {\n sectionId = $section.data('section-id');\n }\n\n // A bit unsafe to extract the course ID from the body but it's the best option we got at the moment\n const courseId = parseInt(String($('body').attr('class')).match(/course-([0-9]*)( |$)/)[1]);\n\n const $backupIcon = create_backup_icon();\n\n $backupIcon.on('click', function() {\n const inPlaceEditSectionName = in_place_edit_section_name($section);\n sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName;\n $.on_section_backup(sectionId, sectionNumber, courseId, sectionName);\n });\n\n let $sectionTitle = $section.find('h3.sectionname').first().find('a').last();\n\n const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable').first();\n if ($inPlaceEditable.length) {\n $sectionTitle = $inPlaceEditable;\n }\n\n // Add the backup icon after the cog wheel if this is a Flexible course format\n if (isFlexibleCourseFormat && sectionNumber === 0) {\n $sectionTitle = $section.find('> .controls');\n $sectionTitle.prepend($backupIcon);\n } else {\n $backupIcon.insertAfter($sectionTitle);\n }\n\n const activitySelector = 'li.activity';\n\n const $activities = $section.find(activitySelector);\n\n $($activities).each(function() {\n add_activity_backup_control($(this));\n });\n }\n\n $(\"body.editing .course-content li.section\").each(function() {\n add_section_backup_control($(this));\n });\n };\n\n /**\n * Initialize the Sharing Cart footer basket for 4.0+.\n */\n function init_footer_basket() {\n let currentDragging;\n const activities = document.querySelectorAll(\".activity.activity-wrapper\");\n const sections = document.querySelectorAll(\".course-section-header\");\n const sharingCartBlock = document.querySelector('section[data-block=\"sharing_cart\"]');\n\n add_draggable_to_first_section();\n\n const footer = document.getElementById('page-footer');\n const footerIconContainer = footer.querySelector('div[data-region=\"footer-container-popover\"]');\n\n const basket = document.createElement('i');\n basket.setAttribute('class', 'fa fa-shopping-basket');\n\n const basketButton = document.createElement('button');\n basketButton.setAttribute('class', 'btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket');\n basketButton.setAttribute('style', 'z-index: 1001;');\n basketButton.append(basket);\n\n const dropAreaText = document.createElement('p');\n dropAreaText.setAttribute('class', 'font-weight-bold text-white');\n dropAreaText.innerText = str('drop_here');\n\n const dropArea = document.createElement('div');\n dropArea.setAttribute('class',\n 'h-100 w-100 position-absolute d-flex justify-content-center align-items-center');\n dropArea.append(dropAreaText);\n\n sections.forEach(section => {\n drag_event_listeners(section);\n });\n\n activities.forEach(activity => {\n drag_event_listeners(activity);\n });\n\n /**\n * Initialize events for dragging\n * @param {object} draggable\n */\n function drag_event_listeners(draggable) {\n draggable.addEventListener('dragstart', (e) => {\n basketButton.classList.remove('shake_basket');\n\n footerIconContainer?.prepend(basketButton);\n sharingCartBlock.children[0].classList.add('dragging_item');\n sharingCartBlock.append(dropArea);\n currentDragging = e.target;\n isDragging = true;\n });\n\n draggable.addEventListener('dragend', () => {\n if (currentDragging instanceof HTMLElement) {\n footerIconContainer?.removeChild(basketButton);\n }\n\n sharingCartBlock.children[0].classList.remove('dragging_item');\n sharingCartBlock.removeChild(dropArea);\n isDragging = false;\n });\n }\n\n [basketButton, sharingCartBlock].forEach((dropzone) => {\n dropzone.addEventListener(\"dragover\", (e) => {\n e.preventDefault();\n dropzone.classList.add('drag_over');\n });\n\n dropzone.addEventListener(\"dragenter\", (e) => {\n e.preventDefault();\n dropzone.classList.add('drag_over');\n });\n\n dropzone.addEventListener(\"dragleave\", () => {\n dropzone.classList.remove('drag_over');\n });\n\n dropzone.addEventListener(\"drop\", () => {\n if (currentDragging instanceof HTMLElement) {\n currentDragging.querySelector('.add-to-sharing-cart').click();\n }\n\n dropzone.classList.remove('drag_over');\n currentDragging = undefined;\n isDragging = false;\n });\n });\n }\n\n /**\n * Make the first section (General) draggable\n */\n function add_draggable_to_first_section() {\n const courseSectionHeader = document.getElementsByClassName(\"course-section-header\")[0] ?? null;\n\n if (courseSectionHeader instanceof HTMLElement) {\n courseSectionHeader.classList.add('draggable');\n courseSectionHeader.setAttribute('draggable', true);\n }\n }\n\n /**\n * Initialize the Sharing Cart block\n */\n $.init = function() {\n M.str.block_sharing_cart.pluginname = this.get_plugin_name();\n\n // Arrange header icons (bulkdelete, help)\n $.init_block_header();\n $.init_item_tree();\n $.init_activity_commands();\n\n if (addMethod === 'drag_and_drop') {\n init_footer_basket();\n }\n };\n var $spinner = $('').addClass('spinner fa fa-3x fa-circle-o-notch fa-spin');\n $('div#sharing-cart-spinner-modal div.spinner-container').prepend($spinner);\n\n $.init();\n });\n\n $('.copy_section').on('click', function() {\n\n const $section_selected = ($('.section-dropdown option:selected'));\n const sectionId = $section_selected.data('section-id');\n const sectionNumber = $section_selected.data('section-number');\n const courseId = $section_selected.data('course-id');\n const sectionName = $section_selected.data('section-name');\n\n $.on_section_backup(sectionId, sectionNumber, courseId, sectionName);\n });\n\n $('.copy_activity').on('click', function(e) {\n const activitySelected = ($('.activity-dropdown option:selected'));\n const activityId = activitySelected.data('activity-id');\n const activityName = activitySelected.data('activity-name');\n\n $.on_backup(e, activityName, activityId);\n });\n};\n"],"names":["addMethod","document","ready","isDragging","str","identifier","M","block_sharing_cart","moodle","get_action_url","name","args","url","cfg","wwwroot","q","k","push","encodeURIComponent","join","remove_basket","footerIconContainer","getElementById","querySelector","sharingCartBasket","removeChild","classList","remove","confirm_modal","obj","checkbox","body","create","type","ModalFactory","types","SAVE_CANCEL","title","done","modal","is_submitted","setSaveButtonText","save_button","userdata_checkbox","getRoot","find","anonymize_checkbox","on","e","currentTarget","checked","attr","prop","ModalEvents","save","response","is","next","cancel","hidden","removeClass","show","in_place_edit_section_name","$section","sectionName","$inPlaceEditable","length","data","on_backup_modal","post_data","title_str","body_str","isSection","post","sectionId","sectionNumber","courseId","userdata","anonymize","$commands","closest","text","trim","String","parent","inPlaceEditSectionName","$spinner","add_spinner","$node_spinner","add_node_spinner","sesskey","reload_tree","fail","show_error","always","hide","backup_section","sectionid","sectionnumber","courseid","cmid","course","id","backup","add","shake_basket","on_success","icon","css","iconClass","$block","match","is_frontpage","hasClass","ex","JSON","parse","responseText","core","exception","message","create_command","iconElement","addClass","append","$node","replaceWith","init_item_tree","directories","KEY","opens","param","readCookie","cookie","pop","getCookieValue","split","map","v","parseInt","expires","Date","setDate","getDate","value","expireTimeInMillisecond","d","setTime","getTime","toUTCString","setCookie","open","$dir","visible","init","i","each","index","dir","target","toggle","reset","move_targets","$cancel","targets","this","$target","item_id","$current","$next","$list","next_id","create_target","area_to","$anchor","m","move","item","$item","to","before","restore_targets","is_directory","$clipboard","section","href","URL","util","image_url","$view","html","prepend","cloneNode","replace","$sitetopic","$mainmenu","$container","format","get_section_wrapper","sectionDOM","first","init_footer_basket","currentDragging","activities","querySelectorAll","sections","sharingCartBlock","courseSectionHeader","getElementsByClassName","HTMLElement","setAttribute","add_draggable_to_first_section","basket","createElement","basketButton","dropAreaText","innerText","dropArea","drag_event_listeners","draggable","addEventListener","children","forEach","activity","dropzone","preventDefault","click","undefined","get_plugin_name","$blockheader","on_backup","activityName","cmId","$backup","$activity","dataowner","on_movedir","$current_dir","current_path","dirs","$form","submit","folder_to","val","$input","setTimeout","focus","unshift","$select","change","$edit","on_move","on_delete","liText","modalBody","isDirectory","description_text","stopPropagation","on_restore","on_section_backup","init_bulk_delete","isspeciallayout","bulkdelete","init_help_icon","helpicon","init_block_header","add_actions","actions","isCopying","action","$command","activity_actions","directory_actions","init_activity_commands","create_backup_icon","$backupIcon","add_activity_backup_control","activityClass","className","modtype","substr","indexOf","$actionMenuItem","ajaxComplete","event","xhr","settings","lastslashindex","lastIndexOf","result","substring","activity_id","isFlexibleCourseFormat","$sectionTitle","last","insertAfter","$activities","add_section_backup_control","pluginname","$section_selected","activitySelected","activityId"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/bulkdelete.js b/amd/src/bulkdelete.js deleted file mode 100644 index ce20fc6..0000000 --- a/amd/src/bulkdelete.js +++ /dev/null @@ -1,117 +0,0 @@ -define(['jquery', 'core/modal_factory', 'core/modal_events'], function($, ModalFactory, ModalEvents) { - - return { - init: function() { - - /** - * Returns a localized string - * - * @param {String} identifier - * @return {String} - */ - function str(identifier) { - return M.str.block_sharing_cart[identifier] || M.str.moodle[identifier]; - } - - /** - * - * @param object - */ - function confirm_modal(object) { - var trigger = $('#create-modal'); - ModalFactory.create({ - type: ModalFactory.types.SAVE_CANCEL, - title: object.title, - body: object.body, - }, trigger).done(function(modal) { - modal.setSaveButtonText(object.save_button); - - modal.getRoot().on(ModalEvents.save, function() { - object.next(); - }); - - // Remove modal from html. - modal.getRoot().on(ModalEvents.hidden, function() { - $('.modal.moodle-has-zindex').remove(); - }); - modal.show(); - }); - } - - /** - * - * @returns {any[]} - */ - function get_checks() { - var elements = $('form :checkbox[name^="delete"]'); - return elements; - } - - /** - * - * @param check - */ - function check_all(check) { - var checks = get_checks(); - $(checks).prop('checked', check.checked); - - $('form :button[name ="delete_checked"]').prop('disabled', !check.checked); - } - - /** - * - */ - function check() { - var delete_checked = $('form :button[name^="delete_checked"]'); - var checks = get_checks(); - var checked_checkbox = false; - - $(checks).each(function(i, val) { - if ($(val).prop('checked')) { - checked_checkbox = true; - return false; - } - }); - - delete_checked.prop('disabled', !checked_checkbox); - $('.bulk-delete-select-all :checkbox').prop('checked', checked_checkbox); - } - - /** - * Check activity button - */ - $('.bulk-delete-item [id^=delete]').on('click', function() { - check(); - }); - - /** - * Select all checkbox. - */ - $('.bulk-delete-select-all input').on('click', function() { - check_all(this); - }); - - /** - * Delete selected, opens modal for confirmation. - */ - $('.form_submit').on('click', function() { - var modal_body = '
      '; - var selected_input = $('.bulk-delete-item input:checked'); - $(selected_input).each(function() { - var label = $('label[for="' + this.id + '"]'); - modal_body += '
    • ' + label.text() + '
    • '; - }); - modal_body += '
    '; - - confirm_modal({ - 'title': str('modal_bulkdelete_title'), - 'body': modal_body, - 'save_button': str('modal_bulkdelete_confirm'), - 'next': function() { - $('#form').submit(); - } - }); - }); - } - }; -}); diff --git a/amd/src/script.js b/amd/src/script.js deleted file mode 100644 index b882b91..0000000 --- a/amd/src/script.js +++ /dev/null @@ -1,1404 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import $ from 'jquery'; -import ModalFactory from 'core/modal_factory'; -import ModalEvents from 'core/modal_events'; - -/** - * @param {string} addMethod - */ -export const init = function(addMethod) { - $(document).ready(function() { - let isDragging = false; - - /** - * Returns a localized string - * - * @param {String} identifier - * @return {String} - */ - function str(identifier) { - return M.str.block_sharing_cart[identifier] || M.str.moodle[identifier]; - } - - /** - * Get an action URL - * - * @param {String} name The action name - * @param {Object} [args] The action parameters - * @return {String} - */ - function get_action_url(name, args) { - let url = M.cfg.wwwroot + '/blocks/sharing_cart/' + name + '.php'; - if (args) { - const q = []; - for (let k in args) { - q.push(k + '=' + encodeURIComponent(args[k])); - } - url += '?' + q.join('&'); - } - return url; - } - - /** - * Shake the basket to indicate cancel/submit - */ - function shake_basket() { - if (addMethod === 'drag_and_drop') { - const sharingCartBasket = document.querySelector('button.sharing_cart_basket'); - sharingCartBasket?.classList.add('shake_basket'); - } - } - - /** - * Remove the shake effect and basket icon - */ - function remove_basket() { - if (addMethod === 'drag_and_drop' && !isDragging) { - const footer = document.getElementById('page-footer'); - const footerIconContainer = footer.querySelector('div[data-region="footer-container-popover"]'); - const sharingCartBasket = document.querySelector('button.sharing_cart_basket'); - - if (sharingCartBasket) { - footerIconContainer?.removeChild(sharingCartBasket); - sharingCartBasket.classList.remove('shake_basket'); - } - } - } - - /** - * Modal called when confirming an action. - * - * @param obj - */ - function confirm_modal(obj) { - - // Checkbox for copying userdata confirmation. - if (obj.checkbox) { - obj.body += - ''; - } - - - ModalFactory.create({ - type: ModalFactory.types.SAVE_CANCEL, - title: obj.title, - body: obj.body, - }).done(function(modal) { - let is_submitted = false; - modal.setSaveButtonText(obj.save_button); - - const userdata_checkbox = $(modal.getRoot()).find('#modal-userdata-checkbox'); - const anonymize_checkbox = $(modal.getRoot()).find('#modal-anonymize-checkbox'); - - userdata_checkbox.on('change', (e) => { - if (e.currentTarget.checked) { - anonymize_checkbox.attr('disabled', null); - } else { - anonymize_checkbox.prop('checked', false).attr('disabled', true); - } - }); - - // On save save check - if checkbox is checked. - modal.getRoot().on(ModalEvents.save, function(e) { - const response = { - 'userdata': userdata_checkbox.is(':checked'), - 'anonymize': anonymize_checkbox.is(':checked'), - }; - - obj.next(response); - is_submitted = true; - }); - - modal.getRoot().on(ModalEvents.cancel, function() { - remove_basket(); - }); - - // Remove modal from html. - modal.getRoot().on(ModalEvents.hidden, function() { - $('body').removeClass('modal-open'); - - if (!is_submitted) { - remove_basket(); - } - }); - - modal.show(); - }); - } - - /** - * Get the section name from the section when - * it's changed with the in place editor - * - * @param $section - * @returns {*} - */ - function in_place_edit_section_name($section) { - let sectionName = ''; - const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable'); - if ($inPlaceEditable.length) { - sectionName = $inPlaceEditable.data('value'); - } - return sectionName; - } - - /** - * @param post_data - * @param title_str - * @param body_str - * @param isSection - */ - function on_backup_modal(post_data, title_str, body_str, isSection) { - (function(on_success) { - $.post(get_action_url('rest'), post_data, - function(response) { - on_success(response); - }, "text") - .fail(function(response) { - show_error(response); - }); - })(function(response) { - const copyable = response === '1'; - let checkbox = false; - - if (copyable) { - checkbox = true; - } - - confirm_modal({ - 'title': title_str, - 'body': body_str, - 'save_button': str('modal_confirm_backup'), - 'checkbox': checkbox, - 'next': function(data) { - if (isSection === true) { - backup_section(post_data.sectionid, post_data.sectionnumber, post_data.courseid, data.userdata, data.anonymize); - } else { - backup(post_data.cmid, data.userdata, data.anonymize); - } - - shake_basket(); - } - }); - }); - } - - /** @var {Object} The icon configurations */ - const icon = { - // Actions - 'backup': { - css: 'editing_backup', - iconClass: 'fa fa-frown-o', - }, - 'movedir': { - css: 'editing_right', - iconClass: 'fa fa-arrow-right', - }, - 'move': { - css: 'editing_move_', - iconClass: 'fa fa-arrows-v', - }, - 'edit': { - css: 'editing_update', - iconClass: 'fa fa-pencil', - }, - 'cancel': { - css: 'editing_cancel', - iconClass: 'fa fa-ban', - }, - 'delete': { - css: 'editing_update', - iconClass: 'fa fa-trash', - }, - 'restore': { - css: 'editing_restore', - iconClass: 'fa fa-clone', - }, - // Directories - 'dir-open': { - iconClass: 'fa fa-folder-open-o' - }, - 'dir-closed': { - iconClass: 'fa fa-folder-o' - }, - }; - - /** @var {Node} The Sharing Cart block container node */ - const $block = $('.block_sharing_cart'); - - /** @var {Object} The current course */ - const course = new function () { - const body = $('body'); - this.id = body.attr('class').match(/course-(\d+)/)[1]; - this.is_frontpage = body.hasClass('pagelayout-frontpage'); - }(); - - /** - * Shows an error message with given Ajax error - * - * @param {Object} response The Ajax response - */ - function show_error(response) { - try { - const ex = JSON.parse(response.responseText); - new M.core.exception({ - name: str('pluginname') + ' - ' + str('error'), - message: ex.message - }); - } catch (e) { - new M.core.exception({ - name: str('pluginname') + ' - ' + str('error'), - message: response.responseText - }); - } - } - - /** - * Check special layout (theme boost) - * - * @return {Boolean} - */ - function verify_layout() { - const menuelement = $block.find('.menubar .dropdown .dropdown-menu'); - return (menuelement.length); - } - - /** - * Set Cookie - * @param name - * @param value - * @param expireTimeInMillisecond - */ - function setCookie(name, value, expireTimeInMillisecond) { - const d = new Date(); - d.setTime(d.getTime() + expireTimeInMillisecond); - const expires = 'expires=' + d.toUTCString(); - document.cookie = name + '=' + value + ';' + expires + ''; - } - - /** - * Get Cookie Value - * @param param - * @returns {*} - */ - function getCookieValue(param) { - const readCookie = document.cookie.match('(^|;)\\s*' + param + '\\s*=\\s*([^;]+)'); - return readCookie ? readCookie.pop() : ''; - } - - /** - * Create a command icon - * - * @param {String} name The command name, predefined in icon - * @param {String} [pix] The icon pix name to override - */ - function create_command(name) { - const iconElement = $('') - .attr('alt', str(name)) - .attr('class', icon[name].iconClass); - // If (verify_layout()) { - // iconElement.addClass('iconcustom'); - // } - - return $('
    ') - .addClass(icon[name].css) - .attr('title', str(name)) - .append(iconElement); - } - - /** - * Create a spinner - * @param $node - * @returns {*|jQuery} - */ - function add_spinner() { - const $spinner = ($('
    ')); - $('section.block_sharing_cart').append($spinner); - return $spinner; - } - - /** - * - * @param $node - * @returns {jQuery.fn.init} - */ - function add_node_spinner($node) { - const $node_spinner = $(''); - $node.append($node_spinner); - return $node_spinner; - } - - $(document).on('click', 'a.restore', function() { - add_spinner(); - }); - - /** - * - * Reload the Sharing Cart item tree - */ - function reload_tree() { - $.post(get_action_url("rest"), - { - "action": "render_tree", - "courseid": course.id - }, - function(response) { - $block.find(".tree").replaceWith($(response)); - $.init_item_tree(); - }, "html") - .fail(function(response) { - show_error(response); - }); - } - - /** - * Backup an activity - * - * @param {int} cmid - * @param {Boolean} userdata - * @param {Boolean} anonymize - */ - function backup(cmid, userdata, anonymize) { - let $commands = $('#module-' + cmid + ' .actions'); - if (!$commands.length) { - $commands = $('[data-owner="#module-' + cmid + '"]'); - } - - const $spinner = add_spinner(); - const $node_spinner = add_node_spinner($commands); - - $.post(get_action_url("rest"), - { - "action": "backup", - "cmid": cmid, - "userdata": userdata, - "anonymize": anonymize, - "sesskey": M.cfg.sesskey, - "courseid": course.id - }, - function() { - reload_tree(); - }) - .fail(function(response) { - show_error(response); - }) - .always(function() { - $node_spinner.hide(); - $spinner.hide(); - remove_basket(); - }); - } - - /** - * Backup an activities in a section - * - * @param {int} sectionId - * @param {int} sectionNumber - * @param {int} courseId - * @param {Boolean} userdata - * @param {Boolean} anonymize - */ - function backup_section(sectionId, sectionNumber, courseId, userdata, anonymize) { - const $commands = $('span.inplaceeditable[data-itemtype=sectionname][data-itemid=' + sectionId + ']'); - const $section = $commands.closest("li.section.main"); - let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim(); - - if (sectionName === null) { - sectionName = String($('#region-main .section_action_menu[data-sectionid=\'' + sectionId + '\']') - .parent().parent().find('h3.sectionname').text()); - } - - const inPlaceEditSectionName = in_place_edit_section_name($section); - sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName; - - const $spinner = add_spinner(); - const $node_spinner = add_node_spinner($commands); - - $.post(get_action_url("rest"), - { - "action": "backup_section", - "sectionid": sectionId, - "sectionnumber": sectionNumber, - "courseid": courseId, - "sectionname": sectionName, - "userdata": userdata, - "anonymize": anonymize, - "sesskey": M.cfg.sesskey - }, - function() { - reload_tree(); - }) - .fail(function(response) { - show_error(response); - }) - .always(function() { - $spinner.hide(); - $node_spinner.hide(); - remove_basket(); - }); - } - - - // /////// CLASSES ///////// - - /** - * @class Directory states manager - */ - const directories = new function () { - const KEY = 'block_sharing_cart-dirs'; - - let opens = getCookieValue(KEY).split(',').map(function (v) { - return parseInt(v); - }); - - function save() { - const expires = new Date(); - expires.setDate(expires.getDate() + 30); - setCookie(KEY, opens.join(','), expires); - } - - function open($dir, visible) { - const iconIndex = visible ? 'dir-open' : 'dir-closed'; - const iconElement = icon[iconIndex].iconClass; - $dir.find('> div i.icon').attr('class', 'icon ' + iconElement); - $dir.find('> ul.list')[visible ? 'show' : 'hide'](); - } - - function toggle(e) { - const $dir = $(e.target).closest('li.directory'); - const i = $dir.attr('id').match(/(\d+)$/)[1]; - const v = $dir.find('> ul.list').css('display') === 'none'; - - open($dir, v); - opens[i] = v ? 1 : 0; - save(); - } - - /** - * Initialize directory states - */ - this.init = function () { - let i = 0; - $block.find('li.directory').each(function (index, dir) { - const $dir = $(dir); - $dir.attr('id', 'block_sharing_cart-dir-' + i); - if (i >= opens.length) { - opens.push(0); - } else if (opens[i]) { - open($dir, true); - } - $dir.find('> div div.toggle-wrapper').css('cursor', 'pointer').on('click', function (e) { - toggle(e); - }); - i++; - }); - }; - - /** - * Reset directory states - */ - this.reset = function () { - opens = []; - this.init(); - save(); - }; - }(); - - /** - * @class Targets for moving an item directory - */ - const move_targets = new function () { - let $cancel = null, - targets = []; - - /** - * Hide move targets - */ - this.hide = function () { - if ($cancel !== null) { - const $commands = $cancel.closest('.commands'); - $cancel.remove(); - $cancel = null; - $commands.closest('li.activity').css('opacity', 1.0); - $commands.find('a').each(function () { - $(this).show(); - }); - $.each(targets, function (index, $target) { - $target.remove(); - }); - targets = []; - } - }; - - /** - * Show move targets for a given item - * - * @param {int} id The item ID - */ - this.show = function (item_id) { - this.hide(); - - function move(e) { - - const m = $(e.target).closest('a').attr('class').match(/move-(\d+)-to-(\d+)/); - const item_id = m[1], - area_to = m[2]; - - const $spinner = add_spinner(); - $.post(get_action_url("rest"), - { - "action": "move", - "item_id": item_id, - "area_to": area_to, - "sesskey": M.cfg.sesskey - }, - function () { - reload_tree(); - }) - .fail(function (response) { - show_error(response); - }) - .always(function () { - $spinner.hide(); - }); - } - - const $current = $block.find('#block_sharing_cart-item-' + item_id); - const $next = $current.next(); - const $list = $current.closest('ul'); - - let next_id = 0; - if ($next.length) { - next_id = $next.attr('id').match(/item-(\d+)$/)[1]; - } - - /** - * - * @param item_id - * @param area_to - * @returns {jQuery} - */ - function create_target(item_id, area_to) { - const $anchor = $('
    ') - .addClass('move-' + item_id + '-to-' + area_to) - .attr('title', str('movehere')) - .append( - $('

    ' + str('clicktomove') + '

    ') - .attr('alt', str('movehere')) - ); - - const $target = $('
  • ') - .append($anchor); - $anchor.on('click', function (e) { - move(e); - }); - - return $target; - } - - $list.find('> li.activity').each(function (index, item) { - const $item = $(item); - const to = $item.attr('id').match(/item-(\d+)$/)[1]; - if (to === item_id) { - $cancel = create_command('cancel', 't/left'); - $cancel.on('click', function () { - move_targets.hide(); - }); - const $commands = $item.find('.commands'); - $commands.find('a').each(function () { - $(this).hide(); - }); - $commands.append($cancel); - $item.css('opacity', 0.5); - } else if (to !== next_id) { - const $target = create_target(item_id, to); - $item.before($target); - targets.push($target); - } - }, this); - - if ($next) { - var $target = create_target(item_id, 0); - $list.append($target); - targets.push($target); - } - }; - }(); - - /** - * @class Targets for restoring an item - */ - const restore_targets = new function () { - this.is_directory = null; - let $clipboard = null, - targets = []; - - /** - * - * @param id - * @param section - * @returns {jQuery} - */ - - function create_target(id, section) { - const href = get_action_url('restore', { - 'directory': (restore_targets.is_directory === true), - 'target': id, - 'course': course.id, - 'section': section, - 'in_section': $('#copy-section-form').data('in-section'), - 'sesskey': M.cfg.sesskey, - 'returnurl': document.URL, - }); - - let $target = $('').attr('href', href).attr('title', str('copyhere')).append( - $('').attr('alt', str('copyhere')).attr('src', M.util.image_url('dropzone_arrow', 'block_sharing_cart')) - ); - - targets.push($target); - - return $target; - } - - /** - * Hide restore targets - */ - this.hide = function () { - if ($clipboard !== null) { - $clipboard.remove(); - $clipboard = null; - $.each(targets, function (index, $target) { - $target.remove(); - }); - targets = []; - } - }; - - /** - * - * - * @param {int} id The item ID - */ - this.show = function (id) { - this.hide(); - - let $view = $(""); - - if (this.is_directory) { - $view.html(id).css('display', 'inline'); - $view.prepend( - $("").addClass("icon") - .attr("alt", id) - // .attr("src", M.util.image_url(icon['dir-closed'].pix, null)) - ); - } else { - const $item = $block.find('#block_sharing_cart-item-' + id); - $view = $($item.find('div')[0].cloneNode(true)).css('display', 'inline'); - $view.attr('class', $view.attr('class').replace(/mod-indent-\d+/, '')); - $view.find('.commands').remove(); - } - - const $cancel = create_command('cancel'); - - $cancel.on('click', this.hide); - - $clipboard = $('
    '); - $clipboard.append(str('clipboard') + ": ").append($view).append($cancel); - - if (course.is_frontpage) { - const $sitetopic = $('.sitetopic'); - const $mainmenu = $('.block_site_main_menu'); - if ($sitetopic) { - $sitetopic.find('*').before($clipboard); - } else if ($mainmenu) { - $mainmenu.find('.content').before($clipboard); - } - - // Mainmenu = section #0, sitetopic = section #1 - if ($mainmenu) { - $mainmenu.find('.footer').before(create_target(id, 0)); - } - if ($sitetopic) { - $sitetopic.find('ul.section').append(create_target(id, 1)); - } - } else { - const $container = $('.course-content'); - $container.prepend($clipboard); - $container.find(M.course.format.get_section_wrapper(null)).each(function (index, sectionDOM) { - const $section = $(sectionDOM); - const section = $section.attr('id').match(/(\d+)$/)[1]; - $section.find('ul.section').first().append(create_target(id, section)); - }, this); - } - }; - }(); - - // /////// INITIALIZATION ///////// - - /** - * - * @returns {string|*} - */ - $.get_plugin_name = function() { - let $blockheader = $block.find("h2"); - - if (!$blockheader.length) { - $blockheader = $block.find("h3"); - - if ($blockheader.length) { - return $blockheader.html(); - } - } else { - return $blockheader.html(); - } - - return ""; - }; - - /** - * - * @param e - * @param activityName - * @param {int} cmId - */ - $.on_backup = function(e, activityName, cmId = 0) { - if (cmId === 0) { - cmId = (function ($backup) { - const $activity = $backup.closest('li.activity'); - if ($activity.length) { - return $activity.attr('id').match(/(\d+)$/)[1]; - } - const $commands = $backup.closest('.commands'); - const dataowner = $commands.attr('data-owner'); - if (dataowner.length) { - return dataowner.match(/(\d+)$/)[1]; - } - return $commands.find('a.editing_delete').attr('href').match(/delete=(\d+)/)[1]; - })($(e.target)); - } - - const data = - { - "action": "is_userdata_copyable", - "cmid": cmId - }; - - on_backup_modal(data, activityName, str('confirm_backup'), false); - }; - - /** - * On movedir command clicked - * - * @param {DOMEventFacade} e - */ - $.on_movedir = function(e) { - const $commands = $(e.target).closest('.commands'); - - const $current_dir = $commands.closest('li.directory'); - const current_path = $current_dir.length ? $current_dir.attr('directory-path') : '/'; - - const item_id = $(e.target).closest('li.activity').attr('id').match(/(\d+)$/)[1]; - - const dirs = []; - $block.find('li.directory').each(function() { - dirs.push($(this).attr('directory-path')); - }); - - const $form = $(''); - // eslint-disable-next-line no-script-url - $form.attr('action', 'javascript:void(0)'); - - function submit() { - const folder_to = $form.find('[name="to"]').val(); - const $spinner = add_spinner(); - $.post(get_action_url('rest'), - { - "action": "movedir", - "item_id": item_id, - "folder_to": folder_to, - "sesskey": M.cfg.sesskey - }, - function() { - reload_tree(); - directories.reset(); - }) - .fail(function(response) { - show_error(response); - }) - .always(function() { - $spinner.hide(); - }); - } - - $form.submit(submit); - - if (dirs.length === 0) { - var $input = $('').val(current_path); - setTimeout(function() { - $input.focus(); - }, 1); - $form.append($input); - } else { - dirs.unshift('/'); - - const $select = $('').val(current_path); - $select.remove(); - $edit.replaceWith($input); - $input.focus(); - }); - - $form.append($edit); - } - - const $cancel = create_command('cancel'); - $cancel.on('click', function() { - $form.remove(); - $commands.find('a').show(); - }); - $form.append($cancel); - - $commands.find('a').each(function() { - $(this).hide(); - }); - $commands.append($form); - }; - - /** - * On move command clicked - * - * @param {DOMEventFacade} e - */ - $.on_move = function(e) { - const $item = $(e.target).closest('li.activity'); - const id = $item.attr('id').match(/(\d+)$/)[1]; - - move_targets.show(id); - }; - - /** - * On delete command clicked - * - * @param {DOMEventFacade} e - */ - $.on_delete = function(e) { - const $item = $(e.target).closest('li'); - const liText = $item[0].innerText; - - let isDirectory = false; - let modalBody; - let item; - let description_text = ''; - - if ($item.hasClass("directory")) { - isDirectory = true; - item = str('folder_string'); - description_text = str('delete_folder'); - } else { - item = str('activity_string'); - } - - modalBody = '

    ' + item + ' ' + liText + description_text + '

    '; - - confirm_modal({ - 'title': str('confirm_delete'), - 'body': modalBody, - 'save_button': str('modal_confirm_delete'), - 'checkbox': false, - 'next': function() { - - let data = {}; - - if (isDirectory === true) { - data = { - "action": "delete_directory", - "path": $item.attr("directory-path"), - "sesskey": M.cfg.sesskey - }; - } else if ($item.hasClass("activity")) { - data = { - "action": "delete", - "id": $item.attr('id').match(/(\d+)$/)[1], - "sesskey": M.cfg.sesskey - }; - } - - const $spinner = add_spinner(); - - $.post(get_action_url("rest"), data, - function() { - reload_tree(); - }) - .fail(function(response) { - show_error(response); - }) - .always(function() { - $spinner.hide(); - }); - - e.stopPropagation(); - } - }); - }; - - /** - * On restore command clicked - * - * @param {DOMEventFacade} e - */ - $.on_restore = function(e) { - const $item = $(e.target).closest('li'); - let id = null; - - if ($item.hasClass("directory")) { - id = $item.attr("directory-path"); - restore_targets.is_directory = true; - } else if ($item.hasClass("activity")) { - id = $item.attr('id').match(/(\d+)$/)[1]; - restore_targets.is_directory = false; - } - - restore_targets.show(id); - }; - - /** - * On backup the whole section as a folder - * - * @param {int} sectionId - * @param {int} sectionNumber - * @param {int} courseId - * @param {string} sectionName - */ - $.on_section_backup = function(sectionId, sectionNumber, courseId, sectionName) { - const data = - { - "action": "is_userdata_copyable_section", - "sectionid": sectionId, - "sectionnumber": sectionNumber, - "courseid": courseId, - }; - - const body_html = '

    ' + str('backup_heavy_load_warning_message') + - '

    ' + str('confirm_backup_section'); - - on_backup_modal(data, sectionName, body_html, true); - }; - - /** - * Initialize the delete bulk - */ - $.init_bulk_delete = function(isspeciallayout) { - const bulkdelete = $block.find('.editing_bulkdelete'); - if (bulkdelete.length) { - if (isspeciallayout) { - bulkdelete.attr('role', 'menuitem').addClass('dropdown-item menu-action'); - bulkdelete.append($("").append(bulkdelete.attr('title'))); - - $block.find('.menubar .dropdown .dropdown-menu').append(bulkdelete); - } else { - $block.find('.header .commands').append(bulkdelete); - } - } - }; - - /** - * Initialize the help icon - */ - $.init_help_icon = function(isspeciallayout) { - const helpicon = $block.find('.header-commands > .help-icon'); - - if (isspeciallayout) { - $block.find('.header-commands').parent().css('display', 'block'); - } else { - $block.find('.header .commands').append(helpicon); - } - }; - - /** - * Initialize the Sharing Cart block header - */ - $.init_block_header = function() { - const isspeciallayout = verify_layout(); - $.init_bulk_delete(isspeciallayout); - $.init_help_icon(isspeciallayout); - }; - - /** - * Initialize the Sharing Cart item tree - */ - $.init_item_tree = function() { - function add_actions(item, actions) { - const $item = $(item); - const isCopying = $item.attr('data-is-copying') === '1'; - const $commands = $item.find('.commands').first(); - - $.each(actions, function(index, action) { - if (action === 'restore' && isCopying) { - return; - } - const $command = create_command(action); - $command.on('click', function(e) { - $['on_' + action](e); - }); - $commands.append($command); - }, this); - } - - const activity_actions = ['movedir', 'move', 'delete']; - if (course) { - activity_actions.push('restore'); - } - - const directory_actions = ['delete', 'restore']; - - // Initialize items - $block.find('li.activity').each(function(index, item) { - if($(item).attr('data-disable-copy') == 1) { - add_actions(item, ['movedir', 'move', 'delete']); - return; - } - add_actions(item, activity_actions); - }); - - // Initialize directory items - $block.find('li.directory').each(function(index, item) { - add_actions(item, directory_actions); - }); - - // Initialize directories - directories.init(); - }; - - /** - * Extract html object from area where moodle ajax was called. - * - * Call add_activity_backup_control to re append sharing cart icon. - */ - $.init_activity_commands = function() { - $(document).ajaxComplete(function(event, xhr, settings) { - - const url = settings.url; - const lastslashindex = url.lastIndexOf('='); - const result = url.substring(lastslashindex + 1); - - if (result === 'core_course_edit_module' || result === 'core_course_get_module') { - - const data = JSON.parse(settings.data); - const action = data[0].args.action; - - // Don't try to add icon if activity has been deleted. - if (action === 'delete') { - return; - } - - setTimeout(function() { - const activity_id = data[0].args.id; - const activity = $('#module-' + activity_id); - add_activity_backup_control(activity); - - if (action === 'duplicate') { - const duplicated = activity.next(); - add_activity_backup_control(duplicated); - } - }, 1); - } - }); - - /** - * Create the backup icon - * - * @returns $backupIcon - */ - function create_backup_icon() { - - const $backupIcon = $('
    ') - .append($('')) - .attr('title', str('backup')); - - if (addMethod !== 'click_to_add') { - $backupIcon.addClass('d-none'); - } - - return $backupIcon; - } - - /** - * Add backup control with a click event to an activity - * Added fix for copying an activity without backup routine - * - * @param $activity - */ - function add_activity_backup_control($activity) { - - const activityClass = $activity[0].className; - - // Selecting modtype without prefix. - const modtype = activityClass.substr(activityClass.indexOf('modtype_') + 8); - - // Default activity name. - let activityName = str('activity_string'); - - // Label is using a different html / css layout, so it's needed to get the name by using another $find. - if (modtype !== 'label') { - activityName = $('.activity#' + $activity[0].id) - .find('.mod-indent-outer .activityinstance span.instancename') - .html(); - } - - const $backupIcon = create_backup_icon(); - - $backupIcon.on('click', function(e) { - $.on_backup(e, activityName); - }); - - const $actionMenuItem = $activity.find('.action-menu.section-cm-edit-actions').parent('.actions'); - - if (!$actionMenuItem.find('.add-to-sharing-cart').length) { - $actionMenuItem.append($backupIcon); - } - } - - /** - * Add backup control with a click event to a section - * - * @param $section - */ - function add_section_backup_control($section) { - - let sectionId = $section.find('.section_action_menu').data('sectionid'); - const sectionNumber = parseInt(String($section.attr('id')).match(/\d+/)[0]); - let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim(); - - const isFlexibleCourseFormat = $('body[id$=flexsections]').length; - - // Extract the section ID from the section if this is a Flexible - // course format (since this format doesn't have an action menu) - if (isFlexibleCourseFormat && (typeof sectionId === 'undefined' || sectionId === null)) { - sectionId = $section.data('section-id'); - } - - // A bit unsafe to extract the course ID from the body but it's the best option we got at the moment - const courseId = parseInt(String($('body').attr('class')).match(/course-([0-9]*)( |$)/)[1]); - - const $backupIcon = create_backup_icon(); - - $backupIcon.on('click', function() { - const inPlaceEditSectionName = in_place_edit_section_name($section); - sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName; - $.on_section_backup(sectionId, sectionNumber, courseId, sectionName); - }); - - let $sectionTitle = $section.find('h3.sectionname').first().find('a').last(); - - const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable').first(); - if ($inPlaceEditable.length) { - $sectionTitle = $inPlaceEditable; - } - - // Add the backup icon after the cog wheel if this is a Flexible course format - if (isFlexibleCourseFormat && sectionNumber === 0) { - $sectionTitle = $section.find('> .controls'); - $sectionTitle.prepend($backupIcon); - } else { - $backupIcon.insertAfter($sectionTitle); - } - - const activitySelector = 'li.activity'; - - const $activities = $section.find(activitySelector); - - $($activities).each(function() { - add_activity_backup_control($(this)); - }); - } - - $("body.editing .course-content li.section").each(function() { - add_section_backup_control($(this)); - }); - }; - - /** - * Initialize the Sharing Cart footer basket for 4.0+. - */ - function init_footer_basket() { - let currentDragging; - const activities = document.querySelectorAll(".activity.activity-wrapper"); - const sections = document.querySelectorAll(".course-section-header"); - const sharingCartBlock = document.querySelector('section[data-block="sharing_cart"]'); - - add_draggable_to_first_section(); - - const footer = document.getElementById('page-footer'); - const footerIconContainer = footer.querySelector('div[data-region="footer-container-popover"]'); - - const basket = document.createElement('i'); - basket.setAttribute('class', 'fa fa-shopping-basket'); - - const basketButton = document.createElement('button'); - basketButton.setAttribute('class', 'btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket'); - basketButton.setAttribute('style', 'z-index: 1001;'); - basketButton.append(basket); - - const dropAreaText = document.createElement('p'); - dropAreaText.setAttribute('class', 'font-weight-bold text-white'); - dropAreaText.innerText = str('drop_here'); - - const dropArea = document.createElement('div'); - dropArea.setAttribute('class', - 'h-100 w-100 position-absolute d-flex justify-content-center align-items-center'); - dropArea.append(dropAreaText); - - sections.forEach(section => { - drag_event_listeners(section); - }); - - activities.forEach(activity => { - drag_event_listeners(activity); - }); - - /** - * Initialize events for dragging - * @param {object} draggable - */ - function drag_event_listeners(draggable) { - draggable.addEventListener('dragstart', (e) => { - basketButton.classList.remove('shake_basket'); - - footerIconContainer?.prepend(basketButton); - sharingCartBlock.children[0].classList.add('dragging_item'); - sharingCartBlock.append(dropArea); - currentDragging = e.target; - isDragging = true; - }); - - draggable.addEventListener('dragend', () => { - if (currentDragging instanceof HTMLElement) { - footerIconContainer?.removeChild(basketButton); - } - - sharingCartBlock.children[0].classList.remove('dragging_item'); - sharingCartBlock.removeChild(dropArea); - isDragging = false; - }); - } - - [basketButton, sharingCartBlock].forEach((dropzone) => { - dropzone.addEventListener("dragover", (e) => { - e.preventDefault(); - dropzone.classList.add('drag_over'); - }); - - dropzone.addEventListener("dragenter", (e) => { - e.preventDefault(); - dropzone.classList.add('drag_over'); - }); - - dropzone.addEventListener("dragleave", () => { - dropzone.classList.remove('drag_over'); - }); - - dropzone.addEventListener("drop", () => { - if (currentDragging instanceof HTMLElement) { - currentDragging.querySelector('.add-to-sharing-cart').click(); - } - - dropzone.classList.remove('drag_over'); - currentDragging = undefined; - isDragging = false; - }); - }); - } - - /** - * Make the first section (General) draggable - */ - function add_draggable_to_first_section() { - const courseSectionHeader = document.getElementsByClassName("course-section-header")[0] ?? null; - - if (courseSectionHeader instanceof HTMLElement) { - courseSectionHeader.classList.add('draggable'); - courseSectionHeader.setAttribute('draggable', true); - } - } - - /** - * Initialize the Sharing Cart block - */ - $.init = function() { - M.str.block_sharing_cart.pluginname = this.get_plugin_name(); - - // Arrange header icons (bulkdelete, help) - $.init_block_header(); - $.init_item_tree(); - $.init_activity_commands(); - - if (addMethod === 'drag_and_drop') { - init_footer_basket(); - } - }; - var $spinner = $('').addClass('spinner fa fa-3x fa-circle-o-notch fa-spin'); - $('div#sharing-cart-spinner-modal div.spinner-container').prepend($spinner); - - $.init(); - }); - - $('.copy_section').on('click', function() { - - const $section_selected = ($('.section-dropdown option:selected')); - const sectionId = $section_selected.data('section-id'); - const sectionNumber = $section_selected.data('section-number'); - const courseId = $section_selected.data('course-id'); - const sectionName = $section_selected.data('section-name'); - - $.on_section_backup(sectionId, sectionNumber, courseId, sectionName); - }); - - $('.copy_activity').on('click', function(e) { - const activitySelected = ($('.activity-dropdown option:selected')); - const activityId = activitySelected.data('activity-id'); - const activityName = activitySelected.data('activity-name'); - - $.on_backup(e, activityName, activityId); - }); -}; diff --git a/backup/moodle2/restore_fix_missing_questions.php b/backup/moodle2/restore_fix_missing_questions.php deleted file mode 100644 index f37ac38..0000000 --- a/backup/moodle2/restore_fix_missing_questions.php +++ /dev/null @@ -1,171 +0,0 @@ -. - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -/** - * The execution step that fixes missing questions - * - * This step must be inserted between restore_process_categories_and_questions - * and restore_create_categories_and_questions of restore_root_task - */ -class restore_fix_missing_questions extends restore_execution_step { - /** - * Checks if mapped questions are exact valid, or marks them to be created - * - * @throws moodle_exception - * @global $DB - */ - protected function define_execution() { - global $DB; - - $restoreid = $this->get_restoreid(); - $courseid = $this->get_courseid(); - $userid = $this->task->get_userid(); - - $workaround_qtypes = explode(',', get_config('block_sharing_cart', 'workaround_qtypes')); - - // @see /backup/util/dbops/restore_dbops.class.php#prechek_precheck_qbanks_by_level - $contexts = restore_dbops::restore_get_question_banks($restoreid); - foreach ($contexts as $contextid => $contextlevel) { - $categories = restore_dbops::restore_get_question_categories($restoreid, $contextid, $contextlevel); - $canadd = false; - if ($targetcontext = restore_dbops::restore_find_best_target_context($categories, $courseid, $contextlevel)) { - $canadd = has_capability('moodle/question:add', $targetcontext, $userid); - } - foreach ($categories as $category) { - $questions = restore_dbops::restore_get_questions($restoreid, $category->id); - foreach ($questions as $question) { - if (!in_array($question->qtype, $workaround_qtypes)) { - continue; - } - $mapping = restore_dbops::get_backup_ids_record($restoreid, 'question', $question->id); - if ($mapping && $mapping->newitemid && - !self::is_question_valid($question->qtype, $mapping->newitemid)) { - if (!$canadd) { - throw new moodle_exception('questioncannotberestored', 'backup', '', $question); - } - $catmapping = restore_dbops::get_backup_ids_record($restoreid, 'question_category', $category->id); - $matchquestions = $DB->get_records('question', array( - 'category' => $catmapping->newitemid, - 'qtype' => $question->qtype, - 'stamp' => $question->stamp, - 'version' => $question->version - )); - $newitemid = 0; // to be created if no valid duplicate exists - foreach ($matchquestions as $q) { - if ($q->id == $mapping->newitemid) { - continue; - } - if (self::is_question_valid($question->qtype, $q->id)) { - $newitemid = $q->id; // updates mapping if a valid one found - break; - } - } - $this->update_mapping($mapping, $newitemid); - } - } - } - } - } - - /** - * Updates existing mapping - * - * @param object $record - * @param int $newitemid - */ - private function update_mapping($record, $newitemid) { - $restoreid = $this->get_restoreid(); - $key = "{$record->itemid} {$record->itemname} {$restoreid}"; - $extrarecord = array('newitemid' => $newitemid); - - // restore_dbops::update_backup_cached_record($record, $extrarecord, $key, $existingrecord = null); - $reflector = new ReflectionMethod('restore_dbops', 'update_backup_cached_record'); - $reflector->setAccessible(true); - $reflector->invoke(null, $record, $extrarecord, $key, $record); - } - - /** - * Checks if a question is valid - * - * @param string $qtypename - * @param int $questionid - * @return boolean - * @global $DB - */ - private static function is_question_valid($qtypename, $questionid) { - global $DB; - - // checks if the question exists by question_type->get_question_options() - $question = (object) array('id' => $questionid); - try { - // qtype_multianswer expects that options property is an object instead of undefined - $question->options = new stdClass; - $oldhandler = set_error_handler(function($n, $s, $f, $l) { - return true; - }); - question_bank::get_qtype($qtypename)->get_question_options($question); - isset($oldhandler) && set_error_handler($oldhandler); - if (count(get_object_vars($question->options)) == 0) { - if ($qtypename === 'random') { - // qtype_random does nothing, but is valid - } else { - return false; - } - } - } catch (moodle_exception $ex) { - isset($oldhandler) && set_error_handler($oldhandler); - return false; - } - // somehow, subquestions might go away, but inconsistency of them causes restore interruption - // @see /question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php#process_match - if (property_exists($question->options, 'subquestions')) { - if (empty($question->options->subquestions)) { - return false; - } - // so, let's check deep -- is there any faster way?? - $dbman = $DB->get_manager(); - if ($dbman->table_exists("question_{$qtypename}") && - $dbman->field_exists("question_{$qtypename}", 'question') && - $dbman->table_exists("question_{$qtypename}_sub") && - $dbman->field_exists("question_{$qtypename}_sub", 'question')) { - // checks if all the subquestions exist - $q = $DB->get_record("question_{$qtypename}", array('question' => $question->id)); - if (!$q || empty($q->subquestions)) { - return false; - } - $subquestionids = explode(',', $q->subquestions); - list ($sql, $params) = $DB->get_in_or_equal($subquestionids); - $sql .= ' AND question = ?'; - $params[] = $question->id; - $count = $DB->get_field_select("question_{$qtypename}_sub", 'COUNT(*)', "id $sql", $params); - if ($count != count($subquestionids)) { - return false; - } - } - } - return true; - } -} diff --git a/backup/moodle2/restore_root_task_fix_missings.php b/backup/moodle2/restore_root_task_fix_missings.php deleted file mode 100644 index 5cfc898..0000000 --- a/backup/moodle2/restore_root_task_fix_missings.php +++ /dev/null @@ -1,47 +0,0 @@ -. - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once __DIR__ . '/restore_fix_missing_questions.php'; - -/** - * The root task that fixes missings before execution - */ -class restore_root_task_fix_missings extends restore_root_task { - public function build() { - parent::build(); - - // inserts a restore_fix_missing_questions step - // before restore_create_categories_and_questions - $fix_missing_questions = new restore_fix_missing_questions('fix_missing_questions'); - $fix_missing_questions->set_task($this); - foreach ($this->steps as $i => $step) { - if ($step instanceof restore_create_categories_and_questions) { - array_splice($this->steps, $i, 0, array($fix_missing_questions)); - break; - } - } - } -} diff --git a/backup/util/helper/restore_fix_missings_helper.php b/backup/util/helper/restore_fix_missings_helper.php deleted file mode 100644 index 856e67e..0000000 --- a/backup/util/helper/restore_fix_missings_helper.php +++ /dev/null @@ -1,67 +0,0 @@ -. - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once __DIR__ . '/../../moodle2/restore_root_task_fix_missings.php'; - -/** - * The helper class that fixes restore plan - */ -final class restore_fix_missings_helper { - /** - * Fixes a restore plan to perform a workaround for question bank restore issue - * - * @param restore_plan $plan - */ - public static function fix_plan(restore_plan $plan) { - // replaces an existing restore_root_task with a restore_root_task_fix_missings - $tasks = $plan->get_tasks(); - foreach ($tasks as $i => $task) { - if ($task instanceof restore_root_task) { - $task = new restore_root_task_fix_missings('root_task'); - // since the task settings already defined by restore_root_task, - // we need to inject the plan instead of calling set_plan(), - // to avoid 'error/multiple_settings_by_name_found' error - self::set_protected_property($task, 'plan', $plan); - $tasks[$i] = $task; - break; - } - } - self::set_protected_property($plan, 'tasks', $tasks); - } - - /** - * Sets a protected/private property - * - * @param object $obj - * @param string $prop - * @param mixed $value - */ - private static function set_protected_property($obj, $prop, $value) { - $reflector = new ReflectionProperty(get_class($obj), $prop); - $reflector->setAccessible(true); - $reflector->setValue($obj, $value); - } -} diff --git a/block_sharing_cart.php b/block_sharing_cart.php index 876a8f4..e6878b9 100644 --- a/block_sharing_cart.php +++ b/block_sharing_cart.php @@ -1,329 +1,45 @@ . - -/** - * Sharing Cart block - * - * @package block_sharing_cart - * @copyright 2023 (c) Don Hinkelman, moxis and others - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use block_sharing_cart\controller; -use block_sharing_cart\repositories\task_repository; -use block_sharing_cart\section; -use block_sharing_cart\module; +// @codeCoverageIgnoreStart defined('MOODLE_INTERNAL') || die(); +// @codeCoverageIgnoreEnd -/** - * PTODO / TODO: See list below - * - Namespaces are wrong (sharing_cart => block_sharing_cart) for autoload - * - JS must be AMD modules - * - Remove require statements and use autoload - * - Create new branches and implement mustache / renderers (some already exists) - * - Fix version variable in contructor - * - Move all exceptions to classes/exceptions and create descriptive classes - * - Implement Core/Str to load language strings - * - * Class block_sharing_cart - */ class block_sharing_cart extends block_base { public function init(): void { - $this->title = get_string('pluginname', __CLASS__); + $this->title = get_string('pluginname', 'block_sharing_cart'); } public function applicable_formats(): array { - return array( + return [ 'all' => false, 'course' => true, 'site' => true - ); + ]; } public function has_config(): bool { return true; } - /** - * Get the block content - * - * @return object|string - * - * @throws coding_exception - * @throws dml_exception|moodle_exception - * @global object $USER - */ - public function get_content() { - global $USER, $COURSE, $PAGE; + public function get_content(): object|string { + global $OUTPUT, $USER; - $section_id = optional_param('sectionid', null, PARAM_INT); - $section_section = optional_param('section', null, PARAM_INT); + $base_factory = \block_sharing_cart\app\factory::make(); - $context = context_course::instance($this->page->course->id); + $this->page->requires->css('/blocks/sharing_cart/style/style.css'); if ($this->content !== null) { return $this->content; } - if (!$this->page->user_is_editing() || !has_capability('moodle/backup:backupactivity', $context)) { + if (!$this->page->user_is_editing() || !has_capability('moodle/backup:backupactivity', $this->context)) { return $this->content = ''; } - $controller = new controller(); - $html = $controller->render_tree($USER->id); - - $controller->delete_unused_sections($this->page->course->id); - - // Fetching all sections for current course. - $sectionsHandler = new section(); - $sections = $sectionsHandler->all($COURSE->id); - - $moduleHandler = new module(); - $activities = $moduleHandler->get_all_from_course($COURSE->id); - - /* Place the
    '; $string['sharing_cart:addinstance'] = 'Add a new Sharing Cart block'; - +/* $string['backup'] = 'Copy to Sharing Cart'; $string['restore'] = 'Copy to course'; $string['movedir'] = 'Move into folder'; @@ -68,7 +46,7 @@ $string['uninstalled_plugin_warning_title'] = 'This plugin is uninstalled. Trying to recover this without reinstalling the plugin: {$a} will cause errors. Restoration is disabled'; $string['drop_here'] = 'Drop here..'; $string['async_restore_in_progress'] = 'Restoring sharing item "{$a->modtext}" to the section number {$a->section}'; - +*/ $string['settings:backup_mode'] = 'Backup mode'; $string['settings:backup_mode_desc'] = 'Choose between immediate or asynchronous backup process'; $string['settings:restore_mode'] = 'Restore mode'; @@ -97,6 +75,7 @@ $string['settings:show_copy_activity_in_block'] = 'Show the "Copy activity" in block'; $string['settings:show_copy_activity_in_block_desc'] = 'Show the "Copy activity" in the sharing cart block, underneath all modules/activities - This is only available if the user has the capability to backup activities, but not the capability to manage/move activities'; +/* $string['invalidoperation'] = 'An invalid operation detected'; $string['unexpectederror'] = 'An unexpected error occurred'; $string['recordnotfound'] = 'Shared item not found'; @@ -130,16 +109,4 @@ $string['modal_bulkdelete_title'] = 'Sure you want to delete'; $string['modal_bulkdelete_confirm'] = 'Delete selected'; - -$string['privacy:metadata:block_sharing_cart'] = 'Sharing cart data is stored here'; -$string['privacy:metadata:block_sharing_cart:userid'] = 'The ID of user'; -$string['privacy:metadata:block_sharing_cart:modname'] = 'The name of activity module'; -$string['privacy:metadata:block_sharing_cart:modicon'] = 'Activity module icon'; -$string['privacy:metadata:block_sharing_cart:modtext'] = 'The title of the activity module'; -$string['privacy:metadata:block_sharing_cart:ctime'] = 'Created time'; -$string['privacy:metadata:block_sharing_cart:tree'] = 'The title of sharing cart folder that display in the block'; -$string['privacy:metadata:block_sharing_cart:weight'] = 'Order of items, sorting in ascending order'; -$string['privacy:metadata:block_sharing_cart_plugins'] = 'Sharing cart plugins data is stored here'; -$string['privacy:metadata:block_sharing_cart_plugins:plugin'] = 'The name of the plugin'; -$string['privacy:metadata:block_sharing_cart_plugins:userid'] = 'The ID of user'; -$string['privacy:metadata:block_sharing_cart_plugins:data'] = 'Sharing cart plugins data'; +*/ \ No newline at end of file diff --git a/lang/es/block_sharing_cart.php b/lang/es/block_sharing_cart.php deleted file mode 100644 index 9daed59..0000000 --- a/lang/es/block_sharing_cart.php +++ /dev/null @@ -1,90 +0,0 @@ -. - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['pluginname'] = 'Bolsa de recursos'; -$string['sharing_cart'] = 'Bolsa de recursos'; -$string['sharing_cart_help'] = -'
    - Copiando del curso a bolsa de recursos -

    Aparecerá un pequeño icono de copia de bolsa de recurso al final de cada - recurso o actividad dentro de un curso. - Clique ese icono para enviar una copia del recurso o actividad a la bolsa de recursos. - Solo se clonará la actividad sin datos de usuario.

    - Copiando desde bolsa de recusos al curso -

    Clique el icono de "copiar a un curso" y seleccione el destino de cada sección. - O clique "cancelar" que esta junto a ese icono.

    - Creando carpetas dentro de bolsa de recursos -

    Clique el icono "mover dentro de una carpeta". - Un elemento de escritura aparecerá si no hay carpeta. - O puede seleccionar una carpeta existente en la lista desplegable. - Se remplazará con un elemento de entrada si clica el icono de "editar"

    -
    '; -$string['sharing_cart:addinstance'] = 'Añadir un nuevo bloque bolsa de recursos'; - -$string['backup'] = 'Copiar a la bolsa de recursos'; -$string['restore'] = 'Copiar al curso'; -$string['movedir'] = 'Mover al curso'; -$string['copyhere'] = 'Copiar aquí'; -$string['notarget'] = 'Destino no encontrado'; -$string['clipboard'] = 'Copiar este item compartido'; -$string['bulkdelete'] = 'Borrado masivo'; -$string['confirm_backup'] = '¿Está seguro de copiar esta/e actividad/recurso a la bolsa de recursos?'; -$string['confirm_backup_section'] = '¿Está seguro de copiar estas/os actividades/recursos a la bolsa de recursos?'; -$string['confirm_userdata'] = '¿Quiere incluir datos de usuarios en la copia de esta/e actividad/recurso? -OK - Copiar *CON* datos de usuarios -Cancelar - Copiar *SIN* datos de usuarios'; -$string['confirm_restore'] = '¿Está seguro de copiar este item al curso?'; -$string['confirm_delete'] = '¿Está seguro de borrar?'; -$string['confirm_delete_selected'] = '¿Está seguro de querer borrar todos los elementos seleccionados?'; -$string['inprogess_pleasewait'] = 'Espere un momento…'; - -$string['settings:userdata_copyable_modtypes'] = 'Tipos de módulos que puede ser copiados'; -$string['settings:userdata_copyable_modtypes_desc'] = 'Mientras se copia una actividad en la bolsa de recursos, -un diálogo muestra si la opción de la copia de la actividad incluye sus datos de usuarios o no, -y si el tipo de módulo seleccionado anteriormente y el usuario tienen las capacidades: moodle/backup:userinfo, -moodle/backup:anonymise y moodle/restore:userinfo. -(Por defecto, solo el rol de administrador tiene esas capacidades.)'; -$string['settings:workaround_qtypes'] = 'Solución para tipos de preguntas'; -$string['settings:workaround_qtypes_desc'] = 'La solución para la restauración de preguntas deberá ser actualizada si el tipo de pregunta está activo. -Cuando las preguntas a restaurar ya existan, sin embargo, esos datos apareceran como inconsistentes. El remedio intentará crear duplicados en lugar de reclicar los datos existentes. -Será útil para evitar errores de restauración del tipo error_question_match_sub_missing_in_db.'; - -$string['invalidoperation'] = 'Se detectón una operación no válida'; -$string['unexpectederror'] = 'Ocurrió un error no esperado'; -$string['recordnotfound'] = 'Elemento compartido no encontrado'; -$string['forbidden'] = 'Usted no tene permisos para acceder a este elemento compartido'; -$string['requirejs'] = 'Recursos compartidos requiere habilitar JavaScript en su navegador'; -$string['requireajax'] = 'Recursos comparridos requiere AJAX'; - -$string['variouscourse'] = 'desde cursos varios'; - -$string['section_name_conflict'] = 'Conflicto de la sección'; -$string['conflict_description'] = '¿Quiere cambiar el título y la configuración de la sección en el curso?'; -$string['conflict_description_note'] = - '*Los formatos de la descripción del título (colores, imágenes, etc.) y la configuración se aparecerán después de copiar al curso.'; -$string['restore_heavy_load_warning_message'] = 'Load time are longer, because more than 10 activities/resources are being processed. (Translation missing)'; -$string['backup_heavy_load_warning_message'] = 'If section contains several activites, processing time will be longer. (Translation missing)'; -$string['conflict_no_overwrite'] = 'Mantener utilizando el título y la configuración de "{$a}"'; -$string['conflict_overwrite_title'] = 'Cambiar el título y la configuración a "{$a}"'; -$string['conflict_submit'] = 'Continuar'; diff --git a/lang/ja/block_sharing_cart.php b/lang/ja/block_sharing_cart.php deleted file mode 100644 index 6a001d9..0000000 --- a/lang/ja/block_sharing_cart.php +++ /dev/null @@ -1,89 +0,0 @@ -. - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['pluginname'] = '共有カート'; -$string['sharing_cart'] = '共有カート'; -$string['sharing_cart_help'] = -'
    - 共有カートへコピー -

    コースを編集モードに切り替えると、コース上の各コンテンツの操作アイコンの右に - 「共有カートへコピー」アイコンが追加されます。

    - コースへコピー -

    共有カート内のアイテム操作アイコンの「コースへコピー」をクリックすると、 - コースの各セクションに「ここへコピー」マーカーが現れるので、 - いずれかを選択してコピーを完了するか、上部の「キャンセル」をクリックします。

    - 共有カート内にフォルダを作成 -

    共有カート内で「フォルダ移動」アイコンをクリックすると、 - 既存のフォルダのリストが表示されるので、その中から移動先を選択するか、 - リスト右の「編集」アイコンをクリックして入力ボックスを表示させ、 - そこに移動先フォルダ名を入力します。

    -
    '; -$string['sharing_cart:addinstance'] = '新しい共有カートブロックを追加する'; - -$string['backup'] = '共有カートへコピー'; -$string['restore'] = 'コースへコピー'; -$string['movedir'] = 'フォルダ移動'; -$string['copyhere'] = 'ここにコピー'; -$string['notarget'] = 'ターゲットが見つかりません'; -$string['clipboard'] = 'この共有アイテムをコピーする'; -$string['bulkdelete'] = '一括削除'; -$string['confirm_backup'] = '共有カートにコピーしますか?'; -$string['confirm_backup_section'] = '共有カートにコピーしますか?'; -$string['confirm_userdata'] = '共有カートへのコピーにユーザーデータを含めますか? -OK:ユーザーデーターを含めて、コピーする -キャンセル:ユーザーデーターを含めずに、コピーする'; -$string['confirm_restore'] = 'コースにコピーしますか?'; -$string['confirm_delete'] = '削除してよろしいですか?'; -$string['confirm_delete_selected'] = '選択したアイテムを全て削除してもよろしいですか?'; -$string['inprogess_pleasewait'] = 'しばらくお待ち下さい…'; - -$string['settings:userdata_copyable_modtypes'] = 'ユーザーデータをコピー可能なモジュールタイプ'; -$string['settings:userdata_copyable_modtypes_desc'] = '共有カートへコピーする際、コピーしようとしているモジュールがここでチェックを付けたモジュールタイプで、 -かつ、操作しているユーザーが moodle/backup:userinfo, -moodle/backup:anonymise, moodle/restore:userinfo ケイパビリティを持っていれば、 -そのモジュールに付随するユーザーデータをコピーに含めるかどうかを選択するダイアログを表示します。
    -(既定では「マネージャ」ロールのみがこれらのケイパビリティを持ちます。)'; -$string['settings:workaround_qtypes'] = 'リストア不具合対策を行う問題タイプ'; -$string['settings:workaround_qtypes_desc'] = 'チェックを付けた問題タイプに対して、リストア不具合対策を行います。 -これを有効にすると、リストアしようとしている問題と全く同じ問題が既に存在していて、 -しかしながらそのデータに破損が見つかった場合、既存データの再利用を避け、 -その問題を再度リストアするように試みます。この対策は、error_question_match_sub_missing_in_db などのエラー回避に有用です。'; - -$string['invalidoperation'] = '無効な操作です'; -$string['unexpectederror'] = '予期しないエラーが発生しました'; -$string['recordnotfound'] = '共有アイテムが見つかりません'; -$string['forbidden'] = 'この共有アイテムにアクセスする権限がありません'; -$string['requirejs'] = 'ブラウザの JavaScript を有効にしてください'; -$string['requireajax'] = 'AJAX が有効になっていません'; - -$string['variouscourse'] = '複数のコースから'; - -$string['section_name_conflict'] = '上書きするトピックの選択'; -$string['conflict_description'] = 'コースのトピックを上書きしますか?'; -$string['conflict_description_note'] = '※トピックの説明のフォーマット(テキストの色、画像など)と設定がコースコピー後に表現する。'; -$string['restore_heavy_load_warning_message'] = 'Load time are longer, because more than 10 activities/resources are being processed. (Translation missing)'; -$string['backup_heavy_load_warning_message'] = 'If section contains several activites, processing time will be longer. (Translation missing)'; -$string['conflict_no_overwrite'] = 'トピック名と設定を変更しない。「{$a}」のままにする。'; -$string['conflict_overwrite_title'] = 'タイトルと設定を「{$a}」に変更する。'; -$string['conflict_submit'] = '続く'; diff --git a/lib.php b/lib.php index 3d14d4a..bf20193 100644 --- a/lib.php +++ b/lib.php @@ -1,35 +1,5 @@ . -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -/** - * Remove sharing cart entity, when related file was removed from the system - * @param object $file file record - * @throws dml_exception - */ -function block_sharing_cart_after_file_deleted($file) { - global $DB; - - $cleaner = new \block_sharing_cart\files\cleaner($DB, $file); - $cleaner->remove_related_sharing_cart_entity(); +function block_sharing_cart_after_file_deleted($file): void { + // TODO: Implement block_sharing_cart_after_file_deleted } diff --git a/lib/settingslib.php b/lib/settingslib.php deleted file mode 100644 index 02c2ad3..0000000 --- a/lib/settingslib.php +++ /dev/null @@ -1,173 +0,0 @@ -. - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die; - -require_once __DIR__ . '/../../../question/engine/bank.php'; - -/** - * Multiple checkboxes with icons for each label - */ -class admin_setting_configmulticheckboxwithicon extends admin_setting_configmulticheckbox { - /** @var array Array of icons value=>icon */ - protected $icons; - - /** - * Constructor: uses parent::__construct - * - * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in - * config_plugins. - * @param string $visiblename localised - * @param string $description long localised info - * @param array $defaultsetting array of selected - * @param array $choices array of $value=>$label for each checkbox - * @param array $icons array of $value=>$icon for each checkbox - */ - public function __construct($name, $visiblename, $description, $defaultsetting, array $choices, array $icons) { - $this->icons = $icons; - parent::__construct($name, $visiblename, $description, $defaultsetting, $choices); - } - - /** - * Returns XHTML field(s) as required by choices - * - * Relies on data being an array should data ever be another valid vartype with - * acceptable value this may cause a warning/error - * if (!is_array($data)) would fix the problem - * - * @param array $data An array of checked values - * @param string $query - * @return string XHTML field - * @todo Add vartype handling to ensure $data is an array - * - */ - public function output_html($data, $query = '') { - if (!$this->load_choices() or empty($this->choices)) { - return ''; - } - $default = $this->get_defaultsetting(); - if (is_null($default)) { - $default = array(); - } - if (is_null($data)) { - $data = array(); - } - $options = array(); - $defaults = array(); - foreach ($this->choices as $key => $description) { - if (!empty($data[$key])) { - $checked = 'checked="checked"'; - } else { - $checked = ''; - } - if (!empty($default[$key])) { - $defaults[] = $description; - } - - // $options[] = '' - // .''; - $options[] = '' - . ''; - } - - if (is_null($default)) { - $defaultinfo = null; - } else if (!empty($defaults)) { - $defaultinfo = implode(', ', $defaults); - } else { - $defaultinfo = get_string('none'); - } - - $return = '
    '; - $return .= ''; // something must be submitted even if nothing selected - if ($options) { - $return .= '
      '; - foreach ($options as $option) { - $return .= '
    • ' . $option . '
    • '; - } - $return .= '
    '; - } - $return .= '
    '; - - return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query); - } -} - -/** - * Multiple checkboxes for module types - */ -class admin_setting_configmulticheckboxmodtypes extends admin_setting_configmulticheckboxwithicon { - /** - * @param string $name - * @param string $visiblename - * @param string $description - * @param array $defaultsetting - * @global moodle_database $DB - * @global core_renderer $OUTPUT - */ - public function __construct($name, $visiblename, $description, $defaultsetting = null) { - global $DB, $OUTPUT; - $choices = array(); - $icons = array(); - foreach ($DB->get_records('modules', array(), 'name ASC') as $module) { - $choices[$module->name] = get_string('modulename', $module->name); - $icons[$module->name] = ' ' . $OUTPUT->pix_icon('icon', '', $module->name, array('class' => 'icon')); - } - parent::__construct($name, $visiblename, $description, $defaultsetting, $choices, $icons); - } -} - -/** - * Multiple checkboxes for question types - */ -class admin_setting_configmulticheckboxqtypes extends admin_setting_configmulticheckboxwithicon { - /** - * @param string $name - * @param string $visiblename - * @param string $description - * @param array $defaultsetting - * @global core_renderer $OUTPUT - */ - public function __construct($name, $visiblename, $description, $defaultsetting = null) { - global $OUTPUT; - $choices = array(); - $icons = array(); - $qtypes = question_bank::get_all_qtypes(); - // some qtypes do not need workaround - unset($qtypes['missingtype']); - unset($qtypes['random']); - // question_bank::sort_qtype_array() expects array(name => local_name) - $qtypenames = array_map(function($qtype) { - return $qtype->local_name(); - }, $qtypes); - foreach (question_bank::sort_qtype_array($qtypenames) as $qtypename => $label) { - $choices[$qtypename] = $label; - $icons[$qtypename] = ' ' . $OUTPUT->pix_icon('icon', '', $qtypes[$qtypename]->plugin_name()) . ' '; - } - parent::__construct($name, $visiblename, $description, $defaultsetting, $choices, $icons); - } -} diff --git a/pix/bulkdelete.gif b/pix/bulkdelete.gif deleted file mode 100644 index f9801adf1dd9becc84a3de960d07d15e1d8a5f51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmZ?wbhEHb6krfwIKsei{`~nlbLPyRJ$u@;X=P<)85tQ#NlD@1;UOU*9v&VJ4h}Xp zHoCgH+S=NRii)zbvNAF. - -/** - * Sharing Cart - REST API - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use block_sharing_cart\controller; -use block_sharing_cart\exception as sharing_cart_exception; -use block_sharing_cart\section; - -require_once __DIR__ . '/../../config.php'; - -global $PAGE, $USER; - -try { - require_login( - null, - false, - null, - false, - true - ); - - $controller = new controller(); - $is_async = get_config('block_sharing_cart', 'backup_mode') === 'async'; - - switch (required_param('action', PARAM_TEXT)) { - case 'render_tree': - $courseid = required_param('courseid', PARAM_INT); - $PAGE->set_course(get_course($courseid)); - $PAGE->set_context(\context_user::instance($USER->id)); - echo $controller->render_tree($USER->id); - exit; - case 'is_userdata_copyable': - $cmid = required_param('cmid', PARAM_INT); - echo $controller->is_userdata_copyable($cmid); - exit; - case 'is_userdata_copyable_section': - $sectionid = optional_param('sectionid', null, PARAM_INT); - if (empty($sectionid)) { - $sectionnumber = required_param('sectionnumber', PARAM_INT); - $courseid = required_param('courseid', PARAM_INT); - $section = section::get($courseid, $sectionnumber); - $sectionid = $section->id; - } - echo $controller->is_userdata_copyable_section((int)$sectionid); - exit; - case 'backup': - $cmid = required_param('cmid', PARAM_INT); - $userdata = required_param('userdata', PARAM_BOOL); - $anonymize = required_param('anonymize', PARAM_BOOL); - $courseid = required_param('courseid', PARAM_INT); - if ($is_async) { - $controller->backup_async( - $cmid, - $courseid, - $userdata, - $anonymize - ); - } - else { - $controller->backup( - $cmid, - $userdata, - $courseid, - $anonymize - ); - } - exit; - case 'backup_section': - $sectionid = optional_param('sectionid', null, PARAM_INT); - $sectionname = optional_param('sectionname', null, PARAM_TEXT); - if (empty($sectionid) || empty($sectionname)) { - $sectionnumber = required_param('sectionnumber', PARAM_INT); - $courseid = required_param('courseid', PARAM_INT); - $section = section::get($courseid, $sectionnumber); - $sectionid = $section->id; - $sectionname = $section->name; - } - $userdata = required_param('userdata', PARAM_BOOL); - $anonymize = required_param('anonymize', PARAM_BOOL); - $courseid = required_param('courseid', PARAM_INT); - $section_id = (int)$sectionid; - - if ($is_async) { - $controller->backup_section_async( - $section_id, - $courseid, - $sectionname, - $userdata, - $anonymize - ); - } - else { - $controller->backup_section($section_id, $sectionname, $userdata, $courseid, $anonymize); - } - exit; - case 'movedir': - $item_id = required_param('item_id', PARAM_INT); - $folder_to = required_param('folder_to', PARAM_TEXT); - $controller->movedir($item_id, $folder_to); - exit; - case 'move': - $item_id = required_param('item_id', PARAM_INT); - $area_to = required_param('area_to', PARAM_INT); - $controller->move($item_id, $area_to); - exit; - case 'delete': - $id = required_param('id', PARAM_INT); - $controller->delete($id); - exit; - case 'delete_directory': - $path = required_param('path', PARAM_TEXT); - $controller->delete_directory($path); - exit; - case 'ensure_backup_present': - require_sesskey(); - $cmid = required_param('cmid', PARAM_INT); - $courseid = required_param('courseid', PARAM_INT); - echo $controller->ensure_backup_in_module($cmid, $courseid); - exit; - case 'is_backup_completed': - $cmid = required_param('cmid', PARAM_INT); - $courseid = required_param('courseid', PARAM_INT); - echo $controller->is_backup_completed($cmid, $courseid); - exit; - } - throw new sharing_cart_exception('invalidoperation'); - -} catch (Exception $ex) { - http_response_code(400); - - $json = array( - 'message' => $ex->getMessage(), - ); - - echo json_encode($json); -} diff --git a/restore.php b/restore.php deleted file mode 100644 index e76fb56..0000000 --- a/restore.php +++ /dev/null @@ -1,126 +0,0 @@ -. - -/** - * Sharing Cart - Restore Operation - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use block_sharing_cart\controller; -use block_sharing_cart\section_title_form; - -require_once '../../config.php'; - -global $CFG, $OUTPUT, $PAGE, $DB, $USER; - -$directory = required_param('directory', PARAM_BOOL); -$target = required_param('target', PARAM_RAW); -$courseid = required_param('course', PARAM_INT); -$sectionnumber = required_param('section', PARAM_INT); -$overwrite = optional_param('overwrite', 0, PARAM_INT); -$returnurl = optional_param('returnurl', '', PARAM_TEXT); - -if (!$returnurl) { - $returnurl = ($courseid == SITEID) ? new moodle_url('/') : new moodle_url('/course/view.php', ['id' => $courseid]); -} - -require_login($courseid); - -try { - - $is_async = get_config('block_sharing_cart', 'restore_mode') === 'async'; - $controller = new controller(); - - // Trying to restore a directory of items - if ($directory) { - - $form = new section_title_form($directory, $target, $courseid, $sectionnumber, [], $returnurl, 0); - - if ($form->is_cancelled()) { - redirect($returnurl); exit; - } - - $target = ltrim($target, '/'); - - $sections = $controller->get_path_sections($target); - - // Directory contains an entire section of items. Display form to let user resolve conflicts - if (count($sections) > 0 && !$form->is_submitted()) { - - $items = $DB->get_records('block_sharing_cart', array('tree' => $target, 'userid' => $USER->id)); - $items_count = count($items); - - $dest_section = $DB->get_record('course_sections', array('course' => $courseid, 'section' => $sectionnumber)); - - $PAGE->set_pagelayout('standard'); - $PAGE->set_url($returnurl); - $PAGE->set_title(get_string('pluginname', 'block_sharing_cart').' - '.get_string('restore', 'block_sharing_cart')); - $PAGE->set_heading(get_string('restore', 'block_sharing_cart')); - - $form = new section_title_form($directory, $target, $courseid, $sectionnumber, $sections, $returnurl, $items_count); - - echo $OUTPUT->header(); - echo $OUTPUT->heading(get_string('section_name_conflict', 'block_sharing_cart')); - $form->display(); - echo $OUTPUT->footer(); - exit; - } - - // Perform directory restore - $controller->restore_directory($target, $courseid, $sectionnumber, $overwrite, $is_async); - - } else { - - // Restore single item - if ($is_async) { - $controller->restore_async($target, $courseid, $sectionnumber); - } - else { - $controller->restore($target, $courseid, $sectionnumber); - } - } - - redirect($returnurl); - -} catch (\block_sharing_cart\exception $ex) { - throw new moodle_exception( - $ex->errorcode, - $ex->module, - $returnurl, - $ex->a - ); - -} catch (Exception $ex) { - - if (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER) { - throw new moodle_exception( - 'notlocalisederrormessage', - 'error', - '', - $ex->__toString() - ); - } else { - throw new moodle_exception( - 'unexpectederror', - 'block_sharing_cart', - $returnurl - ); - } - -} diff --git a/settings.php b/settings.php index 0a90ad7..c90c43c 100644 --- a/settings.php +++ b/settings.php @@ -1,36 +1,16 @@ . -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2023 (c) Don Hinkelman, moxis and others - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ +// @codeCoverageIgnoreStart +defined('MOODLE_INTERNAL') || die(); +// @codeCoverageIgnoreEnd -defined('MOODLE_INTERNAL') || die; - -require_once __DIR__ . '/lib/settingslib.php'; +use block_sharing_cart\admin_settings\multi_checkbox_mod_types; +use block_sharing_cart\admin_settings\multi_checkbox_q_types; /** * @global admin_root $ADMIN * @global admin_settingpage $settings */ - if ($ADMIN->fulltree) { $settings->add(new admin_setting_configselect( 'block_sharing_cart/backup_mode', @@ -53,19 +33,19 @@ ] )); $settings->add( - new admin_setting_configmulticheckboxmodtypes( + new multi_checkbox_mod_types( 'block_sharing_cart/userdata_copyable_modtypes', get_string('settings:userdata_copyable_modtypes', 'block_sharing_cart'), get_string('settings:userdata_copyable_modtypes_desc', 'block_sharing_cart'), - array('data' => 1, 'forum' => 1, 'glossary' => 1, 'wiki' => 1) + ['data' => 1, 'forum' => 1, 'glossary' => 1, 'wiki' => 1] ) ); $settings->add( - new admin_setting_configmulticheckboxqtypes( + new multi_checkbox_q_types( 'block_sharing_cart/workaround_qtypes', get_string('settings:workaround_qtypes', 'block_sharing_cart'), get_string('settings:workaround_qtypes_desc', 'block_sharing_cart'), - array() + [] ) ); $settings->add( diff --git a/style/scss/block/content.scss b/style/scss/block/content.scss new file mode 100644 index 0000000..b5670ef --- /dev/null +++ b/style/scss/block/content.scss @@ -0,0 +1,44 @@ +.block.block_sharing_cart { + .sharing_cart_items { + display: flex; + flex-direction: column; + gap: 1rem; + + .sharing_cart_item { + display: flex; + flex-direction: column; + width: 100%; + padding-top: 0.5rem; + + &:hover { + background-color: rgba(0, 0, 0, .03); + } + + .info { + display: flex; + flex-direction: row; + padding: 0.5rem; + margin-left: 0.5rem; + margin-right: 0.5rem; + + .sharing_cart_item_actions { + display: flex; + flex-direction: row; + align-items: center; + margin-left: auto; + gap: 0.5rem; + + i { + cursor: pointer; + } + } + } + + .sharing_cart_item_children { + display: flex; + flex-direction: column; + gap: 1rem; + } + } + } +} \ No newline at end of file diff --git a/style/scss/style.scss b/style/scss/style.scss new file mode 100644 index 0000000..56e3238 --- /dev/null +++ b/style/scss/style.scss @@ -0,0 +1 @@ +@import "./block/content"; \ No newline at end of file diff --git a/style/style.css b/style/style.css new file mode 100644 index 0000000..fc8ff3a --- /dev/null +++ b/style/style.css @@ -0,0 +1 @@ +.block.block_sharing_cart .sharing_cart_items{display:flex;flex-direction:column;gap:1rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item{display:flex;flex-direction:column;width:100%;padding-top:.5rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item:hover{background-color:rgba(0,0,0,.03)}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .info{display:flex;flex-direction:row;padding:.5rem;margin-left:.5rem;margin-right:.5rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .info .sharing_cart_item_actions{display:flex;flex-direction:row;align-items:center;margin-left:auto;gap:.5rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .info .sharing_cart_item_actions i{cursor:pointer}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .sharing_cart_item_children{display:flex;flex-direction:column;gap:1rem}/*# sourceMappingURL=style.css.map */ diff --git a/style/style.css.map b/style/style.css.map new file mode 100644 index 0000000..5eb58d7 --- /dev/null +++ b/style/style.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["scss/block/content.scss"],"names":[],"mappings":"AACE,8CACE,aACA,sBACA,SAEA,iEACE,aACA,sBACA,WACA,kBAEA,uEACE,iCAGF,uEACE,aACA,mBACA,cACA,kBACA,mBAEA,kGACE,aACA,mBACA,mBACA,iBACA,UAEA,oGACE,eAKN,6FACE,aACA,sBACA","file":"style.css"} \ No newline at end of file diff --git a/styles.css b/styles.css deleted file mode 100644 index c071e60..0000000 --- a/styles.css +++ /dev/null @@ -1,159 +0,0 @@ -.block_sharing_cart .sc-indent-0 { - margin-left: 0em; -} - -.block_sharing_cart .sc-indent-1 { - margin-left: 0.5em; -} - -.block_sharing_cart .sc-indent-2 { - margin-left: 1em; -} - -.block_sharing_cart .sc-indent-3 { - margin-left: 1.5em; -} - -.block_sharing_cart .sc-indent-4 { - margin-left: 2em; -} - -.block_sharing_cart .sc-indent-5 { - margin-left: 2.5em; -} - -.block_sharing_cart .sc-indent-6 { - margin-left: 3em; -} - -.block_sharing_cart .sc-indent-7 { - margin-left: 3.5em; -} - -.block_sharing_cart .sc-indent-8 { - margin-left: 4em; -} - -.block_sharing_cart .sc-indent-9 { - margin-left: 4.5em; -} - -.block_sharing_cart .sc-indent-10 { - margin-left: 5em; -} - -.block_sharing_cart .sc-indent-11 { - margin-left: 5.5em; -} - -.block_sharing_cart .sc-indent-12 { - margin-left: 6em; -} - -.block_sharing_cart .sc-indent-13 { - margin-left: 6.5em; -} - -.block_sharing_cart .sc-indent-14 { - margin-left: 7em; -} - -.block_sharing_cart .sc-indent-15 { - margin-left: 7.5em; -} - -.block_sharing_cart .sc-space-1 { - margin-left: 1px; -} - -.block_sharing_cart .sc-space-2 { - margin-left: 2px; -} - -.block_sharing_cart .sc-space-3 { - margin-left: 3px; -} - -.block_sharing_cart .sc-space-4 { - margin-left: 4px; -} - -.block_sharing_cart .sc-space-5 { - margin-left: 5px; -} - -.block_sharing_cart .tree.list { - font-size:90%; -} - -.course-content .move_target { - max-width: 100%; - border: 2px dashed #999; - margin-top: 15px; - margin-bottom: 15px; - padding: 5px; -} - -.course-content .clipboard { - max-width: 100%; - border: 2px dashed #999; - margin-bottom: 15px; - padding: 10px; - font-weight: bold; -} - -.course-content h3.sectionname a:nth-child(1) { - margin-right: 5px; -} - -.course-content h3.sectionname .inplaceeditable-text .icon { - margin-right: 0; -} - -.course-content .action-menu-item .icon { - margin-right: 0; -} - -.format-grid #section-0 .sectionname .fa { - padding-left: 5px; -} - -.editing.format-topics .course-content h3.sectionname .add-to-sharing-cart, -.editing.format-weeks .course-content h3.sectionname .add-to-sharing-cart, -.editing.format-grid .course-content h3.sectionname .add-to-sharing-cart { - padding-top: 15px; -} - -.block_sharing_cart div.dragging_item { - animation: drop_area ease-in-out .5s forwards; - filter: blur(2px); -} - -@keyframes drop_area { - 100% { - background-color: lightslategrey; - } -} - -.sharing_cart_basket.drag_over { - filter: brightness(.8); -} - -.sharing_cart_basket.shake_basket { - animation: shake_basket infinite 3s linear !important; -} - -@keyframes shake_basket { - 0% { - transform: rotate(0deg); - } - 10% { - transform: rotate(-30deg); - } - 20% { - transform: rotate(30deg); - } - 30%, 100% { - transform: rotate(0deg); - } -} \ No newline at end of file diff --git a/templates/block/content.mustache b/templates/block/content.mustache new file mode 100644 index 0000000..9c4d28f --- /dev/null +++ b/templates/block/content.mustache @@ -0,0 +1,5 @@ + diff --git a/templates/block/item.mustache b/templates/block/item.mustache new file mode 100644 index 0000000..52b3528 --- /dev/null +++ b/templates/block/item.mustache @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/tests/integration/controller/controller_test.php b/tests/integration/controller/controller_test.php deleted file mode 100644 index 695873e..0000000 --- a/tests/integration/controller/controller_test.php +++ /dev/null @@ -1,437 +0,0 @@ -resetAfterTest(); - } - - /** - * Test add activities to sharing cart - * @throws dml_exception - * @throws moodle_exception - */ - public function test_add_activities_to_sharing_cart(): void { - // Create course, user and assignments - $user = $this->create_user(); - $course = $this->create_course(); - - $assignment = $this->create_assignment($course, 1); - $label = $this->create_module('label', $course, 2); - $forum = $this->create_module('forum', $course, 2); - - // Enrolling user that capable to do backup and restore - $this->enrol_users($course, [$user]); - - // Set session key and set current user - $this->set_session_key($user); - - rebuild_course_cache($course->id); - - // Test if sharing cart is empty for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(0, $entities); - - $controller = new controller(); - $controller->backup($assignment->cmid, false, $course->id, false); - $controller->backup($label->cmid, false, $course->id, false); - $controller->backup($forum->cmid, false, $course->id, false); - - // Test if sharing cart have 3 copied activities for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(3, $entities); - - foreach ($entities as $entity) { - switch ($entity->modname) { - case 'assign': - $this->assertEquals($assignment->name, $entity->modtext); - break; - case 'forum': - $this->assertEquals($forum->name, $entity->modtext); - break; - case 'label': - $this->assertEquals($label->name, strip_tags($entity->modtext)); - break; - default: - $this->throwException(new Exception('No activity in sharing cart')); - break; - } - } - } - - /** - * Test add sections to sharing cart - * @throws dml_exception - * @throws moodle_exception - */ - public function test_add_sections_to_sharing_cart(): void { - // Create course, user and assignments - $user = $this->create_user(); - $course = $this->create_course(); - $assignment1 = $this->create_assignment($course, 1); - $assignment2 = $this->create_assignment($course, 1); - $assignment3 = $this->create_assignment($course, 2); - $assignment4 = $this->create_assignment($course, 2); - - $section1 = $this->get_course_section($course, 1); - $section2 = $this->get_course_section($course, 2); - - // Enrolling user that capable to do backup and restore - $this->enrol_users($course, [$user]); - - // Set session key and set current user - $this->set_session_key($user); - - rebuild_course_cache($course->id); - - // Test if sharing cart is empty for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(0, $entities); - - $controller = new controller(); - $controller->backup_section($section1->id, $section1->name, false, $course->id, false); - $controller->backup_section($section2->id, $section2->name, false, $course->id, false); - - // Test if sharing cart have 4 copied activities for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(4, $entities); - $names = [ - $assignment1->name, - $assignment2->name, - $assignment3->name, - $assignment4->name, - ]; - - // Test if entities and the same name as assignments - foreach ($entities as $entity) { - $this->assertContains($entity->modtext, $names); - } - } - - /** - * Test add sections to sharing cart when module visisble is false - * @throws dml_exception - * @throws moodle_exception - */ - public function test_add_sections_to_sharing_cart_when_module_visible_is_false(): void { - // Create course, user and assignments - $user = $this->create_user(); - $course = $this->create_course(); - $this->create_assignment($course, 1); - $this->create_assignment($course, 1); - $this->create_assignment($course, 2); - $this->create_assignment($course, 2); - - $url1 = $this->create_url($course, 1); - $url2 = $this->create_url($course, 2); - - $section1 = $this->get_course_section($course, 1); - $section2 = $this->get_course_section($course, 2); - - $this->disable_assign(); - - // Enrolling user that capable to do backup and restore - $this->enrol_users($course, [$user]); - - // Set session key and set current user - $this->set_session_key($user); - - rebuild_course_cache($course->id); - - // Test if sharing cart is empty for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(0, $entities); - - $controller = new controller(); - $controller->backup_section($section1->id, $section1->name, false, $course->id, false); - $controller->backup_section($section2->id, $section2->name, false, $course->id, false); - - $this->enable_assign(); - - // Test if sharing cart have 2 copied urls for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(2, $entities); - $names = [ - $url1->name, - $url2->name - ]; - - // Test if entities and the same name as url's - foreach ($entities as $entity) { - $this->assertContains($entity->modtext, $names); - } - } - - /** - * Test add section and modules to sharing cart - * @throws dml_exception - * @throws moodle_exception - */ - public function test_add_sections_and_modules_to_sharing_cart(): void { - // Create course, user and assignments - $user = $this->create_user(); - $course = $this->create_course(); - $label = $this->create_module('label', $course, 1); - $assignment1 = $this->create_assignment($course, 2); - $assignment2 = $this->create_assignment($course, 2); - - $section2 = $this->get_course_section($course, 1); - - // Enrolling user that capable to do backup and restore - $this->enrol_users($course, [$user]); - - // Set session key and set current user - $this->set_session_key($user); - - rebuild_course_cache($course->id); - - // Test if sharing cart is empty for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(0, $entities); - - $controller = new controller(); - $controller->backup_section($section2->id, $section2->name, false, $course->id, false); - $controller->backup($assignment1->cmid, false, $course->id, false); - $controller->backup($assignment2->cmid, false, $course->id, false); - - // Test if sharing cart have 3 copied activities for current user - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - $this->assertCount(3, $entities); - - $copied_labels = self::db()->get_records(record::TABLE, ['modname' => 'label']); - $copied_assignments = self::db()->get_records(record::TABLE, ['modname' => 'assign']); - } - - public function test_restore_modules_from_sharing_cart() { - $user = $this->create_user(); - $course = $this->create_course(); - $section1 = $this->get_course_section($course, 1); - $section2 = $this->get_course_section($course, 2); - $section3 = $this->get_course_section($course, 3); - - $assignment = $this->create_assignment($course, $section1->section); - $label = $this->create_module('label', $course, $section2->section); - $forum = $this->create_module('forum', $course, $section3->section); - - $this->enrol_users($course, [$user]); - $this->set_session_key($user); - - $controller = new controller(); - $controller->backup($assignment->cmid, false, $course->id, false); - $controller->backup($forum->cmid, false, $course->id, false); - $controller->backup($label->cmid, false, $course->id, false); - $controller->backup_section($section1->id, $section1->name, false, $course->id, false); - $controller->backup_section($section2->id, $section2->name, false, $course->id, false); - $controller->backup_section($section3->id, $section3->name, false, $course->id, false); - - rebuild_course_cache($course->id); - - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - - $this->assertCount(6, $entities); - - $new_course = $this->create_course(); - $this->enrol_users($new_course, [$user]); - - $course_modules = get_course_mods($new_course->id); - $this->assertCount(0, $course_modules); - - foreach ($entities as $entity) { - if (empty($entity->tree)) { - $controller->restore($entity->id, $new_course->id, 1); - } - else { - $controller->restore_directory($entity->tree, $new_course->id, 1, false); - } - } - - $new_course_modules = get_course_mods($new_course->id); - $this->assertNotEmpty($new_course_modules); - - $exclude_properties = [ - 'id', - 'cmid', - 'course' - ]; - - foreach ($new_course_modules as $new_module) { - $module = null; - switch ($new_module->modname) { - case 'assign': - $module = $assignment; - break; - case 'forum': - $module = $forum; - break; - case 'label': - $module = $label; - break; - } - - if (empty($module)) { - throw new Exception('Cannot find any module in the course'); - } - - $this->compare_properties($assignment, $new_module, $exclude_properties); - } - } - - /** - * Test moving item in sharing cart to a new position - * - * @throws exception - * @throws dml_exception - * @throws moodle_exception - */ - public function test_move_sharing_cart_position() { - $user = $this->create_user(); - $course = $this->create_course(); - $forum = $this->create_module('forum', $course, 0); - $assignment = $this->create_assignment($course, 1); - $label = $this->create_module('label', $course, 2); - - $section1 = $this->get_course_section($course, 1); - $section2 = $this->get_course_section($course, 2); - - $this->enrol_users($course, [$user]); - $this->set_session_key($user); - - // Copy modules to the sharing cart - $controller = new controller(); - $controller->backup($forum->cmid, false, $course->id, false); - $controller->backup($assignment->cmid, false, $course->id, false); - $controller->backup($label->cmid, false, $course->id, false); - $controller->backup_section($section1->id, $section1->name, false, $course->id, false); - $controller->backup_section($section2->id, $section2->name, false, $course->id, false); - - $sharing_cart_assignment = $this->get_sharing_cart_by_module('assign'); - $sharing_cart_forum = $this->get_sharing_cart_by_module('forum'); - $sharing_cart_label = $this->get_sharing_cart_by_module('label'); - - // Order - // assignment, label, forum - $controller->move($sharing_cart_forum->id, 0); - - $weight_assignment = $this->get_sharing_cart_weight($sharing_cart_assignment->id); - $weight_forum = $this->get_sharing_cart_weight($sharing_cart_forum->id); - $weight_label = $this->get_sharing_cart_weight($sharing_cart_label->id); - - // Assert order position assignment > label > forum - $this->assertTrue($weight_assignment < $weight_label && $weight_label < $weight_forum); - - // Order - // label, assignment, forum - $controller->move($sharing_cart_label->id, $sharing_cart_assignment->id); - - $weight_assignment = $this->get_sharing_cart_weight($sharing_cart_assignment->id); - $weight_forum = $this->get_sharing_cart_weight($sharing_cart_forum->id); - $weight_label = $this->get_sharing_cart_weight($sharing_cart_label->id); - - // Assert order position label > assignment > forum - $this->assertTrue($weight_label < $weight_assignment && $weight_assignment < $weight_forum); - - // Order - // forum, label, assignment - $controller->move($sharing_cart_forum->id, $sharing_cart_label->id); - // forum, assignment, label - $controller->move($sharing_cart_assignment->id, $sharing_cart_label->id); - - $weight_assignment = $this->get_sharing_cart_weight($sharing_cart_assignment->id); - $weight_forum = $this->get_sharing_cart_weight($sharing_cart_forum->id); - $weight_label = $this->get_sharing_cart_weight($sharing_cart_label->id); - - // Assert order original position forum > assign > label - $this->assertTrue($weight_forum < $weight_assignment && $weight_assignment < $weight_label); - - // Move label from root to section1 folder - $controller->movedir($sharing_cart_label->id, $section1->name); - - $section_label = $this->get_sharing_cart_entity(['id' => $sharing_cart_label->id]); - $this->assertEquals($section_label->tree, $section1->name); - - // Move label from section1 to section2 folder - $controller->movedir($sharing_cart_label->id, $section2->name); - - $section_label = $this->get_sharing_cart_entity(['id' => $sharing_cart_label->id]); - $this->assertEquals($section_label->tree, $section2->name); - - } - - public function test_delete_sharing_cart() { - $user = $this->create_user(); - $course = $this->create_course(); - $assignment = $this->create_assignment($course, 1); - $label = $this->create_module('label', $course, 1); - $section1 = $this->get_course_section($course, 1); - - $this->enrol_users($course, [$user]); - $this->set_session_key($user); - - $controller = new controller(); - $controller->backup($assignment->cmid, false, $course->id, false); - $controller->backup($label->cmid, false, $course->id, false); - $controller->backup_section($section1->id, $section1->name, false, $course->id, false); - - $entities = $this->get_sharing_cart_entities(['userid' => $user->id]); - - // Expect user to have 4 copies of modules - $this->assertCount(4, $entities); - - $entities_no_folder = $this->get_sharing_cart_entities(['userid' => $user->id, 'tree' => '']); - $entities_with_folder = $this->get_sharing_cart_entities(['userid' => $user->id, 'tree' => $section1->name]); - - // Expect user to have 2 copies of modules - $this->assertCount(2, $entities_no_folder); - - // Expect user to have 2 copies of modules - $this->assertCount(2, $entities_with_folder); - - foreach ($entities_no_folder as $entity) { - $controller->delete($entity->id); - } - - $entities_no_folder = $this->get_sharing_cart_entities(['userid' => $user->id, 'tree' => '']); - // Expect user to have no copy of modules left - $this->assertCount(0, $entities_no_folder); - - $controller->delete_directory($section1->name); - $entities_with_folder = $this->get_sharing_cart_entities(['userid' => $user->id, 'tree' => $section1->name]); - // Expect user to have no copy of modules left for this section - $this->assertCount(0, $entities_with_folder); - } - - /** - * @param $a - * @param $b - * @param array $exclude_properties - */ - private function compare_properties($a, $b, array $exclude_properties = []) { - $obj_a = get_object_vars($a); - $obj_b = get_object_vars($a); - $this->assertSameSize($obj_a, $obj_b); - - foreach ($obj_a as $name => $value) { - if (!in_array($name, $exclude_properties, true)) { - continue; - } - $this->assertEquals($obj_b[$name], $obj_b[$name]); - } - } -} diff --git a/tests/integration/db/upgrade_database_test.php b/tests/integration/db/upgrade_database_test.php deleted file mode 100644 index fb77f45..0000000 --- a/tests/integration/db/upgrade_database_test.php +++ /dev/null @@ -1,272 +0,0 @@ -resetAfterTest(); - } - - /** - * Test upgrade script before version 2011111100 - * This test try to simulate the upgrade script that rename sharing cart columns - * @see xmldb_block_sharing_cart_upgrade() below the code: if ($oldversion < 2011111100)... - * @test - */ - public function change_sharing_cart_table_column_names() { - $table = new xmldb_table($this->random_name()); - $fields = [ - 'id' => new xmldb_field('id', XMLDB_TYPE_INTEGER, 10, true, XMLDB_NOTNULL, true), - 'userid' => new xmldb_field('user', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, false, 0), - 'modname' => new xmldb_field('name', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, false), - 'modicon' => new xmldb_field('icon', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, false), - 'modtext' => new xmldb_field('text', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, false), - 'ctime' => new xmldb_field('time', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, false, 0), - 'filename' => new xmldb_field('file', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, false), - 'tree' => new xmldb_field('tree', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, false), - 'weight' => new xmldb_field('sort', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, false, 0) - ]; - - $table = $this->create_table($table, $fields); - $old_column_names = $this->get_column_names($table); - - // Asserting current column names - foreach ($fields as $field) { - $this->assertContains($field->getName(), $old_column_names); - } - - $dbman = $this->db()->get_manager(); - - // Change column names - foreach ($fields as $name => $field) { - // Skip column that contain the same name - if ($field->getName() === $name) { - continue; - } - $dbman->rename_field($table, $field, $name); - } - - // Asserting new column names - $new_column_names = $this->get_column_names($table); - foreach ($fields as $name => $field) { - $this->assertContains($name, $new_column_names); - } - - $this->drop_junk_tables(); - } - - /** - * Test upgrade script before version 2017121200 - * This test try to simulate the upgrade script that contain bug when upgrading the plugin the column that have NOT NULL should not have default value as empty string - * @see xmldb_block_sharing_cart_upgrade() below the code: if ($oldversion < 2017121200)... - * @test - */ - public function create_sharing_cart_section_table() { - $this->setAdminUser(); - - // New script - $working_table = new xmldb_table($this->random_name()); - $fields = [ - 'id' => new xmldb_field('id', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null), - 'name' => new xmldb_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null, null, 'id'), - 'summary' => new xmldb_field('summary', XMLDB_TYPE_TEXT, null, null, null, null, null, 'name'), - 'summaryformat' => new xmldb_field('summaryformat', XMLDB_TYPE_INTEGER, 2, null, XMLDB_NOTNULL, false, 0, 'summary'), - ]; - $working_table = $this->create_table($working_table, $fields); - $this->assertEmpty($working_table->getAllErrors()); - - $this->drop_junk_tables(); - } - - /** - * Test upgrade script before version 2020072700 - * @throws ddl_exception - * @throws ddl_field_missing_exception - * @throws ddl_table_missing_exception|coding_exception - *@see xmldb_block_sharing_cart_upgrade() below the code: if ($oldversion < 2020072700)... - * @test - */ - public function change_default_value_for_section_table() { - $dbman = $this->db()->get_manager(); - $table = $this->create_section_table(); - - $field_name = new xmldb_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL); - $field_summary = new xmldb_field('summary', XMLDB_TYPE_TEXT, null, null, null, null, null, 'name'); - $field_summaryformat = new xmldb_field('summaryformat', XMLDB_TYPE_INTEGER, 2, null, XMLDB_NOTNULL, false, 0, 'summary'); - - if ($dbman->field_exists($table, $field_name)) { - $dbman->change_field_default($table, $field_name); - } - if ($dbman->field_exists($table, $field_summary)) { - $dbman->change_field_default($table, $field_summary); - } - if ($dbman->field_exists($table, $field_summaryformat)) { - $dbman->change_field_default($table, $field_summaryformat); - } - - $this->drop_junk_tables(); - } - - /** - * Create sharing cart sections similar to install.xml - * @param string|null $table_name - * @return xmldb_table - * @throws ddl_exception|coding_exception - */ - private function create_section_table(&$table_name = null) { - if (empty($table_name)) { - $table_name = $this->random_name(); - } - - // Initiate table - $table = new xmldb_table($table_name); - - // Set table fields - $table->add_field('id', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); - $table->add_field('name', XMLDB_TYPE_CHAR, 255, null, null, null, '', 'id'); - $table->add_field('summary', XMLDB_TYPE_TEXT, null, null, null, null, null, 'name'); - $table->add_field('summaryformat', XMLDB_TYPE_INTEGER, 2, null, XMLDB_NOTNULL, null, 0, 'summary'); - - // Set index - $table->add_key('id', XMLDB_KEY_PRIMARY, array('id')); - - return $this->create_table($table); - } - - /** - * @param xmldb_table $table - * @param array $fields - * @return xmldb_table - * @throws coding_exception - * @throws ddl_exception - */ - private function create_table(xmldb_table $table, array $fields = []) { - foreach ($fields as $field) { - if (!$field instanceof xmldb_field) { - continue; - } - $table->addField($field); - - $field_name = $field->getName(); - if ($field_name === 'id') { - $table->add_key( - $field_name, - XMLDB_KEY_PRIMARY, - [$field_name] - ); - } - } - - $dbman = $this->db()->get_manager(); - $dbman->create_table($table); - $this->junk_tables[$table->getName()] = $table; - return $table; - } - - /** - * Drop junk tables that created by $this->create_table(...) method - * @param mixed ...$tables if empty, it will drop all junk tables - * @throws ddl_exception - * @throws ddl_table_missing_exception - */ - private function drop_junk_tables(...$tables) { - if (empty($tables)) { - $tables = $this->junk_tables; - } - - $exist_tables = []; - foreach ($tables as $name => $table) { - if (isset($this->junk_tables[$name])) { - $exist_tables[] = $table; - } - } - - if (!empty($exist_tables)) { - $this->drop_tables(...$exist_tables); - } - } - - /** - * @param array $tables - * @throws ddl_exception - * @throws ddl_table_missing_exception - */ - private function drop_tables(...$tables) { - $dbman = $this->db()->get_manager(); - - foreach ($tables as $table) { - if (is_string($table)) { - $table = new xmldb_table($table); - } - if (!$table instanceof xmldb_table) { - continue; - } - - $dbman->drop_table($table); - } - } - - /** - * @param int $length - * @return string - * @throws Exception - */ - private function random_name($length = 16) { - $chars = 'abcdefghijklmnopqrstuvwxyz'; - $position = random_int(0, strlen($chars) - 1); - $first_letter = $chars[$position]; - - return $first_letter . substr( - bin2hex(random_bytes($length)), - 0, - $length - 1 - ); - } - - /** - * @param xmldb_table $table - * @return string[] - */ - private function get_column_names(xmldb_table $table) { - $columns = $this->db()->get_columns($table->getName()); - - if (empty($columns)) { - return []; - } - - return array_map(function($column) { - return $column->name; - }, $columns); - } - - /** - * Get moodle database - * @return moodle_database - */ - private function db(): moodle_database { - global $DB; - return $DB; - } -} diff --git a/tests/integration/observers_test.php b/tests/integration/observers_test.php index 257d8dd..309cc39 100644 --- a/tests/integration/observers_test.php +++ b/tests/integration/observers_test.php @@ -1,37 +1,15 @@ . - -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2017 (C) VERSION2, INC. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ namespace block_sharing_cart\integration; -use advanced_testcase; -use core\event\user_deleted; -use moodle_database; - // @codeCoverageIgnoreStart defined('MOODLE_INTERNAL') || die(); // @codeCoverageIgnoreEnd +use advanced_testcase; +use core\event\user_deleted; +use moodle_database; + class observers_test extends advanced_testcase { protected function setUp(): void diff --git a/tests/integration/privacy/privacy_test.php b/tests/integration/privacy/privacy_test.php deleted file mode 100644 index 3de2f7d..0000000 --- a/tests/integration/privacy/privacy_test.php +++ /dev/null @@ -1,489 +0,0 @@ -resetAfterTest(); - } - - /** - * Test getting the context for the user ID related to sharing cart. - * @test - */ - public function get_contexts_for_user(): void { - $course = self::getDataGenerator()->create_course(); - $user = self::getDataGenerator()->create_user(); - $assign = $this->create_assignment($course, 1); - - self::getDataGenerator()->enrol_user($user->id, $course->id, 'editingteacher'); - - $this->set_session_key($user); - $this->add_sharing_cart_activity($course, $assign); - - $context = context_user::instance($user->id); - $contextlist = provider::get_contexts_for_userid($user->id); - - $actual_contexts = $contextlist->get_contexts(); - - $this->assertContains( - $context, - $actual_contexts - ); - } - - /** - * @test - */ - public function get_users_in_context(): void { - $component = 'block_sharing_cart'; - - $generator = self::getDataGenerator(); - - // Prepare user, course and assignments - $user1 = $generator->create_user(); - $user2 = $generator->create_user(); - $course = $generator->create_course(['numsections' => 4]); - - $generator->enrol_user($user1->id, $course->id, 'editingteacher'); - $generator->enrol_user($user2->id, $course->id, 'editingteacher'); - - $user1_context = context_user::instance($user1->id); - $user2_context = context_user::instance($user2->id); - - $this->create_assignment($course, 1); - $this->create_assignment($course, 2); - - $this->set_session_key($user1); - - $userlist1 = new userlist($user1_context, $component); - provider::get_users_in_context($userlist1); - $this->assertCount(0, $userlist1); - - // Copy section to sharing cart - $this->add_sharing_cart_section($course, 1); - - provider::get_users_in_context($userlist1); - // Expect user1 to have 1 data - $this->assertCount(1, $userlist1); - - $this->set_session_key($user2); - - $userlist2 = new userlist($user2_context, $component); - provider::get_users_in_context($userlist2); - - // Expect user2 to have 0 data - $this->assertCount(0, $userlist2); - - // Validate in system context - $systemcontext = context_system::instance(); - $userlist3 = new userlist($systemcontext, $component); - provider::get_users_in_context($userlist3); - $this->assertCount(0, $userlist3); - } - - - - /** - * Test exporting user data - * @test - * @throws coding_exception - * @throws moodle_exception - */ - public function export_user_data(): void { - $generator = self::getDataGenerator(); - - // Prepare user, course and assignments - $user = $generator->create_user(); - $course = $generator->create_course(['numsections' => 4]); - $generator->enrol_user($user->id, $course->id, 'editingteacher'); - - $cm1 = $this->create_assignment($course, 0); - $cm2 = $this->create_assignment($course, 1); - $cm3 = $this->create_assignment($course, 2); - $cm4 = $this->create_assignment($course, 2); - - $this->set_session_key($user); - - // Copy assignment and sections to sharing cart - $this->add_sharing_cart_activity($course, $cm1); - $this->add_sharing_cart_section($course, 1); - $this->add_sharing_cart_section($course, 2); - $this->add_sharing_cart_section($course, 2); - - // Prepare assignments names for assertion - $root_cm_names = [ - $cm1->name - ]; - $section1_cm_names = [ - $cm2->name - ]; - $section2_cm_names = [ - $cm3->name, - $cm4->name - ]; - - // Prepare user context - $context = context_user::instance($user->id); - $component = 'block_sharing_cart'; - $root_name = get_string('pluginname', $component); - - // Privacy data writer - $writer = writer::with_context($context); - $this->assertFalse($writer->has_any_data()); - - // Export user data - $this->export_context_data_for_user($user->id, $context, $component); - - // Get user data - $section1_name = get_section_name($course->id, 1); - $section2_name = get_section_name($course->id, 2); - $section2_name_copy = "$section2_name (1)"; - - $data_root = (array)$writer->get_data([$root_name]); - $data_section1 = (array)$writer->get_data([$root_name, $section1_name]); - $data_section2 = (array)$writer->get_data([$root_name, $section2_name]); - $data_section2_copy = (array)$writer->get_data([$root_name, $section2_name_copy]); - - // Assertions - $this->assertNotEmpty($data_root); - $this->assertNotEmpty($data_section1); - $this->assertNotEmpty($data_section2); - $this->assertNotEmpty($data_section2_copy); - - foreach ($data_root as $data) { - $this->assertEquals('assign', $data['type']); - $this->assertContains($data['name'], $root_cm_names); - } - - foreach ($data_section1 as $data) { - $this->assertEquals('assign', $data['type']); - $this->assertContains($data['name'], $section1_cm_names); - } - - foreach ($data_section2 as $data) { - $this->assertEquals('assign', $data['type']); - $this->assertContains($data['name'], $section2_cm_names); - } - - foreach ($data_section2_copy as $data) { - $this->assertEquals('assign', $data['type']); - $this->assertContains($data['name'], $section2_cm_names); - } - } - - /** - * @test - * @throws coding_exception - * @throws dml_exception - * @throws moodle_exception - */ - public function delete_data_for_all_users_in_context(): void { - $generator = self::getDataGenerator(); - $course = $generator->create_course(); - $assign = $this->create_assignment($course, 1); - $user = $generator->create_user(); - $context = context_user::instance($user->id); - - $generator->enrol_user($user->id, $course->id, 'editingteacher'); - - $this->set_session_key($user); - $this->add_sharing_cart_section($course, 1); - - $entities = $this->get_user_sharing_cart($user); - $this->assertCount(1, $entities); - - provider::delete_data_for_all_users_in_context($context); - - $entities = $this->get_user_sharing_cart($user); - $this->assertCount(0, $entities); - } - - /** - * Test delete data for users - * @test - */ - public function delete_data_for_users() { - global $DB; - - $generator = self::getDataGenerator(); - $component = 'block_sharing_cart'; - $rolename = 'editingteacher'; - - // Course - $course = $generator->create_course(['numsections' => 4]); - - // Assignments - $section1_assign1 = $this->create_assignment($course, 1); - $section2_assign1 = $this->create_assignment($course, 2); - $section2_assign2 = $this->create_assignment($course, 2); - $section3_assign1 = $this->create_assignment($course, 3); - $section3_assign2 = $this->create_assignment($course, 3); - $section3_assign3 = $this->create_assignment($course, 3); - $section4_assign1 = $this->create_assignment($course, 4); - $section4_assign2 = $this->create_assignment($course, 4); - $section4_assign3 = $this->create_assignment($course, 4); - $section4_assign4 = $this->create_assignment($course, 4); - - // Users - $user1 = $generator->create_user(); - $user2 = $generator->create_user(); - $user3 = $generator->create_user(); - $user4 = $generator->create_user(); - - // Enrolling users - $generator->enrol_user($user1->id, $course->id, $rolename); - $generator->enrol_user($user2->id, $course->id, $rolename); - $generator->enrol_user($user3->id, $course->id, $rolename); - $generator->enrol_user($user4->id, $course->id, $rolename); - - // Contexts - $usercontext1 = context_user::instance($user1->id); - $usercontext2 = context_user::instance($user2->id); - $usercontext3 = context_user::instance($user3->id); - $usercontext4 = context_user::instance($user4->id); - - // User list - $userlist1 = new userlist($usercontext1, $component); - $userlist2 = new userlist($usercontext2, $component); - $userlist3 = new userlist($usercontext3, $component); - $userlist4 = new userlist($usercontext4, $component); - - // Copy assignment to sharing cart for user1 - // user1 have 1 section copied - $this->set_session_key($user1); - $this->add_sharing_cart_section($course, 1); - - // Copy assignments to sharing cart for user2 - // user2 have 2 sections copied - $this->set_session_key($user2); - $this->add_sharing_cart_section($course, 1); - $this->add_sharing_cart_section($course, 2); - - // Copy assignments to sharing cart for user3 - // user3 have 3 sections copied - $this->set_session_key($user3); - $this->add_sharing_cart_section($course, 1); - $this->add_sharing_cart_section($course, 2); - $this->add_sharing_cart_section($course, 3); - - // Copy assignments to sharing cart for user4 - // user4 have 4 sections copied - $this->set_session_key($user4); - $this->add_sharing_cart_section($course, 1); - $this->add_sharing_cart_section($course, 2); - $this->add_sharing_cart_section($course, 3); - $this->add_sharing_cart_section($course, 4); - - // Test if data is actual exist for user2 & user4 - $user2_data = $this->get_user_sharing_cart($user2, $course); - $user4_data = $this->get_user_sharing_cart($user4, $course); - $this->assertCount(1 + 2, $user2_data); - $this->assertCount(1 + 2 + 3 + 4, $user4_data); - - // Userlist assertions - provider::get_users_in_context($userlist1); - $this->assertCount(1, $userlist1); - $this->assertEquals([$user1->id], $userlist1->get_userids()); - - provider::get_users_in_context($userlist2); - $this->assertCount(1, $userlist2); - $this->assertEquals([$user2->id], $userlist2->get_userids()); - - provider::get_users_in_context($userlist2); - $this->assertCount(1, $userlist2); - $this->assertEquals([$user2->id], $userlist2->get_userids()); - - provider::get_users_in_context($userlist4); - $this->assertCount(1, $userlist4); - $this->assertEquals([$user4->id], $userlist4->get_userids()); - - // Delete data user user2 & user4 - $approvedlist2 = new approved_userlist($usercontext2, $component, $userlist2->get_userids()); - $approvedlist4 = new approved_userlist($usercontext4, $component, $userlist4->get_userids()); - - provider::delete_data_for_users($approvedlist2); - provider::delete_data_for_users($approvedlist4); - - // re-initialize userlist for user2 & user4 - $userlist2 = new userlist($usercontext2, $component); - $userlist4 = new userlist($usercontext4, $component); - $this->assertCount(0, $userlist2); - $this->assertCount(0, $userlist4); - - // Test the actual was deleted for user2 & user4 - $user2_data = $this->get_user_sharing_cart($user2, $course); - $user4_data = $this->get_user_sharing_cart($user4, $course); - $this->assertCount(0, $user2_data); - $this->assertCount(0, $user4_data); - } - - /** - * Test delete data for user - * @test - */ - public function delete_data_for_user(): void { - global $DB; - - $generator = self::getDataGenerator(); - $user = $generator->create_user(); - $user2 = $generator->create_user(); - $course = $generator->create_course(['numsections' => 4]); - $assign1 = $this->create_assignment($course, 1); - $assign2 = $this->create_assignment($course, 2); - - $generator->enrol_user($user->id, $course->id, 'editingteacher'); - $generator->enrol_user($user2->id, $course->id, 'student'); - $this->set_session_key($user); - - $this->add_sharing_cart_activity($course, $assign1); - $this->add_sharing_cart_section($course, 2); - - // Expect 2 items in this assertion - $entities = $this->get_user_sharing_cart($user); - $this->assertCount(2, $entities); - - // Out of context delete data request - $contextids = [ - context_system::instance()->id, - context_course::instance($course->id)->id, - ]; - $contextlist = new approved_contextlist($user,'block_sharing_cart',$contextids); - // Expect nothing to be delete for the system context - provider::delete_data_for_user($contextlist); - - // Still expect 2 items in this assertion - $entities = $this->get_user_sharing_cart($user); - $this->assertCount(2, $entities); - - // Adding another user context and test if another user context have any affect on current user - $contextids[] = context_user::instance($user2->id)->id; - $contextlist = new approved_contextlist($user,'block_sharing_cart', $contextids); - // Expect nothing to be delete for the system context - provider::delete_data_for_user($contextlist); - - // Still expect 2 items in this assertion - $entities = $this->get_user_sharing_cart($user); - $this->assertCount(2, $entities); - - // Request data deletion for the user - $context = context_user::instance($user->id); - $contextlist = new approved_contextlist($user, 'block_sharing_cart', [$context->id]); - provider::delete_data_for_user($contextlist); - - // Expect 0 item in this assertion - $entities = $this->get_user_sharing_cart($user); - $this->assertCount(0, $entities); - } - - /** - * Set session via GET method - * @param object $user - */ - private function set_session_key(object $user): void { - // Set current user - self::setUser($user); - - // Sharing cart required session key as parameter - // Send session key via GET - $_GET['sesskey'] = sesskey(); - } - - /** - * Add activity to sharing cart - * @param object $course - * @param object $module - * @throws moodle_exception - */ - private function add_sharing_cart_activity(object $course, object $module): void { - // Creating sharing cart item - $controller = new controller(); - $controller->backup($module->cmid, false, $course->id, false); - } - - /** - * Add section to sharing cart - * @param object $course - * @param int $section - * @return string Section name - * @throws dml_exception - * @throws moodle_exception - */ - private function add_sharing_cart_section(object $course, int $section = 0): string { - global $DB; - - $section_record = $DB->get_record( - 'course_sections', - ['course' => $course->id, 'section' => $section] - ); - - // Section is out of range - if (empty($section_record)) { - throw new InvalidArgumentException('Given section is out of range for the course with id '. $course->id); - } - - $section_name = get_section_name($course, $section); - - // Backup section for sharing cart - $controller = new controller(); - $controller->backup_section($section_record->id, $section_name, false, $course->id, false); - - return $section_name; - } - - /** - * @param $user - * @param object|null $course - * @return array - * @throws dml_exception - */ - private function get_user_sharing_cart($user, ?object $course = null): array { - global $DB; - - $params = ['userid' => $user->id]; - - if (!empty($course)) { - $params['course'] = $course->id; - } - - return $DB->get_records('block_sharing_cart', $params); - } - - /** - * @param object $course - * @param int $section - * @param array $properties Record properties - * @param array $options - * @return object - */ - private function create_assignment(object $course, int $section = 0, array $properties = [], array $options = []): object { - $properties['course'] = $course->id; - $properties['section'] = $section; - return self::getDataGenerator()->create_module('assign', $properties, $options); - } -} \ No newline at end of file diff --git a/tests/integration/repositories/course_repository_test.php b/tests/integration/repositories/course_repository_test.php deleted file mode 100644 index 7525675..0000000 --- a/tests/integration/repositories/course_repository_test.php +++ /dev/null @@ -1,72 +0,0 @@ -create_user(); - self::setUser($user); - - $course1 = $this->create_course(); - $course2 = $this->create_course(); - - $item1 = $this->create_sharing_chart_record($user, $course1); - $item2 = $this->create_sharing_chart_record($user, $course2); - $items = [ - (int)$item1->id => $item1, - (int)$item2->id => $item2, - ]; - - $repo = new course_repository($this->db()); - $actual_course_fullnames = $repo->get_course_fullnames_by_sharing_carts($items); - $expect_course_fullnames = [ - (int)$course1->id => $course1->fullname, - (int)$course2->id => $course2->fullname, - ]; - - natsort($actual_course_fullnames); - natsort($expect_course_fullnames); - - $this->assertSame($expect_course_fullnames, $actual_course_fullnames); - } - - /** - * @throws coding_exception - * @throws dml_exception - */ - public function test_get_course_fullnames_from_empty_entity_list() { - $user = $this->create_user(); - self::setUser($user); - - $this->create_course(); - $this->create_course(); - - $repo = new course_repository($this->db()); - $actual_course_fullnames = $repo->get_course_fullnames_by_sharing_carts([]); - - $this->assertEmpty($actual_course_fullnames); - } -} diff --git a/tests/integration/storage/storage_test.php b/tests/integration/storage/storage_test.php deleted file mode 100644 index 39b0ff9..0000000 --- a/tests/integration/storage/storage_test.php +++ /dev/null @@ -1,122 +0,0 @@ -resetAfterTest(); - } - - public function test_get_non_existent_file_expect_cannot_find_file_exception(): void { - $this->expectException(cannot_find_file_exception::class); - - $user = self::getDataGenerator()->create_user(); - $filename = $this->get_unique_filename_by_user_id($user->id); - $store = new storage($user->id); - $store->get($filename); - } - - public function test_get_stored_file(): void { - $user = self::getDataGenerator()->create_user(); - $content = 'This is a text in the test file'; - $record = $this->create_file_record_by_user_id($user->id); - $record->filename = $this->get_unique_filename_by_user_id($user->id); - - $file_storage = get_file_storage(); - $expected_file = $file_storage->create_file_from_string( - $record, - $content - ); - - $storage = new storage($user->id); - $actual_file = $storage->get($record->filename); - - $this->assertSame((int)$expected_file->get_id(), (int)$actual_file->get_id()); - $this->assertSame($content, $actual_file->get_content()); - $this->assertSame($record->filename, $actual_file->get_filename()); - } - - /** - * @param int $user_id - * @return string - * @throws dml_exception - * @throws Exception - */ - private function get_unique_filename_by_user_id(int $user_id): string { - $filenames = $this->get_filenames_by_user_id($user_id); - $occupied_names = array_flip($filenames); - - do { - $name = hash('md5', random_bytes(16)); - if ($name === false) { - throw new Exception('Unable to randomize the filename with md5 hash function'); - } - } - while (isset($occupied_names[$name])); - - return $name; - } - - /** - * @param int $id - * @return string[] - * @throws dml_exception - */ - private function get_filenames_by_user_id(int $id): array { - $sql = "contextid = :contextid - AND userid = :userid - AND component = :component - AND filearea = :filearea - AND itemid = :itemid - AND filepath = :filepath"; - - $params = (array)$this->create_file_record_by_user_id($id); - - return $this->db()->get_fieldset_select( - 'files', - 'filename', - $sql, - $params - ); - } - - /** - * @param int $id - * @return object - */ - private function create_file_record_by_user_id(int $id): object { - return (object)[ - 'contextid' => context_user::instance($id)->id, - 'userid' => $id, - 'component' => storage::COMPONENT, - 'filearea' => storage::FILEAREA, - 'itemid' => storage::ITEMID, - 'filepath' => storage::FILEPATH, - ]; - } - - /** - * @return moodle_database - */ - private function db(): moodle_database { - global $DB; - return $DB; - } -} diff --git a/version.php b/version.php index 1c12b6d..d6dc2b1 100644 --- a/version.php +++ b/version.php @@ -1,28 +1,8 @@ . -/** - * Sharing Cart - * - * @package block_sharing_cart - * @copyright 2024 (c) Don Hinkelman, moxis and others - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die; +// @codeCoverageIgnoreStart +defined('MOODLE_INTERNAL') || die(); +// @codeCoverageIgnoreEnd /** @var object $plugin */ $plugin->component = 'block_sharing_cart'; From 3285054731a806f0ff15fbfca921e3664cb09f71 Mon Sep 17 00:00:00 2001 From: Frederik Milling Pytlick Date: Tue, 12 Mar 2024 14:12:14 +0100 Subject: [PATCH 03/43] #133 - Started working on the frontend --- amd/build/app/block/course/element.min.js | 3 + amd/build/app/block/course/element.min.js.map | 1 + amd/build/app/block/course/factory.min.js | 3 + amd/build/app/block/course/factory.min.js.map | 1 + amd/build/app/block/element.min.js | 3 + amd/build/app/block/element.min.js.map | 1 + amd/build/app/block/event_handler.min.js | 3 + amd/build/app/block/event_handler.min.js.map | 1 + amd/build/app/block/factory.min.js | 3 + amd/build/app/block/factory.min.js.map | 1 + amd/build/app/block/item.min.js | 3 + amd/build/app/block/item.min.js.map | 1 + amd/build/app/block/item/element.min.js | 3 + amd/build/app/block/item/element.min.js.map | 1 + amd/build/app/block/item/factory.min.js | 3 + amd/build/app/block/item/factory.min.js.map | 1 + amd/build/app/factory.min.js | 3 + amd/build/app/factory.min.js.map | 1 + amd/build/block.min.js | 3 + amd/build/block.min.js.map | 1 + amd/src/app/block/course/element.js | 158 ++++++++++++++++++ amd/src/app/block/course/factory.js | 26 +++ amd/src/app/block/element.js | 83 +++++++++ amd/src/app/block/event_handler.js | 26 +++ amd/src/app/block/factory.js | 45 +++++ amd/src/app/block/item/element.js | 146 ++++++++++++++++ amd/src/app/block/item/factory.js | 26 +++ amd/src/app/factory.js | 17 ++ amd/src/block.js | 5 + block_sharing_cart.php | 1 + classes/output/block/content.php | 66 ++++---- db/install.xml | 6 +- lang/en/block_sharing_cart.php | 69 ++++---- pix/bulkdelete.svg | 10 -- pix/dropzone_arrow.svg | 18 -- style/scss/block/content.scss | 83 ++++++++- style/style.css | 84 +++++++++- style/style.css.map | 2 +- .../add_to_sharing_cart_button.mustache | 1 + templates/block/course/clipboard.mustache | 5 + .../block/course/clipboard_target.mustache | 3 + templates/block/item.mustache | 16 +- templates/block/item/icon.mustache | 9 + 43 files changed, 840 insertions(+), 105 deletions(-) create mode 100644 amd/build/app/block/course/element.min.js create mode 100644 amd/build/app/block/course/element.min.js.map create mode 100644 amd/build/app/block/course/factory.min.js create mode 100644 amd/build/app/block/course/factory.min.js.map create mode 100644 amd/build/app/block/element.min.js create mode 100644 amd/build/app/block/element.min.js.map create mode 100644 amd/build/app/block/event_handler.min.js create mode 100644 amd/build/app/block/event_handler.min.js.map create mode 100644 amd/build/app/block/factory.min.js create mode 100644 amd/build/app/block/factory.min.js.map create mode 100644 amd/build/app/block/item.min.js create mode 100644 amd/build/app/block/item.min.js.map create mode 100644 amd/build/app/block/item/element.min.js create mode 100644 amd/build/app/block/item/element.min.js.map create mode 100644 amd/build/app/block/item/factory.min.js create mode 100644 amd/build/app/block/item/factory.min.js.map create mode 100644 amd/build/app/factory.min.js create mode 100644 amd/build/app/factory.min.js.map create mode 100644 amd/build/block.min.js create mode 100644 amd/build/block.min.js.map create mode 100644 amd/src/app/block/course/element.js create mode 100644 amd/src/app/block/course/factory.js create mode 100644 amd/src/app/block/element.js create mode 100644 amd/src/app/block/event_handler.js create mode 100644 amd/src/app/block/factory.js create mode 100644 amd/src/app/block/item/element.js create mode 100644 amd/src/app/block/item/factory.js create mode 100644 amd/src/app/factory.js create mode 100644 amd/src/block.js delete mode 100644 pix/bulkdelete.svg delete mode 100644 pix/dropzone_arrow.svg create mode 100644 templates/block/course/add_to_sharing_cart_button.mustache create mode 100644 templates/block/course/clipboard.mustache create mode 100644 templates/block/course/clipboard_target.mustache create mode 100644 templates/block/item/icon.mustache diff --git a/amd/build/app/block/course/element.min.js b/amd/build/app/block/course/element.min.js new file mode 100644 index 0000000..f9afb31 --- /dev/null +++ b/amd/build/app/block/course/element.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/course/element",["exports","../factory","core/templates"],(function(_exports,_factory,_templates){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=_interopRequireDefault(_factory),_templates=_interopRequireDefault(_templates);var _baseFactory=new WeakMap,_blockElement=new WeakMap,_element=new WeakMap,_clipboard=new WeakMap;return _exports.default=class{constructor(baseFactory,blockElement,element){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_blockElement,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_element,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_clipboard,{writable:!0,value:null}),_classPrivateFieldSet(this,_baseFactory,baseFactory),_classPrivateFieldSet(this,_blockElement,blockElement),_classPrivateFieldSet(this,_element,element)}async addBackupToSharingCartButtons(){let element=document.createElement("div");const{html:html,js:js}=await new Promise(((resolve,reject)=>{_templates.default.render("block_sharing_cart/block/course/add_to_sharing_cart_button",{}).then((async(html,js)=>{resolve({html:html,js:js})})).fail(reject)}));element=await _templates.default.replaceNode(element,html,js)[0];_classPrivateFieldGet(this,_element).querySelectorAll(".course-section-header .inplaceeditable").forEach((sectionTitle=>{const button=element.cloneNode(!0);sectionTitle.after(button)})),_classPrivateFieldGet(this,_element).querySelectorAll(".cm_action_menu").forEach((courseModuleActionMenu=>{const button=element.cloneNode(!0);courseModuleActionMenu.append(button)}))}async renderClipboard(){_classPrivateFieldSet(this,_clipboard,document.createElement("div"));const{html:html,js:js}=await new Promise(((resolve,reject)=>{_templates.default.render("block_sharing_cart/block/course/clipboard",{}).then((async(html,js)=>{resolve({html:html,js:js})})).fail(reject)}));_classPrivateFieldSet(this,_clipboard,await _templates.default.replaceNode(_classPrivateFieldGet(this,_clipboard),html,js)[0]),_classPrivateFieldGet(this,_element).prepend(_classPrivateFieldGet(this,_clipboard)),_classPrivateFieldGet(this,_clipboard).querySelector('[data-action="clear-clipboard"]').addEventListener("click",this.clearClipboard.bind(this))}clearClipboard(e){e.preventDefault(),e.stopPropagation(),_classPrivateFieldGet(this,_clipboard).classList.add("d-none"),this.clearClipboardTargets(),_classPrivateFieldGet(this,_blockElement).clearClipboard()}updateClipboard(item){_classPrivateFieldGet(this,_clipboard).classList.add("d-none");_classPrivateFieldGet(this,_clipboard).querySelector(".info").innerHTML=item.getItemInfo().innerHTML,_classPrivateFieldGet(this,_clipboard).classList.remove("d-none")}async updateClipboardTargets(item){let element=document.createElement("div");const{html:html,js:js}=await new Promise(((resolve,reject)=>{_templates.default.render("block_sharing_cart/block/course/clipboard_target",{}).then((async(html,js)=>{resolve({html:html,js:js})})).fail(reject)}));element=await _templates.default.replaceNode(element,html,js)[0],_classPrivateFieldGet(this,_element).querySelectorAll('[data-for="cmlist"]').forEach((section=>{var _section$querySelecto;const clipboardTarget=null!==(_section$querySelecto=section.querySelector(".clipboard_target"))&&void 0!==_section$querySelecto?_section$querySelecto:element.cloneNode(!0);section.prepend(clipboardTarget)}))}getClipboardTargets(){return _classPrivateFieldGet(this,_element).querySelectorAll(".clipboard_target")}clearClipboardTargets(){this.getClipboardTargets().forEach((target=>{target.remove()}))}async setClipboard(item){_classPrivateFieldGet(this,_clipboard)||await this.renderClipboard(),this.updateClipboard(item),await this.updateClipboardTargets(item)}},_exports.default})); + +//# sourceMappingURL=element.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/course/element.min.js.map b/amd/build/app/block/course/element.min.js.map new file mode 100644 index 0000000..ac2235e --- /dev/null +++ b/amd/build/app/block/course/element.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"element.min.js","sources":["../../../../src/app/block/course/element.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\nimport Templates from \"core/templates\";\n\nexport default class CourseElement {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @type {BlockElement}\n */\n #blockElement;\n\n /**\n * @type {HTMLElement}\n */\n #element;\n\n /**\n * @type {HTMLElement|null}\n */\n #clipboard = null;\n\n constructor(baseFactory, blockElement, element) {\n this.#baseFactory = baseFactory;\n this.#blockElement = blockElement;\n this.#element = element;\n }\n\n async addBackupToSharingCartButtons() {\n let element = document.createElement('div');\n const {html, js} = await new Promise((resolve, reject) => {\n Templates.render('block_sharing_cart/block/course/add_to_sharing_cart_button', {})\n .then(async (html, js) => {\n resolve({\n html,\n js\n });\n }).fail(reject);\n });\n element = await Templates.replaceNode(\n element,\n html,\n js\n )[0];\n\n const sectionTitles = this.#element.querySelectorAll('.course-section-header .inplaceeditable');\n sectionTitles.forEach((sectionTitle) => {\n const button = element.cloneNode(true);\n\n sectionTitle.after(button);\n });\n\n this.#element.querySelectorAll('.cm_action_menu').forEach((courseModuleActionMenu) => {\n const button = element.cloneNode(true);\n\n courseModuleActionMenu.append(button);\n });\n }\n\n async renderClipboard() {\n this.#clipboard = document.createElement('div');\n\n const {html, js} = await new Promise((resolve, reject) => {\n Templates.render('block_sharing_cart/block/course/clipboard', {})\n .then(async (html, js) => {\n resolve({\n html,\n js\n });\n }).fail(reject);\n });\n this.#clipboard = await Templates.replaceNode(\n this.#clipboard,\n html,\n js\n )[0];\n\n this.#element.prepend(this.#clipboard);\n this.#clipboard.querySelector('[data-action=\"clear-clipboard\"]').addEventListener('click', this.clearClipboard.bind(this));\n }\n\n /**\n * @param {Event} e\n */\n clearClipboard(e) {\n e.preventDefault();\n e.stopPropagation();\n\n this.#clipboard.classList.add('d-none');\n this.clearClipboardTargets();\n\n this.#blockElement.clearClipboard();\n }\n\n /**\n * @param {ItemElement} item\n */\n updateClipboard(item) {\n this.#clipboard.classList.add('d-none');\n\n const clipboardItemInfo = this.#clipboard.querySelector('.info');\n clipboardItemInfo.innerHTML = item.getItemInfo().innerHTML;\n\n this.#clipboard.classList.remove('d-none');\n }\n\n /**\n * @param {ItemElement} item\n */\n async updateClipboardTargets(item) {\n let element = document.createElement('div');\n const {html, js} = await new Promise((resolve, reject) => {\n Templates.render('block_sharing_cart/block/course/clipboard_target', {})\n .then(async (html, js) => {\n resolve({\n html,\n js\n });\n }).fail(reject);\n });\n element = await Templates.replaceNode(\n element,\n html,\n js\n )[0];\n\n this.#element.querySelectorAll('[data-for=\"cmlist\"]').forEach((section) => {\n const clipboardTarget = section.querySelector('.clipboard_target') ?? element.cloneNode(true);\n\n section.prepend(clipboardTarget);\n });\n }\n\n getClipboardTargets() {\n return this.#element.querySelectorAll('.clipboard_target');\n }\n\n clearClipboardTargets() {\n this.getClipboardTargets().forEach((target) => {\n target.remove();\n });\n }\n\n /**\n * @param {ItemElement} item\n */\n async setClipboard(item) {\n if (!this.#clipboard) {\n await this.renderClipboard();\n }\n\n this.updateClipboard(item);\n await this.updateClipboardTargets(item);\n }\n}"],"names":["constructor","baseFactory","blockElement","element","document","createElement","html","js","Promise","resolve","reject","render","then","async","fail","Templates","replaceNode","_classPrivateFieldGet","querySelectorAll","forEach","sectionTitle","button","cloneNode","after","courseModuleActionMenu","append","this","prepend","querySelector","addEventListener","clearClipboard","bind","e","preventDefault","stopPropagation","classList","add","clearClipboardTargets","updateClipboard","item","innerHTML","getItemInfo","remove","section","clipboardTarget","getClipboardTargets","target","renderClipboard","updateClipboardTargets"],"mappings":"khDAyBIA,YAAYC,YAAaC,aAAcC,+RAF1B,+CAGWF,sDACCC,kDACLC,mDAIZA,QAAUC,SAASC,cAAc,aAC/BC,KAACA,KAADC,GAAOA,UAAY,IAAIC,SAAQ,CAACC,QAASC,6BACjCC,OAAO,6DAA8D,IAC1EC,MAAKC,MAAOP,KAAMC,MACfE,QAAQ,CACJH,KAAAA,KACAC,GAAAA,QAELO,KAAKJ,WAEhBP,cAAgBY,mBAAUC,YACtBb,QACAG,KACAC,IACF,GAEoBU,qCAAcC,iBAAiB,2CACvCC,SAASC,qBACbC,OAASlB,QAAQmB,WAAU,GAEjCF,aAAaG,MAAMF,gDAGTH,iBAAiB,mBAAmBC,SAASK,+BACjDH,OAASlB,QAAQmB,WAAU,GAEjCE,uBAAuBC,OAAOJ,yEAKhBjB,SAASC,cAAc,cAEnCC,KAACA,KAADC,GAAOA,UAAY,IAAIC,SAAQ,CAACC,QAASC,6BACjCC,OAAO,4CAA6C,IACzDC,MAAKC,MAAOP,KAAMC,MACfE,QAAQ,CACJH,KAAAA,KACAC,GAAAA,QAELO,KAAKJ,uDAEQK,mBAAUC,kCAC9BU,iBACApB,KACAC,IACF,yCAEYoB,8BAAQD,yDACNE,cAAc,mCAAmCC,iBAAiB,QAASH,KAAKI,eAAeC,KAAKL,OAMxHI,eAAeE,GACXA,EAAEC,iBACFD,EAAEE,yDAEcC,UAAUC,IAAI,eACzBC,kEAEcP,iBAMvBQ,gBAAgBC,6CACIJ,UAAUC,IAAI,UAEJnB,uCAAgBW,cAAc,SACtCY,UAAYD,KAAKE,cAAcD,iDAEjCL,UAAUO,OAAO,uCAMRH,UACrBpC,QAAUC,SAASC,cAAc,aAC/BC,KAACA,KAADC,GAAOA,UAAY,IAAIC,SAAQ,CAACC,QAASC,6BACjCC,OAAO,mDAAoD,IAChEC,MAAKC,MAAOP,KAAMC,MACfE,QAAQ,CACJH,KAAAA,KACAC,GAAAA,QAELO,KAAKJ,WAEhBP,cAAgBY,mBAAUC,YACtBb,QACAG,KACAC,IACF,wCAEYW,iBAAiB,uBAAuBC,SAASwB,0CACrDC,8CAAkBD,QAAQf,cAAc,4EAAwBzB,QAAQmB,WAAU,GAExFqB,QAAQhB,QAAQiB,oBAIxBC,6BACW5B,qCAAcC,iBAAiB,qBAG1CmB,6BACSQ,sBAAsB1B,SAAS2B,SAChCA,OAAOJ,+BAOIH,4BACVb,wBACKA,KAAKqB,uBAGVT,gBAAgBC,YACfb,KAAKsB,uBAAuBT"} \ No newline at end of file diff --git a/amd/build/app/block/course/factory.min.js b/amd/build/app/block/course/factory.min.js new file mode 100644 index 0000000..0218dae --- /dev/null +++ b/amd/build/app/block/course/factory.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/course/factory",["exports","../factory","./element"],(function(_exports,_factory,_element){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=_interopRequireDefault(_factory),_element=_interopRequireDefault(_element);var _baseFactory=new WeakMap;return _exports.default=class{constructor(baseFactory){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_baseFactory,baseFactory)}element(blockElement,element){return new _element.default(function(receiver,descriptor){return descriptor.get?descriptor.get.call(receiver):descriptor.value}(receiver=this,_classExtractFieldDescriptor(receiver,_baseFactory,"get")),blockElement,element);var receiver}},_exports.default})); + +//# sourceMappingURL=factory.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/course/factory.min.js.map b/amd/build/app/block/course/factory.min.js.map new file mode 100644 index 0000000..629b934 --- /dev/null +++ b/amd/build/app/block/course/factory.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"factory.min.js","sources":["../../../../src/app/block/course/factory.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\nimport CourseElement from \"./element\";\n\nexport default class Factory {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @param {BaseFactory} baseFactory\n */\n constructor(baseFactory) {\n this.#baseFactory = baseFactory;\n }\n\n /**\n * @param {BlockElement} blockElement\n * @param {HTMLElement} element\n * @returns {CourseElement}\n */\n element(blockElement, element) {\n return new CourseElement(this.#baseFactory, blockElement, element);\n }\n}"],"names":["constructor","baseFactory","element","blockElement","CourseElement","this"],"mappings":"stCAaIA,YAAYC,8HACYA,aAQxBC,QAAQC,aAAcD,gBACX,IAAIE,8HAAcC,gEAAmBF,aAAcD"} \ No newline at end of file diff --git a/amd/build/app/block/element.min.js b/amd/build/app/block/element.min.js new file mode 100644 index 0000000..4761392 --- /dev/null +++ b/amd/build/app/block/element.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/element",["exports","../factory"],(function(_exports,_factory){var obj;function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=(obj=_factory)&&obj.__esModule?obj:{default:obj};var _baseFactory=new WeakMap,_element=new WeakMap,_course=new WeakMap,_items=new WeakMap,_clipboardItem=new WeakMap;return _exports.default=class{constructor(baseFactory,element){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_element,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_course,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_items,{writable:!0,value:[]}),_classPrivateFieldInitSpec(this,_clipboardItem,{writable:!0,value:null}),_classPrivateFieldSet(this,_baseFactory,baseFactory),_classPrivateFieldSet(this,_element,element)}addEventListeners(){this.setupCourse(),this.setupItems()}setupCourse(){const course=document.querySelector(".course-content"),courseElement=_classPrivateFieldGet(this,_baseFactory).blockFactory().course().element(this,course);courseElement.addBackupToSharingCartButtons(),_classPrivateFieldSet(this,_course,courseElement)}setupItems(){_classPrivateFieldGet(this,_element).querySelectorAll(".sharing_cart_item").forEach((element=>{const itemElement=_classPrivateFieldGet(this,_baseFactory).blockFactory().item().element(this,element);itemElement.addEventListeners(),_classPrivateFieldGet(this,_items).push(itemElement)}))}deleteItem(item){const index=_classPrivateFieldGet(this,_items).findIndex((i=>i.getItemId()===item.getItemId()));_classPrivateFieldGet(this,_items).splice(index,1),item.remove()}async setClipboard(item){_classPrivateFieldSet(this,_clipboardItem,item),await _classPrivateFieldGet(this,_course).setClipboard(item)}clearClipboard(){_classPrivateFieldSet(this,_clipboardItem,null)}},_exports.default})); + +//# sourceMappingURL=element.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/element.min.js.map b/amd/build/app/block/element.min.js.map new file mode 100644 index 0000000..3fde94c --- /dev/null +++ b/amd/build/app/block/element.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"element.min.js","sources":["../../../src/app/block/element.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\n\nexport default class BlockElement {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @type {HTMLElement}\n */\n #element;\n\n /**\n * @type {CourseElement}\n */\n #course;\n\n /**\n * @type {ItemElement[]}\n */\n #items = [];\n\n /**\n * @type {ItemElement|NULL}\n */\n #clipboardItem = null;\n constructor(baseFactory, element) {\n this.#baseFactory = baseFactory;\n this.#element = element;\n }\n\n addEventListeners() {\n this.setupCourse();\n this.setupItems();\n }\n\n setupCourse() {\n const course = document.querySelector('.course-content');\n\n const courseElement = this.#baseFactory.blockFactory().course().element(this, course);\n courseElement.addBackupToSharingCartButtons();\n\n this.#course = courseElement;\n }\n setupItems() {\n const items = this.#element.querySelectorAll('.sharing_cart_item');\n\n items.forEach((element) => {\n const itemElement = this.#baseFactory.blockFactory().item().element(this, element);\n itemElement.addEventListeners();\n\n this.#items.push(\n itemElement\n );\n });\n }\n\n /**\n * @param {ItemElement} item\n */\n deleteItem(item) {\n // TODO: Do web service call to delete item\n\n const index = this.#items.findIndex((i) => i.getItemId() === item.getItemId());\n this.#items.splice(index, 1);\n item.remove();\n }\n\n /**\n * @param {ItemElement} item\n */\n async setClipboard(item) {\n this.#clipboardItem = item;\n\n await this.#course.setClipboard(item);\n }\n\n clearClipboard() {\n this.#clipboardItem = null;\n }\n}"],"names":["constructor","baseFactory","element","addEventListeners","setupCourse","setupItems","course","document","querySelector","courseElement","_classPrivateFieldGet","blockFactory","this","addBackupToSharingCartButtons","querySelectorAll","forEach","itemElement","item","push","deleteItem","index","findIndex","i","getItemId","splice","remove","setClipboard","clearClipboard"],"mappings":"w5CA4BIA,YAAYC,YAAaC,qRANhB,uEAKQ,+CAEOD,iDACJC,SAGpBC,yBACSC,mBACAC,aAGTD,oBACUE,OAASC,SAASC,cAAc,mBAEhCC,cAAgBC,yCAAkBC,eAAeL,SAASJ,QAAQU,KAAMN,QAC9EG,cAAcI,mEAECJ,eAEnBJ,aACkBK,qCAAcI,iBAAiB,sBAEvCC,SAASb,gBACLc,YAAcN,yCAAkBC,eAAeM,OAAOf,QAAQU,KAAMV,SAC1Ec,YAAYb,uDAEAe,KACRF,gBAQZG,WAAWF,YAGDG,MAAQV,mCAAYW,WAAWC,GAAMA,EAAEC,cAAgBN,KAAKM,iDACtDC,OAAOJ,MAAO,GAC1BH,KAAKQ,4BAMUR,gDACOA,YAEhBP,oCAAagB,aAAaT,MAGpCU,2DAC0B"} \ No newline at end of file diff --git a/amd/build/app/block/event_handler.min.js b/amd/build/app/block/event_handler.min.js new file mode 100644 index 0000000..6c9fabf --- /dev/null +++ b/amd/build/app/block/event_handler.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/event_handler",["exports","../factory"],(function(_exports,_factory){var obj;function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=(obj=_factory)&&obj.__esModule?obj:{default:obj};var _baseFactory=new WeakMap;return _exports.default=class{constructor(baseFactory){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_baseFactory,baseFactory)}onLoad(){this.setupBlock()}setupBlock(){const block=document.querySelector(".block.block_sharing_cart");var receiver,privateMap;(receiver=this,privateMap=_baseFactory,function(receiver,descriptor){return descriptor.get?descriptor.get.call(receiver):descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))).blockFactory().element(block).addEventListeners()}},_exports.default})); + +//# sourceMappingURL=event_handler.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/event_handler.min.js.map b/amd/build/app/block/event_handler.min.js.map new file mode 100644 index 0000000..985ae07 --- /dev/null +++ b/amd/build/app/block/event_handler.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event_handler.min.js","sources":["../../../src/app/block/event_handler.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\n\nexport default class EventHandler {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @param {BaseFactory} baseFactory\n */\n constructor(baseFactory) {\n this.#baseFactory = baseFactory;\n }\n\n onLoad() {\n this.setupBlock();\n }\n\n setupBlock() {\n const block = document.querySelector('.block.block_sharing_cart');\n\n this.#baseFactory.blockFactory().element(block).addEventListeners();\n }\n}\n"],"names":["constructor","baseFactory","onLoad","setupBlock","block","document","querySelector","blockFactory","element","addEventListeners"],"mappings":"4lCAYIA,YAAYC,8HACYA,aAGxBC,cACSC,aAGTA,mBACUC,MAAQC,SAASC,cAAc,mQAEnBC,eAAeC,QAAQJ,OAAOK"} \ No newline at end of file diff --git a/amd/build/app/block/factory.min.js b/amd/build/app/block/factory.min.js new file mode 100644 index 0000000..dca2dd0 --- /dev/null +++ b/amd/build/app/block/factory.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/factory",["exports","../factory","./event_handler","./course/factory","./item/factory","./element"],(function(_exports,_factory,_event_handler,_factory2,_factory3,_element){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=_interopRequireDefault(_factory),_event_handler=_interopRequireDefault(_event_handler),_factory2=_interopRequireDefault(_factory2),_factory3=_interopRequireDefault(_factory3),_element=_interopRequireDefault(_element);var _baseFactory=new WeakMap;return _exports.default=class{constructor(baseFactory){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_baseFactory,baseFactory)}eventHandler(){return new _event_handler.default(_classPrivateFieldGet(this,_baseFactory))}course(){return new _factory2.default(_classPrivateFieldGet(this,_baseFactory))}item(){return new _factory3.default(_classPrivateFieldGet(this,_baseFactory))}element(element){return new _element.default(_classPrivateFieldGet(this,_baseFactory),element)}},_exports.default})); + +//# sourceMappingURL=factory.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/factory.min.js.map b/amd/build/app/block/factory.min.js.map new file mode 100644 index 0000000..2e7bacb --- /dev/null +++ b/amd/build/app/block/factory.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"factory.min.js","sources":["../../../src/app/block/factory.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\nimport EventHandler from \"./event_handler\";\nimport CourseFactory from \"./course/factory\";\nimport ItemFactory from \"./item/factory\";\nimport BlockElement from \"./element\";\n\nexport default class Factory {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @param {BaseFactory} baseFactory\n */\n constructor(baseFactory) {\n this.#baseFactory = baseFactory;\n }\n\n /**\n * @returns {EventHandler}\n */\n eventHandler() {\n return new EventHandler(this.#baseFactory);\n }\n\n /**\n * @returns {CourseFactory}\n */\n course() {\n return new CourseFactory(this.#baseFactory);\n }\n\n /**\n * @returns {ItemFactory}\n */\n item() {\n return new ItemFactory(this.#baseFactory);\n }\n\n element(element) {\n return new BlockElement(this.#baseFactory, element);\n }\n}"],"names":["constructor","baseFactory","eventHandler","EventHandler","this","course","CourseFactory","item","ItemFactory","element","BlockElement"],"mappings":"iqDAgBIA,YAAYC,8HACYA,aAMxBC,sBACW,IAAIC,6CAAaC,oBAM5BC,gBACW,IAAIC,wCAAcF,oBAM7BG,cACW,IAAIC,wCAAYJ,oBAG3BK,QAAQA,gBACG,IAAIC,uCAAaN,mBAAmBK"} \ No newline at end of file diff --git a/amd/build/app/block/item.min.js b/amd/build/app/block/item.min.js new file mode 100644 index 0000000..980dc2d --- /dev/null +++ b/amd/build/app/block/item.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/item",["exports","../factory"],(function(_exports,_factory){var obj;function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=(obj=_factory)&&obj.__esModule?obj:{default:obj};var _baseFactory=new WeakMap,_element=new WeakMap;return _exports.default=class{constructor(baseFactory,element){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_element,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_baseFactory,baseFactory),_classPrivateFieldSet(this,_element,element)}addEventListeners(){"course"!==_classPrivateFieldGet(this,_element).dataset.type&&"section"!==_classPrivateFieldGet(this,_element).dataset.type||_classPrivateFieldGet(this,_element).addEventListener("click",this.toggleCollapseRecursively.bind(this))}getItemChildrenRecursively(){return _classPrivateFieldGet(this,_element).querySelectorAll(".sharing_cart_item")}toggleCollapse(item){let collapse=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if("course"!==item.dataset.type&&"section"!==item.dataset.type)return;item.dataset.collapsed=null!==collapse?collapse?"true":"false":"true"===item.dataset.collapsed?"false":"true";const iconElement=item.querySelector(".info > i");iconElement.classList.remove("fa-folder-o","fa-folder-open-o"),iconElement.classList.add("true"===item.dataset.collapsed?"fa-folder-o":"fa-folder-open-o")}toggleCollapseRecursively(e){e.preventDefault(),e.stopPropagation(),this.toggleCollapse(_classPrivateFieldGet(this,_element)),this.getItemChildrenRecursively().forEach((item=>{this.toggleCollapse(item,"true"===_classPrivateFieldGet(this,_element).dataset.collapsed)}))}},_exports.default})); + +//# sourceMappingURL=item.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/item.min.js.map b/amd/build/app/block/item.min.js.map new file mode 100644 index 0000000..fdcaace --- /dev/null +++ b/amd/build/app/block/item.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"item.min.js","sources":["../../../src/app/block/item.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\n\nexport default class Item {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @type {HTMLElement}\n */\n #element;\n\n constructor(baseFactory, element) {\n this.#baseFactory = baseFactory;\n this.#element = element;\n }\n\n addEventListeners() {\n if(this.#element.dataset.type === 'course' || this.#element.dataset.type === 'section') {\n this.#element.addEventListener('click', this.toggleCollapseRecursively.bind(this));\n }\n }\n\n /**\n * @return {NodeListOf}\n */\n getItemChildrenRecursively() {\n return this.#element.querySelectorAll('.sharing_cart_item');\n }\n\n /**\n * @param {HTMLElement} item\n * @param {Boolean|NULL} collapse\n */\n toggleCollapse(item, collapse = null) {\n if (item.dataset.type !== 'course' && item.dataset.type !== 'section') {\n return;\n }\n\n if (collapse !== null) {\n item.dataset.collapsed = collapse ? 'true' : 'false';\n } else {\n item.dataset.collapsed = item.dataset.collapsed === 'true' ? 'false' : 'true';\n }\n\n const iconElement = item.querySelector('.info > i');\n iconElement.classList.remove('fa-folder-o', 'fa-folder-open-o');\n iconElement.classList.add(item.dataset.collapsed === 'true' ? 'fa-folder-o' : 'fa-folder-open-o');\n }\n\n /**\n * @param {Event} e\n */\n toggleCollapseRecursively(e) {\n e.preventDefault();\n e.stopPropagation();\n\n this.toggleCollapse(this.#element);\n this.getItemChildrenRecursively().forEach((item) => {\n this.toggleCollapse(item, this.#element.dataset.collapsed === 'true');\n });\n }\n}"],"names":["constructor","baseFactory","element","addEventListeners","_classPrivateFieldGet","dataset","type","addEventListener","this","toggleCollapseRecursively","bind","getItemChildrenRecursively","querySelectorAll","toggleCollapse","item","collapse","collapsed","iconElement","querySelector","classList","remove","add","e","preventDefault","stopPropagation","forEach"],"mappings":"m1CAcIA,YAAYC,YAAaC,+LACDD,iDACJC,SAGpBC,oBACsC,WAA/BC,qCAAcC,QAAQC,MAAoD,YAA/BF,qCAAcC,QAAQC,2CAClDC,iBAAiB,QAASC,KAAKC,0BAA0BC,KAAKF,OAOpFG,oCACWP,qCAAcQ,iBAAiB,sBAO1CC,eAAeC,UAAMC,gEAAW,QACF,WAAtBD,KAAKT,QAAQC,MAA2C,YAAtBQ,KAAKT,QAAQC,YAK/CQ,KAAKT,QAAQW,UADA,OAAbD,SACyBA,SAAW,OAAS,QAEO,SAA3BD,KAAKT,QAAQW,UAAuB,QAAU,aAGrEC,YAAcH,KAAKI,cAAc,aACvCD,YAAYE,UAAUC,OAAO,cAAe,oBAC5CH,YAAYE,UAAUE,IAA+B,SAA3BP,KAAKT,QAAQW,UAAuB,cAAgB,oBAMlFP,0BAA0Ba,GACtBA,EAAEC,iBACFD,EAAEE,uBAEGX,qCAAeL,qBACfG,6BAA6Bc,SAASX,YAClCD,eAAeC,KAA0C,SAApCV,qCAAcC,QAAQW"} \ No newline at end of file diff --git a/amd/build/app/block/item/element.min.js b/amd/build/app/block/item/element.min.js new file mode 100644 index 0000000..4c835c2 --- /dev/null +++ b/amd/build/app/block/item/element.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/item/element",["exports","../factory","core/modal_factory","core/modal_events","core/str"],(function(_exports,_factory,_modal_factory,_modal_events,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldGet(receiver,privateMap){return function(receiver,descriptor){if(descriptor.get)return descriptor.get.call(receiver);return descriptor.value}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"get"))}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=_interopRequireDefault(_factory),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events);var _baseFactory=new WeakMap,_blockElement=new WeakMap,_element=new WeakMap;return _exports.default=class{constructor(baseFactory,blockElement,element){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_blockElement,{writable:!0,value:void 0}),_classPrivateFieldInitSpec(this,_element,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_baseFactory,baseFactory),_classPrivateFieldSet(this,_blockElement,blockElement),_classPrivateFieldSet(this,_element,element)}addEventListeners(){var _classPrivateFieldGet2,_classPrivateFieldGet3;"course"!==_classPrivateFieldGet(this,_element).dataset.type&&"section"!==_classPrivateFieldGet(this,_element).dataset.type||_classPrivateFieldGet(this,_element).addEventListener("click",this.toggleCollapseRecursively.bind(this)),null===(_classPrivateFieldGet2=_classPrivateFieldGet(this,_element).querySelector('[data-action="delete"]'))||void 0===_classPrivateFieldGet2||_classPrivateFieldGet2.addEventListener("click",this.confirmDeleteItem.bind(this)),null===(_classPrivateFieldGet3=_classPrivateFieldGet(this,_element).querySelector('[data-action="copy_to_course"]'))||void 0===_classPrivateFieldGet3||_classPrivateFieldGet3.addEventListener("click",this.copyItemToCourse.bind(this))}async copyItemToCourse(e){e.preventDefault(),e.stopPropagation(),_classPrivateFieldGet(this,_blockElement).setClipboard(this)}async confirmDeleteItem(e){e.preventDefault(),e.stopPropagation();const strings=await(0,_str.get_strings)([{key:"delete_item",component:"block_sharing_cart"},{key:"confirm_delete_item",component:"block_sharing_cart"},{key:"delete",component:"core"},{key:"cancel",component:"core"}]),modal=await _modal_factory.default.create({type:_modal_factory.default.types.DELETE_CANCEL,title:strings[0]+': "'+this.getItemName()+'"',body:strings[1],buttons:{delete:strings[2],cancel:strings[3]},removeOnClose:!0});modal.getRoot().on(_modal_events.default.delete,_classPrivateFieldGet(this,_blockElement).deleteItem.bind(_classPrivateFieldGet(this,_blockElement),this)),await modal.show()}getItemChildrenRecursively(){return _classPrivateFieldGet(this,_element).querySelectorAll(".sharing_cart_item")}getItemName(){return _classPrivateFieldGet(this,_element).querySelector(".name").innerText}getItemId(){return Number.parseInt(_classPrivateFieldGet(this,_element).dataset.itemid)}getItemInfo(){return _classPrivateFieldGet(this,_element).querySelector(".info")}toggleCollapse(item){let collapse=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if("course"!==item.dataset.type&&"section"!==item.dataset.type)return;item.dataset.collapsed=null!==collapse?collapse?"true":"false":"true"===item.dataset.collapsed?"false":"true";const iconElement=item.querySelector(".info > i");iconElement.classList.remove("fa-folder-o","fa-folder-open-o"),iconElement.classList.add("true"===item.dataset.collapsed?"fa-folder-o":"fa-folder-open-o")}toggleCollapseRecursively(e){e.preventDefault(),e.stopPropagation(),this.toggleCollapse(_classPrivateFieldGet(this,_element)),this.getItemChildrenRecursively().forEach((item=>{this.toggleCollapse(item,"true"===_classPrivateFieldGet(this,_element).dataset.collapsed)}))}remove(){_classPrivateFieldGet(this,_element).remove()}},_exports.default})); + +//# sourceMappingURL=element.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/item/element.min.js.map b/amd/build/app/block/item/element.min.js.map new file mode 100644 index 0000000..2ea8eec --- /dev/null +++ b/amd/build/app/block/item/element.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"element.min.js","sources":["../../../../src/app/block/item/element.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport { get_strings } from \"core/str\";\n\nexport default class ItemElement {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @type {BlockElement}\n */\n #blockElement;\n\n /**\n * @type {HTMLElement}\n */\n #element;\n\n constructor(baseFactory, blockElement, element) {\n this.#baseFactory = baseFactory;\n this.#blockElement = blockElement;\n this.#element = element;\n }\n\n addEventListeners() {\n if(this.#element.dataset.type === 'course' || this.#element.dataset.type === 'section') {\n this.#element.addEventListener('click', this.toggleCollapseRecursively.bind(this));\n }\n\n this.#element.querySelector('[data-action=\"delete\"]')?.addEventListener('click', this.confirmDeleteItem.bind(this));\n this.#element.querySelector('[data-action=\"copy_to_course\"]')?.addEventListener('click', this.copyItemToCourse.bind(this));\n }\n\n async copyItemToCourse(e) {\n e.preventDefault();\n e.stopPropagation();\n\n this.#blockElement.setClipboard(this);\n }\n\n async confirmDeleteItem(e) {\n e.preventDefault();\n e.stopPropagation();\n\n const strings = await get_strings([\n {\n key: 'delete_item',\n component: 'block_sharing_cart',\n },\n {\n key: 'confirm_delete_item',\n component: 'block_sharing_cart',\n },\n {\n key: 'delete',\n component: 'core',\n },\n {\n key: 'cancel',\n component: 'core',\n }\n ]);\n\n const modal = await ModalFactory.create({\n type: ModalFactory.types.DELETE_CANCEL,\n title: strings[0] + ': \"' + this.getItemName() + '\"',\n body: strings[1],\n buttons: {\n delete: strings[2],\n cancel: strings[3],\n },\n removeOnClose: true,\n });\n modal.getRoot().on(ModalEvents.delete, this.#blockElement.deleteItem.bind(this.#blockElement, this));\n await modal.show();\n }\n\n /**\n * @return {NodeListOf}\n */\n getItemChildrenRecursively() {\n return this.#element.querySelectorAll('.sharing_cart_item');\n }\n\n /**\n * @return {String}\n */\n getItemName() {\n return this.#element.querySelector('.name').innerText;\n }\n\n /**\n * @return {Number}\n */\n getItemId() {\n return Number.parseInt(this.#element.dataset.itemid);\n }\n\n /**\n * @return {HTMLElement}\n */\n getItemInfo() {\n return this.#element.querySelector('.info');\n }\n\n /**\n * @param {HTMLElement} item\n * @param {Boolean|NULL} collapse\n */\n toggleCollapse(item, collapse = null) {\n if (item.dataset.type !== 'course' && item.dataset.type !== 'section') {\n return;\n }\n\n if (collapse !== null) {\n item.dataset.collapsed = collapse ? 'true' : 'false';\n } else {\n item.dataset.collapsed = item.dataset.collapsed === 'true' ? 'false' : 'true';\n }\n\n const iconElement = item.querySelector('.info > i');\n iconElement.classList.remove('fa-folder-o', 'fa-folder-open-o');\n iconElement.classList.add(item.dataset.collapsed === 'true' ? 'fa-folder-o' : 'fa-folder-open-o');\n }\n\n /**\n * @param {Event} e\n */\n toggleCollapseRecursively(e) {\n e.preventDefault();\n e.stopPropagation();\n\n this.toggleCollapse(this.#element);\n this.getItemChildrenRecursively().forEach((item) => {\n this.toggleCollapse(item, this.#element.dataset.collapsed === 'true');\n });\n }\n\n remove() {\n this.#element.remove();\n }\n}"],"names":["constructor","baseFactory","blockElement","element","addEventListeners","_classPrivateFieldGet","dataset","type","addEventListener","this","toggleCollapseRecursively","bind","querySelector","confirmDeleteItem","copyItemToCourse","e","preventDefault","stopPropagation","setClipboard","strings","key","component","modal","ModalFactory","create","types","DELETE_CANCEL","title","getItemName","body","buttons","delete","cancel","removeOnClose","getRoot","on","ModalEvents","deleteItem","show","getItemChildrenRecursively","querySelectorAll","innerText","getItemId","Number","parseInt","itemid","getItemInfo","toggleCollapse","item","collapse","collapsed","iconElement","classList","remove","add","forEach"],"mappings":"+mDAsBIA,YAAYC,YAAaC,aAAcC,yQACfF,sDACCC,kDACLC,SAGpBC,sEACsC,WAA/BC,qCAAcC,QAAQC,MAAoD,YAA/BF,qCAAcC,QAAQC,2CAClDC,iBAAiB,QAASC,KAAKC,0BAA0BC,KAAKF,2EAGlEG,cAAc,oFAA2BJ,iBAAiB,QAASC,KAAKI,kBAAkBF,KAAKF,2EAC/FG,cAAc,4FAAmCJ,iBAAiB,QAASC,KAAKK,iBAAiBH,KAAKF,8BAGjGM,GACnBA,EAAEC,iBACFD,EAAEE,4DAEiBC,aAAaT,8BAGZM,GACpBA,EAAEC,iBACFD,EAAEE,wBAEIE,cAAgB,oBAAY,CAC9B,CACIC,IAAK,cACLC,UAAW,sBAEf,CACID,IAAK,sBACLC,UAAW,sBAEf,CACID,IAAK,SACLC,UAAW,QAEf,CACID,IAAK,SACLC,UAAW,UAIbC,YAAcC,uBAAaC,OAAO,CACpCjB,KAAMgB,uBAAaE,MAAMC,cACzBC,MAAOR,QAAQ,GAAK,MAAQV,KAAKmB,cAAgB,IACjDC,KAAMV,QAAQ,GACdW,QAAS,CACLC,OAAQZ,QAAQ,GAChBa,OAAQb,QAAQ,IAEpBc,eAAe,IAEnBX,MAAMY,UAAUC,GAAGC,sBAAYL,OAAQ1B,0CAAmBgC,WAAW1B,2BAAKF,oBAAoBA,aACxFa,MAAMgB,OAMhBC,oCACWlC,qCAAcmC,iBAAiB,sBAM1CZ,qBACWvB,qCAAcO,cAAc,SAAS6B,UAMhDC,mBACWC,OAAOC,SAASvC,qCAAcC,QAAQuC,QAMjDC,qBACWzC,qCAAcO,cAAc,SAOvCmC,eAAeC,UAAMC,gEAAW,QACF,WAAtBD,KAAK1C,QAAQC,MAA2C,YAAtByC,KAAK1C,QAAQC,YAK/CyC,KAAK1C,QAAQ4C,UADA,OAAbD,SACyBA,SAAW,OAAS,QAEO,SAA3BD,KAAK1C,QAAQ4C,UAAuB,QAAU,aAGrEC,YAAcH,KAAKpC,cAAc,aACvCuC,YAAYC,UAAUC,OAAO,cAAe,oBAC5CF,YAAYC,UAAUE,IAA+B,SAA3BN,KAAK1C,QAAQ4C,UAAuB,cAAgB,oBAMlFxC,0BAA0BK,GACtBA,EAAEC,iBACFD,EAAEE,uBAEG8B,qCAAetC,qBACf8B,6BAA6BgB,SAASP,YAClCD,eAAeC,KAA0C,SAApC3C,qCAAcC,QAAQ4C,cAIxDG,8CACkBA"} \ No newline at end of file diff --git a/amd/build/app/block/item/factory.min.js b/amd/build/app/block/item/factory.min.js new file mode 100644 index 0000000..1e75731 --- /dev/null +++ b/amd/build/app/block/item/factory.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/block/item/factory",["exports","../factory","./element"],(function(_exports,_factory,_element){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classPrivateFieldInitSpec(obj,privateMap,value){!function(obj,privateCollection){if(privateCollection.has(obj))throw new TypeError("Cannot initialize the same private elements twice on an object")}(obj,privateMap),privateMap.set(obj,value)}function _classPrivateFieldSet(receiver,privateMap,value){return function(receiver,descriptor,value){if(descriptor.set)descriptor.set.call(receiver,value);else{if(!descriptor.writable)throw new TypeError("attempted to set read only private field");descriptor.value=value}}(receiver,_classExtractFieldDescriptor(receiver,privateMap,"set"),value),value}function _classExtractFieldDescriptor(receiver,privateMap,action){if(!privateMap.has(receiver))throw new TypeError("attempted to "+action+" private field on non-instance");return privateMap.get(receiver)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=_interopRequireDefault(_factory),_element=_interopRequireDefault(_element);var _baseFactory=new WeakMap;return _exports.default=class{constructor(baseFactory){_classPrivateFieldInitSpec(this,_baseFactory,{writable:!0,value:void 0}),_classPrivateFieldSet(this,_baseFactory,baseFactory)}element(blockElement,element){return new _element.default(function(receiver,descriptor){return descriptor.get?descriptor.get.call(receiver):descriptor.value}(receiver=this,_classExtractFieldDescriptor(receiver,_baseFactory,"get")),blockElement,element);var receiver}},_exports.default})); + +//# sourceMappingURL=factory.min.js.map \ No newline at end of file diff --git a/amd/build/app/block/item/factory.min.js.map b/amd/build/app/block/item/factory.min.js.map new file mode 100644 index 0000000..b16bdfb --- /dev/null +++ b/amd/build/app/block/item/factory.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"factory.min.js","sources":["../../../../src/app/block/item/factory.js"],"sourcesContent":["// eslint-disable-next-line no-unused-vars\nimport BaseFactory from '../factory';\nimport ItemElement from \"./element\";\n\nexport default class Factory {\n /**\n * @type {BaseFactory}\n */\n #baseFactory;\n\n /**\n * @param {BaseFactory} baseFactory\n */\n constructor(baseFactory) {\n this.#baseFactory = baseFactory;\n }\n\n /**\n * @param {BlockElement} blockElement\n * @param {HTMLElement} element\n * @returns {ItemElement}\n */\n element(blockElement, element) {\n return new ItemElement(this.#baseFactory, blockElement, element);\n }\n}"],"names":["constructor","baseFactory","element","blockElement","ItemElement","this"],"mappings":"otCAaIA,YAAYC,8HACYA,aAQxBC,QAAQC,aAAcD,gBACX,IAAIE,8HAAYC,gEAAmBF,aAAcD"} \ No newline at end of file diff --git a/amd/build/app/factory.min.js b/amd/build/app/factory.min.js new file mode 100644 index 0000000..4f26965 --- /dev/null +++ b/amd/build/app/factory.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/app/factory",["exports","./block/factory"],(function(_exports,_factory){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_factory=(obj=_factory)&&obj.__esModule?obj:{default:obj};return _exports.default=class{static make(){return new this}blockFactory(){return new _factory.default(this)}},_exports.default})); + +//# sourceMappingURL=factory.min.js.map \ No newline at end of file diff --git a/amd/build/app/factory.min.js.map b/amd/build/app/factory.min.js.map new file mode 100644 index 0000000..3d68391 --- /dev/null +++ b/amd/build/app/factory.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"factory.min.js","sources":["../../src/app/factory.js"],"sourcesContent":["import BlockFactory from \"./block/factory\";\n\nexport default class Factory {\n /**\n * @returns {Factory}\n */\n static make() {\n return new this();\n }\n\n /**\n * @returns {BlockFactory}\n */\n blockFactory() {\n return new BlockFactory(this);\n }\n}\n"],"names":["this","blockFactory","BlockFactory"],"mappings":"wSAOe,IAAIA,KAMfC,sBACW,IAAIC,iBAAaF"} \ No newline at end of file diff --git a/amd/build/block.min.js b/amd/build/block.min.js new file mode 100644 index 0000000..10b9647 --- /dev/null +++ b/amd/build/block.min.js @@ -0,0 +1,3 @@ +define("block_sharing_cart/block",["exports","./app/factory"],(function(_exports,_factory){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_factory=(obj=_factory)&&obj.__esModule?obj:{default:obj};_exports.init=()=>{_factory.default.make().blockFactory().eventHandler().onLoad()}})); + +//# sourceMappingURL=block.min.js.map \ No newline at end of file diff --git a/amd/build/block.min.js.map b/amd/build/block.min.js.map new file mode 100644 index 0000000..9f7856c --- /dev/null +++ b/amd/build/block.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"block.min.js","sources":["../src/block.js"],"sourcesContent":["import BaseFactory from './app/factory';\n\nexport const init = () => {\n BaseFactory.make().blockFactory().eventHandler().onLoad();\n};"],"names":["make","blockFactory","eventHandler","onLoad"],"mappings":"wPAEoB,sBACJA,OAAOC,eAAeC,eAAeC"} \ No newline at end of file diff --git a/amd/src/app/block/course/element.js b/amd/src/app/block/course/element.js new file mode 100644 index 0000000..a23118f --- /dev/null +++ b/amd/src/app/block/course/element.js @@ -0,0 +1,158 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; +import Templates from "core/templates"; + +export default class CourseElement { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @type {BlockElement} + */ + #blockElement; + + /** + * @type {HTMLElement} + */ + #element; + + /** + * @type {HTMLElement|null} + */ + #clipboard = null; + + constructor(baseFactory, blockElement, element) { + this.#baseFactory = baseFactory; + this.#blockElement = blockElement; + this.#element = element; + } + + async addBackupToSharingCartButtons() { + let element = document.createElement('div'); + const {html, js} = await new Promise((resolve, reject) => { + Templates.render('block_sharing_cart/block/course/add_to_sharing_cart_button', {}) + .then(async (html, js) => { + resolve({ + html, + js + }); + }).fail(reject); + }); + element = await Templates.replaceNode( + element, + html, + js + )[0]; + + const sectionTitles = this.#element.querySelectorAll('.course-section-header .inplaceeditable'); + sectionTitles.forEach((sectionTitle) => { + const button = element.cloneNode(true); + + sectionTitle.after(button); + }); + + this.#element.querySelectorAll('.cm_action_menu').forEach((courseModuleActionMenu) => { + const button = element.cloneNode(true); + + courseModuleActionMenu.append(button); + }); + } + + async renderClipboard() { + this.#clipboard = document.createElement('div'); + + const {html, js} = await new Promise((resolve, reject) => { + Templates.render('block_sharing_cart/block/course/clipboard', {}) + .then(async (html, js) => { + resolve({ + html, + js + }); + }).fail(reject); + }); + this.#clipboard = await Templates.replaceNode( + this.#clipboard, + html, + js + )[0]; + + this.#element.prepend(this.#clipboard); + this.#clipboard.querySelector('[data-action="clear-clipboard"]').addEventListener('click', this.clearClipboard.bind(this)); + } + + /** + * @param {Event} e + */ + clearClipboard(e) { + e.preventDefault(); + e.stopPropagation(); + + this.#clipboard.classList.add('d-none'); + this.clearClipboardTargets(); + + this.#blockElement.clearClipboard(); + } + + /** + * @param {ItemElement} item + */ + updateClipboard(item) { + this.#clipboard.classList.add('d-none'); + + const clipboardItemInfo = this.#clipboard.querySelector('.info'); + clipboardItemInfo.innerHTML = item.getItemInfo().innerHTML; + + this.#clipboard.classList.remove('d-none'); + } + + /** + * @param {ItemElement} item + */ + async updateClipboardTargets(item) { + let element = document.createElement('div'); + const {html, js} = await new Promise((resolve, reject) => { + Templates.render('block_sharing_cart/block/course/clipboard_target', {}) + .then(async (html, js) => { + resolve({ + html, + js + }); + }).fail(reject); + }); + element = await Templates.replaceNode( + element, + html, + js + )[0]; + + this.#element.querySelectorAll('[data-for="cmlist"]').forEach((section) => { + const clipboardTarget = section.querySelector('.clipboard_target') ?? element.cloneNode(true); + + section.prepend(clipboardTarget); + }); + } + + getClipboardTargets() { + return this.#element.querySelectorAll('.clipboard_target'); + } + + clearClipboardTargets() { + this.getClipboardTargets().forEach((target) => { + target.remove(); + }); + } + + /** + * @param {ItemElement} item + */ + async setClipboard(item) { + if (!this.#clipboard) { + await this.renderClipboard(); + } + + this.updateClipboard(item); + await this.updateClipboardTargets(item); + } +} \ No newline at end of file diff --git a/amd/src/app/block/course/factory.js b/amd/src/app/block/course/factory.js new file mode 100644 index 0000000..f2c122f --- /dev/null +++ b/amd/src/app/block/course/factory.js @@ -0,0 +1,26 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; +import CourseElement from "./element"; + +export default class Factory { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @param {BaseFactory} baseFactory + */ + constructor(baseFactory) { + this.#baseFactory = baseFactory; + } + + /** + * @param {BlockElement} blockElement + * @param {HTMLElement} element + * @returns {CourseElement} + */ + element(blockElement, element) { + return new CourseElement(this.#baseFactory, blockElement, element); + } +} \ No newline at end of file diff --git a/amd/src/app/block/element.js b/amd/src/app/block/element.js new file mode 100644 index 0000000..eb5010b --- /dev/null +++ b/amd/src/app/block/element.js @@ -0,0 +1,83 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; + +export default class BlockElement { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @type {HTMLElement} + */ + #element; + + /** + * @type {CourseElement} + */ + #course; + + /** + * @type {ItemElement[]} + */ + #items = []; + + /** + * @type {ItemElement|NULL} + */ + #clipboardItem = null; + constructor(baseFactory, element) { + this.#baseFactory = baseFactory; + this.#element = element; + } + + addEventListeners() { + this.setupCourse(); + this.setupItems(); + } + + setupCourse() { + const course = document.querySelector('.course-content'); + + const courseElement = this.#baseFactory.blockFactory().course().element(this, course); + courseElement.addBackupToSharingCartButtons(); + + this.#course = courseElement; + } + setupItems() { + const items = this.#element.querySelectorAll('.sharing_cart_item'); + + items.forEach((element) => { + const itemElement = this.#baseFactory.blockFactory().item().element(this, element); + itemElement.addEventListeners(); + + this.#items.push( + itemElement + ); + }); + } + + /** + * @param {ItemElement} item + */ + deleteItem(item) { + // TODO: Do web service call to delete item + + const index = this.#items.findIndex((i) => i.getItemId() === item.getItemId()); + this.#items.splice(index, 1); + item.remove(); + } + + /** + * @param {ItemElement} item + */ + async setClipboard(item) { + this.#clipboardItem = item; + + await this.#course.setClipboard(item); + } + + clearClipboard() { + this.#clipboardItem = null; + } +} \ No newline at end of file diff --git a/amd/src/app/block/event_handler.js b/amd/src/app/block/event_handler.js new file mode 100644 index 0000000..645fc47 --- /dev/null +++ b/amd/src/app/block/event_handler.js @@ -0,0 +1,26 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; + +export default class EventHandler { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @param {BaseFactory} baseFactory + */ + constructor(baseFactory) { + this.#baseFactory = baseFactory; + } + + onLoad() { + this.setupBlock(); + } + + setupBlock() { + const block = document.querySelector('.block.block_sharing_cart'); + + this.#baseFactory.blockFactory().element(block).addEventListeners(); + } +} diff --git a/amd/src/app/block/factory.js b/amd/src/app/block/factory.js new file mode 100644 index 0000000..810547c --- /dev/null +++ b/amd/src/app/block/factory.js @@ -0,0 +1,45 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; +import EventHandler from "./event_handler"; +import CourseFactory from "./course/factory"; +import ItemFactory from "./item/factory"; +import BlockElement from "./element"; + +export default class Factory { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @param {BaseFactory} baseFactory + */ + constructor(baseFactory) { + this.#baseFactory = baseFactory; + } + + /** + * @returns {EventHandler} + */ + eventHandler() { + return new EventHandler(this.#baseFactory); + } + + /** + * @returns {CourseFactory} + */ + course() { + return new CourseFactory(this.#baseFactory); + } + + /** + * @returns {ItemFactory} + */ + item() { + return new ItemFactory(this.#baseFactory); + } + + element(element) { + return new BlockElement(this.#baseFactory, element); + } +} \ No newline at end of file diff --git a/amd/src/app/block/item/element.js b/amd/src/app/block/item/element.js new file mode 100644 index 0000000..b5bed66 --- /dev/null +++ b/amd/src/app/block/item/element.js @@ -0,0 +1,146 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; +import ModalFactory from 'core/modal_factory'; +import ModalEvents from 'core/modal_events'; +import { get_strings } from "core/str"; + +export default class ItemElement { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @type {BlockElement} + */ + #blockElement; + + /** + * @type {HTMLElement} + */ + #element; + + constructor(baseFactory, blockElement, element) { + this.#baseFactory = baseFactory; + this.#blockElement = blockElement; + this.#element = element; + } + + addEventListeners() { + if(this.#element.dataset.type === 'course' || this.#element.dataset.type === 'section') { + this.#element.addEventListener('click', this.toggleCollapseRecursively.bind(this)); + } + + this.#element.querySelector('[data-action="delete"]')?.addEventListener('click', this.confirmDeleteItem.bind(this)); + this.#element.querySelector('[data-action="copy_to_course"]')?.addEventListener('click', this.copyItemToCourse.bind(this)); + } + + async copyItemToCourse(e) { + e.preventDefault(); + e.stopPropagation(); + + this.#blockElement.setClipboard(this); + } + + async confirmDeleteItem(e) { + e.preventDefault(); + e.stopPropagation(); + + const strings = await get_strings([ + { + key: 'delete_item', + component: 'block_sharing_cart', + }, + { + key: 'confirm_delete_item', + component: 'block_sharing_cart', + }, + { + key: 'delete', + component: 'core', + }, + { + key: 'cancel', + component: 'core', + } + ]); + + const modal = await ModalFactory.create({ + type: ModalFactory.types.DELETE_CANCEL, + title: strings[0] + ': "' + this.getItemName() + '"', + body: strings[1], + buttons: { + delete: strings[2], + cancel: strings[3], + }, + removeOnClose: true, + }); + modal.getRoot().on(ModalEvents.delete, this.#blockElement.deleteItem.bind(this.#blockElement, this)); + await modal.show(); + } + + /** + * @return {NodeListOf} + */ + getItemChildrenRecursively() { + return this.#element.querySelectorAll('.sharing_cart_item'); + } + + /** + * @return {String} + */ + getItemName() { + return this.#element.querySelector('.name').innerText; + } + + /** + * @return {Number} + */ + getItemId() { + return Number.parseInt(this.#element.dataset.itemid); + } + + /** + * @return {HTMLElement} + */ + getItemInfo() { + return this.#element.querySelector('.info'); + } + + /** + * @param {HTMLElement} item + * @param {Boolean|NULL} collapse + */ + toggleCollapse(item, collapse = null) { + if (item.dataset.type !== 'course' && item.dataset.type !== 'section') { + return; + } + + if (collapse !== null) { + item.dataset.collapsed = collapse ? 'true' : 'false'; + } else { + item.dataset.collapsed = item.dataset.collapsed === 'true' ? 'false' : 'true'; + } + + const iconElement = item.querySelector('.info > i'); + iconElement.classList.remove('fa-folder-o', 'fa-folder-open-o'); + iconElement.classList.add(item.dataset.collapsed === 'true' ? 'fa-folder-o' : 'fa-folder-open-o'); + } + + /** + * @param {Event} e + */ + toggleCollapseRecursively(e) { + e.preventDefault(); + e.stopPropagation(); + + this.toggleCollapse(this.#element); + this.getItemChildrenRecursively().forEach((item) => { + this.toggleCollapse(item, this.#element.dataset.collapsed === 'true'); + }); + } + + remove() { + this.#element.remove(); + } +} \ No newline at end of file diff --git a/amd/src/app/block/item/factory.js b/amd/src/app/block/item/factory.js new file mode 100644 index 0000000..86ca5e9 --- /dev/null +++ b/amd/src/app/block/item/factory.js @@ -0,0 +1,26 @@ +// eslint-disable-next-line no-unused-vars +import BaseFactory from '../factory'; +import ItemElement from "./element"; + +export default class Factory { + /** + * @type {BaseFactory} + */ + #baseFactory; + + /** + * @param {BaseFactory} baseFactory + */ + constructor(baseFactory) { + this.#baseFactory = baseFactory; + } + + /** + * @param {BlockElement} blockElement + * @param {HTMLElement} element + * @returns {ItemElement} + */ + element(blockElement, element) { + return new ItemElement(this.#baseFactory, blockElement, element); + } +} \ No newline at end of file diff --git a/amd/src/app/factory.js b/amd/src/app/factory.js new file mode 100644 index 0000000..8b3da2b --- /dev/null +++ b/amd/src/app/factory.js @@ -0,0 +1,17 @@ +import BlockFactory from "./block/factory"; + +export default class Factory { + /** + * @returns {Factory} + */ + static make() { + return new this(); + } + + /** + * @returns {BlockFactory} + */ + blockFactory() { + return new BlockFactory(this); + } +} diff --git a/amd/src/block.js b/amd/src/block.js new file mode 100644 index 0000000..a8a15d7 --- /dev/null +++ b/amd/src/block.js @@ -0,0 +1,5 @@ +import BaseFactory from './app/factory'; + +export const init = () => { + BaseFactory.make().blockFactory().eventHandler().onLoad(); +}; \ No newline at end of file diff --git a/block_sharing_cart.php b/block_sharing_cart.php index e6878b9..41be5ae 100644 --- a/block_sharing_cart.php +++ b/block_sharing_cart.php @@ -27,6 +27,7 @@ public function get_content(): object|string { $base_factory = \block_sharing_cart\app\factory::make(); $this->page->requires->css('/blocks/sharing_cart/style/style.css'); + $this->page->requires->js_call_amd('block_sharing_cart/block', 'init', []); if ($this->content !== null) { return $this->content; diff --git a/classes/output/block/content.php b/classes/output/block/content.php index 79eac0a..9b38ea3 100644 --- a/classes/output/block/content.php +++ b/classes/output/block/content.php @@ -6,6 +6,7 @@ defined('MOODLE_INTERNAL') || die(); // @codeCoverageIgnoreEnd +use block_sharing_cart\app\collection; use block_sharing_cart\app\factory as base_factory; class content implements \renderable, \core\output\named_templatable { @@ -21,51 +22,52 @@ public function get_template_name(\renderer_base $renderer): string { return 'block_sharing_cart/block/content'; } - public function export_items_for_template(): array { + private function export_item_for_template(object $item): object { + $item->is_root = $item->parent_item_id === null; + + $item->is_course = $item->type === 'course'; + $item->is_section = $item->type === 'section'; + + $item->is_module = !$item->is_course && !$item->is_section; + $item->mod_icon = $item->is_module ? $this->get_mod_icon($item) : null; + + return $item; + } + + private function export_items_for_template(): array { $all_items = $this->base_factory->items()->repository()->get_by_user_id($this->user_id); - $all_items->map(function (object $item) { - $item->is_root = $item->parent_item_id === null; - $item->icon = $this->get_item_icon($item); - }); + $all_items->map(fn($item) => $this->export_item_for_template($item)); $root_items = $all_items->filter(static function (object $item) { return $item->is_root; }); - - $get_children = static function (object $item) use (&$get_children, $all_items) { - $children = $all_items->filter(static function (object $child_item) use ($item) { - return $child_item->parent_item_id === $item->id; - }); - foreach ($children as $child) { - $child->children = $get_children($child); - } - return $children; - }; - - foreach ($root_items as $root_item) { - $root_item->children = $get_children($root_item); - } + $root_items->map(function (object $root_item) use ($all_items) { + $root_item->children = $this->get_item_children($root_item, $all_items); + }); return $root_items->to_array(true); } - public function export_for_template(\renderer_base $output): array { - return [ - 'items' => $this->export_items_for_template() - ]; + private function get_item_children(object $item, collection $all_items): collection { + $children = $all_items->filter(static function (object $child_item) use ($item) { + return $child_item->parent_item_id === $item->id; + }); + $children->map(function (object $child) use ($all_items) { + $child->children = $this->get_item_children($child, $all_items); + }); + + return $children; } - private function get_item_icon(object $item): string { + private function get_mod_icon(object $item): string { global $OUTPUT; - if ($item->type === 'course') { - return $OUTPUT->image_url('icon', 'course'); - } - - if ($item->type === 'section') { - return $OUTPUT->image_url('icon', 'section'); - } - return $OUTPUT->image_url('icon', $item->type); } + + public function export_for_template(\renderer_base $output): array { + return [ + 'items' => $this->export_items_for_template() + ]; + } } \ No newline at end of file diff --git a/db/install.xml b/db/install.xml index 1d142fd..465b7b7 100644 --- a/db/install.xml +++ b/db/install.xml @@ -8,10 +8,11 @@ - + - + + @@ -26,6 +27,7 @@ + diff --git a/lang/en/block_sharing_cart.php b/lang/en/block_sharing_cart.php index 0b67ef4..da33b60 100644 --- a/lang/en/block_sharing_cart.php +++ b/lang/en/block_sharing_cart.php @@ -19,6 +19,7 @@ Which will be replaced with an input box if you click "Edit" icon.

  • '; $string['sharing_cart:addinstance'] = 'Add a new Sharing Cart block'; + /* $string['backup'] = 'Copy to Sharing Cart'; $string['restore'] = 'Copy to course'; @@ -26,7 +27,6 @@ $string['clicktomove'] = 'Click to move here'; $string['copyhere'] = 'Copy here'; $string['notarget'] = 'Target not found'; -$string['clipboard'] = 'Copying this shared item'; $string['bulkdelete'] = 'Bulk delete'; $string['confirm_backup'] = 'Are you sure you want to copy this activity/resource into Sharing Cart?'; $string['confirm_backup_section'] = 'Do you want to copy this course section and its activities / resources into Sharing Cart?'; @@ -46,36 +46,7 @@ $string['uninstalled_plugin_warning_title'] = 'This plugin is uninstalled. Trying to recover this without reinstalling the plugin: {$a} will cause errors. Restoration is disabled'; $string['drop_here'] = 'Drop here..'; $string['async_restore_in_progress'] = 'Restoring sharing item "{$a->modtext}" to the section number {$a->section}'; -*/ -$string['settings:backup_mode'] = 'Backup mode'; -$string['settings:backup_mode_desc'] = 'Choose between immediate or asynchronous backup process'; -$string['settings:restore_mode'] = 'Restore mode'; -$string['settings:restore_mode_desc'] = 'Choose between immediate or asynchronous restore process'; -$string['settings:backup_restore_mode_immediate'] = 'Immediate'; -$string['settings:backup_restore_mode_async'] = 'Asynchronous'; -$string['settings:userdata_copyable_modtypes'] = 'User data copyable module types'; -$string['settings:userdata_copyable_modtypes_desc'] = 'While copying an activity into the Sharing Cart, -a dialog shows an option whether a copy of an activity includes its user data or not, -if its module type is checked in the above and an operator has moodle/backup:userinfo, -moodle/backup:anonymise and moodle/restore:userinfo capabilities. -(By default, only manager role has those capabilities.)'; -$string['settings:workaround_qtypes'] = 'Workaround for question types'; -$string['settings:workaround_qtypes_desc'] = 'The workaround for question restore issue will be performed if its question type is checked. -When the questions to be restored already exist, however, those data look like inconsistent, -this workaround will try to make another duplicates instead of reusing existing data. -It may be useful for avoiding some restore errors, such as error_question_match_sub_missing_in_db.'; -$string['settings:add_to_sharing_cart'] = 'Add to sharing cart'; -$string['settings:add_to_sharing_cart_desc'] = 'Choose between drag and drop or click to add method for adding modules and sections to the sharing cart. - -The \'Click to add\' method is the standard in the sharing cart, where you need to click on the basket icon, before it will be added into the sharing cart. - -The \'Drag and drop\' allows you to drag and drop modules/activities and sections into the sharing cart block or into the basket icon in the footer. Please note that this is only supported for Moodle 4.0+'; -$string['settings:drag_and_drop'] = 'Drag and drop'; -$string['settings:click_to_add'] = 'Click to add'; -$string['settings:show_copy_section_in_block'] = 'Show the "Copy section" in block'; -$string['settings:show_copy_section_in_block_desc'] = 'Show the "Copy section" in the sharing cart block, underneath all modules/activities'; -$string['settings:show_copy_activity_in_block'] = 'Show the "Copy activity" in block'; -$string['settings:show_copy_activity_in_block_desc'] = 'Show the "Copy activity" in the sharing cart block, underneath all modules/activities - This is only available if the user has the capability to backup activities, but not the capability to manage/move activities'; -/* $string['invalidoperation'] = 'An invalid operation detected'; $string['unexpectederror'] = 'An unexpected error occurred'; $string['recordnotfound'] = 'Shared item not found'; @@ -109,4 +80,40 @@ $string['modal_bulkdelete_title'] = 'Sure you want to delete'; $string['modal_bulkdelete_confirm'] = 'Delete selected'; -*/ \ No newline at end of file +*/ + +// Block +$string['rename_item'] = 'Rename item'; +$string['delete_item'] = 'Delete item'; +$string['copy_item'] = 'Copy item'; +$string['confirm_delete_item'] = 'Are you sure you want to delete this item? All sub-items will also be deleted.'; +$string['copying_this_item'] = 'Copying this item'; + +// Settings +$string['settings:backup_mode'] = 'Backup mode'; +$string['settings:backup_mode_desc'] = 'Choose between immediate or asynchronous backup process'; +$string['settings:restore_mode'] = 'Restore mode'; +$string['settings:restore_mode_desc'] = 'Choose between immediate or asynchronous restore process'; +$string['settings:backup_restore_mode_immediate'] = 'Immediate'; +$string['settings:backup_restore_mode_async'] = 'Asynchronous'; +$string['settings:userdata_copyable_modtypes'] = 'User data copyable module types'; +$string['settings:userdata_copyable_modtypes_desc'] = 'While copying an activity into the Sharing Cart, +a dialog shows an option whether a copy of an activity includes its user data or not, +if its module type is checked in the above and an operator has moodle/backup:userinfo, +moodle/backup:anonymise and moodle/restore:userinfo capabilities. +(By default, only manager role has those capabilities.)'; +$string['settings:workaround_qtypes'] = 'Workaround for question types'; +$string['settings:workaround_qtypes_desc'] = 'The workaround for question restore issue will be performed if its question type is checked. +When the questions to be restored already exist, however, those data look like inconsistent, +this workaround will try to make another duplicates instead of reusing existing data. +It may be useful for avoiding some restore errors, such as error_question_match_sub_missing_in_db.'; +$string['settings:add_to_sharing_cart'] = 'Add to sharing cart'; +$string['settings:add_to_sharing_cart_desc'] = 'Choose between drag and drop or click to add method for adding modules and sections to the sharing cart. - +The \'Click to add\' method is the standard in the sharing cart, where you need to click on the basket icon, before it will be added into the sharing cart. - +The \'Drag and drop\' allows you to drag and drop modules/activities and sections into the sharing cart block or into the basket icon in the footer. Please note that this is only supported for Moodle 4.0+'; +$string['settings:drag_and_drop'] = 'Drag and drop'; +$string['settings:click_to_add'] = 'Click to add'; +$string['settings:show_copy_section_in_block'] = 'Show the "Copy section" in block'; +$string['settings:show_copy_section_in_block_desc'] = 'Show the "Copy section" in the sharing cart block, underneath all modules/activities'; +$string['settings:show_copy_activity_in_block'] = 'Show the "Copy activity" in block'; +$string['settings:show_copy_activity_in_block_desc'] = 'Show the "Copy activity" in the sharing cart block, underneath all modules/activities - This is only available if the user has the capability to backup activities, but not the capability to manage/move activities'; \ No newline at end of file diff --git a/pix/bulkdelete.svg b/pix/bulkdelete.svg deleted file mode 100644 index f2c9ba3..0000000 --- a/pix/bulkdelete.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/pix/dropzone_arrow.svg b/pix/dropzone_arrow.svg deleted file mode 100644 index 7577394..0000000 --- a/pix/dropzone_arrow.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - diff --git a/style/scss/block/content.scss b/style/scss/block/content.scss index b5670ef..10e2d9e 100644 --- a/style/scss/block/content.scss +++ b/style/scss/block/content.scss @@ -1,25 +1,100 @@ +body:has(.block.block_sharing_cart) .course-content { + .clipboard { + display: flex; + border: 2px dashed #999; + margin-bottom: 1rem; + padding: 0.5rem; + font-weight: bold; + gap: 0.5rem; + align-items: center; + + .info { + display: flex; + align-items: center; + gap: 0.5rem; + } + + [data-action="clear-clipboard"] { + cursor: pointer; + } + } + + .clipboard_target { + display: flex; + border: 2px dashed #999; + margin-top: 1rem; + margin-bottom: 1rem; + padding: 0.5rem; + font-size: 2rem; + justify-content: center; + cursor: pointer; + } + + .add_to_sharing_cart { + display: flex; + align-self: center; + cursor: pointer; + } +} + .block.block_sharing_cart { .sharing_cart_items { display: flex; flex-direction: column; - gap: 1rem; .sharing_cart_item { display: flex; flex-direction: column; width: 100%; - padding-top: 0.5rem; + cursor: auto; &:hover { background-color: rgba(0, 0, 0, .03); } - .info { + &[data-type="course"], &[data-type="section"] { + cursor: pointer; + } + + &[data-type="course"] { + .item-body { + } + } + + &[data-type="section"] { + .item-body { + } + } + + // Not course or section (meaning module) + &:not([data-type="course"]):not([data-type="section"]) { + .item-body { + .info { + img { + width: 1rem; + height: 1rem; + } + } + } + } + + // Collapsed + &[data-collapsed="true"] { + .sharing_cart_item_children { + display: none; + } + } + + .item-body { display: flex; flex-direction: row; padding: 0.5rem; - margin-left: 0.5rem; margin-right: 0.5rem; + align-items: center; + + .name { + margin-left: 0.5rem; + } .sharing_cart_item_actions { display: flex; diff --git a/style/style.css b/style/style.css index fc8ff3a..f46a28f 100644 --- a/style/style.css +++ b/style/style.css @@ -1 +1,83 @@ -.block.block_sharing_cart .sharing_cart_items{display:flex;flex-direction:column;gap:1rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item{display:flex;flex-direction:column;width:100%;padding-top:.5rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item:hover{background-color:rgba(0,0,0,.03)}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .info{display:flex;flex-direction:row;padding:.5rem;margin-left:.5rem;margin-right:.5rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .info .sharing_cart_item_actions{display:flex;flex-direction:row;align-items:center;margin-left:auto;gap:.5rem}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .info .sharing_cart_item_actions i{cursor:pointer}.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .sharing_cart_item_children{display:flex;flex-direction:column;gap:1rem}/*# sourceMappingURL=style.css.map */ +body:has(.block.block_sharing_cart) .course-content .clipboard { + display: flex; + border: 2px dashed #999; + margin-bottom: 1rem; + padding: 0.5rem; + font-weight: bold; + gap: 0.5rem; + align-items: center; +} +body:has(.block.block_sharing_cart) .course-content .clipboard .info { + display: flex; + align-items: center; + gap: 0.5rem; +} +body:has(.block.block_sharing_cart) .course-content .clipboard [data-action=clear-clipboard] { + cursor: pointer; +} +body:has(.block.block_sharing_cart) .course-content .clipboard_target { + display: flex; + border: 2px dashed #999; + margin-top: 1rem; + margin-bottom: 1rem; + padding: 0.5rem; + font-size: 2rem; + justify-content: center; + cursor: pointer; +} +body:has(.block.block_sharing_cart) .course-content .add_to_sharing_cart { + display: flex; + align-self: center; + cursor: pointer; +} + +.block.block_sharing_cart .sharing_cart_items { + display: flex; + flex-direction: column; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item { + display: flex; + flex-direction: column; + width: 100%; + cursor: auto; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item:hover { + background-color: rgba(0, 0, 0, 0.03); +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item[data-type=course], .block.block_sharing_cart .sharing_cart_items .sharing_cart_item[data-type=section] { + cursor: pointer; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item:not([data-type=course]):not([data-type=section]) .item-body .info img { + width: 1rem; + height: 1rem; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item[data-collapsed=true] .sharing_cart_item_children { + display: none; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .item-body { + display: flex; + flex-direction: row; + padding: 0.5rem; + margin-right: 0.5rem; + align-items: center; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .item-body .name { + margin-left: 0.5rem; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .item-body .sharing_cart_item_actions { + display: flex; + flex-direction: row; + align-items: center; + margin-left: auto; + gap: 0.5rem; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .item-body .sharing_cart_item_actions i { + cursor: pointer; +} +.block.block_sharing_cart .sharing_cart_items .sharing_cart_item .sharing_cart_item_children { + display: flex; + flex-direction: column; + gap: 1rem; +} + +/*# sourceMappingURL=style.css.map */ diff --git a/style/style.css.map b/style/style.css.map index 5eb58d7..e99723e 100644 --- a/style/style.css.map +++ b/style/style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["scss/block/content.scss"],"names":[],"mappings":"AACE,8CACE,aACA,sBACA,SAEA,iEACE,aACA,sBACA,WACA,kBAEA,uEACE,iCAGF,uEACE,aACA,mBACA,cACA,kBACA,mBAEA,kGACE,aACA,mBACA,mBACA,iBACA,UAEA,oGACE,eAKN,6FACE,aACA,sBACA","file":"style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["scss/block/content.scss"],"names":[],"mappings":"AACE;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;;AAKF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAiBI;EACE;EACA;;AAQN;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA","file":"style.css"} \ No newline at end of file diff --git a/templates/block/course/add_to_sharing_cart_button.mustache b/templates/block/course/add_to_sharing_cart_button.mustache new file mode 100644 index 0000000..3fb4595 --- /dev/null +++ b/templates/block/course/add_to_sharing_cart_button.mustache @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/block/course/clipboard.mustache b/templates/block/course/clipboard.mustache new file mode 100644 index 0000000..3b137a9 --- /dev/null +++ b/templates/block/course/clipboard.mustache @@ -0,0 +1,5 @@ +
    + {{#str}}copying_this_item, block_sharing_cart{{/str}}: +
    + +
    \ No newline at end of file diff --git a/templates/block/course/clipboard_target.mustache b/templates/block/course/clipboard_target.mustache new file mode 100644 index 0000000..2f95911 --- /dev/null +++ b/templates/block/course/clipboard_target.mustache @@ -0,0 +1,3 @@ +
    + +
    \ No newline at end of file diff --git a/templates/block/item.mustache b/templates/block/item.mustache index 52b3528..32e3bb8 100644 --- a/templates/block/item.mustache +++ b/templates/block/item.mustache @@ -1,16 +1,20 @@ -
  • ').append($anchor);return $anchor.on("click",(function(e){!function(e){const m=(0,_jquery.default)(e.target).closest("a").attr("class").match(/move-(\d+)-to-(\d+)/),item_id=m[1],area_to=m[2],$spinner=add_spinner();_jquery.default.post(get_action_url("rest"),{action:"move",item_id:item_id,area_to:area_to,sesskey:M.cfg.sesskey},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()}))}(e)})),$target}if($next.length&&(next_id=$next.attr("id").match(/item-(\d+)$/)[1]),$list.find("> li.activity").each((function(index,item){const $item=(0,_jquery.default)(item),to=$item.attr("id").match(/item-(\d+)$/)[1];if(to===item_id){$cancel=create_command("cancel"),$cancel.on("click",(function(){move_targets.hide()}));const $commands=$item.find(".commands");$commands.find("a").each((function(){(0,_jquery.default)(this).hide()})),$commands.append($cancel),$item.css("opacity",.5)}else if(to!==next_id){const $target=create_target(item_id,to);$item.before($target),targets.push($target)}}),this),$next){var $target=create_target(item_id,0);$list.append($target),targets.push($target)}}},restore_targets=new function(){this.is_directory=null;let $clipboard=null,targets=[];function create_target(id,section){const href=get_action_url("restore",{directory:!0===restore_targets.is_directory,target:id,course:course.id,section:section,in_section:(0,_jquery.default)("#copy-section-form").data("in-section"),sesskey:M.cfg.sesskey,returnurl:document.URL});let $target=(0,_jquery.default)("").attr("href",href).attr("title",str("copyhere")).append((0,_jquery.default)('').attr("alt",str("copyhere")).attr("src",M.util.image_url("dropzone_arrow","block_sharing_cart")));return targets.push($target),$target}this.hide=function(){null!==$clipboard&&($clipboard.remove(),$clipboard=null,_jquery.default.each(targets,(function(index,$target){$target.remove()})),targets=[])},this.show=function(id){this.hide();let $view=(0,_jquery.default)("");if(this.is_directory)$view.html(id).css("display","inline"),$view.prepend((0,_jquery.default)("").addClass("icon").attr("alt",id));else{const $item=$block.find("#block_sharing_cart-item-"+id);$view=(0,_jquery.default)($item.find("div")[0].cloneNode(!0)).css("display","inline"),$view.attr("class",$view.attr("class").replace(/mod-indent-\d+/,"")),$view.find(".commands").remove()}const $cancel=create_command("cancel");if($cancel.on("click",this.hide),$clipboard=(0,_jquery.default)('
    '),$clipboard.append(str("clipboard")+": ").append($view).append($cancel),course.is_frontpage){const $sitetopic=(0,_jquery.default)(".sitetopic"),$mainmenu=(0,_jquery.default)(".block_site_main_menu");$sitetopic?$sitetopic.find("*").before($clipboard):$mainmenu&&$mainmenu.find(".content").before($clipboard),$mainmenu&&$mainmenu.find(".footer").before(create_target(id,0)),$sitetopic&&$sitetopic.find("ul.section").append(create_target(id,1))}else{const $container=(0,_jquery.default)(".course-content");$container.prepend($clipboard),$container.find(M.course.format.get_section_wrapper(null)).each((function(index,sectionDOM){const $section=(0,_jquery.default)(sectionDOM),section=$section.attr("id").match(/(\d+)$/)[1];$section.find("ul.section").first().append(create_target(id,section))}),this)}}};function init_footer_basket(){let currentDragging;const activities=document.querySelectorAll(".activity.activity-wrapper"),sections=document.querySelectorAll(".course-section-header"),sharingCartBlock=document.querySelector('section[data-block="sharing_cart"]');!function(){var _document$getElements;const courseSectionHeader=null!==(_document$getElements=document.getElementsByClassName("course-section-header")[0])&&void 0!==_document$getElements?_document$getElements:null;courseSectionHeader instanceof HTMLElement&&(courseSectionHeader.classList.add("draggable"),courseSectionHeader.setAttribute("draggable",!0))}();const footerIconContainer=document.getElementById("page-footer").querySelector('div[data-region="footer-container-popover"]'),basket=document.createElement("i");basket.setAttribute("class","fa fa-shopping-basket");const basketButton=document.createElement("button");basketButton.setAttribute("class","btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket"),basketButton.setAttribute("style","z-index: 1001;"),basketButton.append(basket);const dropAreaText=document.createElement("p");dropAreaText.setAttribute("class","font-weight-bold text-white"),dropAreaText.innerText=str("drop_here");const dropArea=document.createElement("div");function drag_event_listeners(draggable){draggable.addEventListener("dragstart",(e=>{basketButton.classList.remove("shake_basket"),null==footerIconContainer||footerIconContainer.prepend(basketButton),sharingCartBlock.children[0].classList.add("dragging_item"),sharingCartBlock.append(dropArea),currentDragging=e.target,isDragging=!0})),draggable.addEventListener("dragend",(()=>{currentDragging instanceof HTMLElement&&(null==footerIconContainer||footerIconContainer.removeChild(basketButton)),sharingCartBlock.children[0].classList.remove("dragging_item"),sharingCartBlock.removeChild(dropArea),isDragging=!1}))}dropArea.setAttribute("class","h-100 w-100 position-absolute d-flex justify-content-center align-items-center"),dropArea.append(dropAreaText),sections.forEach((section=>{drag_event_listeners(section)})),activities.forEach((activity=>{drag_event_listeners(activity)})),[basketButton,sharingCartBlock].forEach((dropzone=>{dropzone.addEventListener("dragover",(e=>{e.preventDefault(),dropzone.classList.add("drag_over")})),dropzone.addEventListener("dragenter",(e=>{e.preventDefault(),dropzone.classList.add("drag_over")})),dropzone.addEventListener("dragleave",(()=>{dropzone.classList.remove("drag_over")})),dropzone.addEventListener("drop",(()=>{currentDragging instanceof HTMLElement&¤tDragging.querySelector(".add-to-sharing-cart").click(),dropzone.classList.remove("drag_over"),currentDragging=void 0,isDragging=!1}))}))}_jquery.default.get_plugin_name=function(){let $blockheader=$block.find("h2");return $blockheader.length?$blockheader.html():($blockheader=$block.find("h3"),$blockheader.length?$blockheader.html():"")},_jquery.default.on_backup=function(e,activityName){let cmId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;0===cmId&&(cmId=function($backup){const $activity=$backup.closest("li.activity");if($activity.length)return $activity.attr("id").match(/(\d+)$/)[1];const $commands=$backup.closest(".commands"),dataowner=$commands.attr("data-owner");return dataowner.length?dataowner.match(/(\d+)$/)[1]:$commands.find("a.editing_delete").attr("href").match(/delete=(\d+)/)[1]}((0,_jquery.default)(e.target)));const data={action:"is_userdata_copyable",cmid:cmId};on_backup_modal(data,activityName,str("confirm_backup"),!1)},_jquery.default.on_movedir=function(e){const $commands=(0,_jquery.default)(e.target).closest(".commands"),$current_dir=$commands.closest("li.directory"),current_path=$current_dir.length?$current_dir.attr("directory-path"):"/",item_id=(0,_jquery.default)(e.target).closest("li.activity").attr("id").match(/(\d+)$/)[1],dirs=[];$block.find("li.directory").each((function(){dirs.push((0,_jquery.default)(this).attr("directory-path"))}));const $form=(0,_jquery.default)("
    ");function submit(){const folder_to=$form.find('[name="to"]').val(),$spinner=add_spinner();_jquery.default.post(get_action_url("rest"),{action:"movedir",item_id:item_id,folder_to:folder_to,sesskey:M.cfg.sesskey},(function(){reload_tree(),directories.reset()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()}))}if($form.attr("action","javascript:void(0)"),$form.submit(submit),0===dirs.length){var $input=(0,_jquery.default)('').val(current_path);setTimeout((function(){$input.focus()}),1),$form.append($input)}else{dirs.unshift("/");const $select=(0,_jquery.default)('').val(current_path);$select.remove(),$edit.replaceWith($input),$input.focus()})),$form.append($edit)}const $cancel=create_command("cancel");$cancel.on("click",(function(){$form.remove(),$commands.find("a").show()})),$form.append($cancel),$commands.find("a").each((function(){(0,_jquery.default)(this).hide()})),$commands.append($form)},_jquery.default.on_move=function(e){const id=(0,_jquery.default)(e.target).closest("li.activity").attr("id").match(/(\d+)$/)[1];move_targets.show(id)},_jquery.default.on_delete=function(e){const $item=(0,_jquery.default)(e.target).closest("li"),liText=$item[0].innerText;let modalBody,item,isDirectory=!1,description_text="";$item.hasClass("directory")?(isDirectory=!0,item=str("folder_string"),description_text=str("delete_folder")):item=str("activity_string"),modalBody='

    '+item+" "+liText+description_text+"

    ",confirm_modal({title:str("confirm_delete"),body:modalBody,save_button:str("modal_confirm_delete"),checkbox:!1,next:function(){let data={};!0===isDirectory?data={action:"delete_directory",path:$item.attr("directory-path"),sesskey:M.cfg.sesskey}:$item.hasClass("activity")&&(data={action:"delete",id:$item.attr("id").match(/(\d+)$/)[1],sesskey:M.cfg.sesskey});const $spinner=add_spinner();_jquery.default.post(get_action_url("rest"),data,(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()})),e.stopPropagation()}})},_jquery.default.on_restore=function(e){const $item=(0,_jquery.default)(e.target).closest("li");let id=null;$item.hasClass("directory")?(id=$item.attr("directory-path"),restore_targets.is_directory=!0):$item.hasClass("activity")&&(id=$item.attr("id").match(/(\d+)$/)[1],restore_targets.is_directory=!1),restore_targets.show(id)},_jquery.default.on_section_backup=function(sectionId,sectionNumber,courseId,sectionName){on_backup_modal({action:"is_userdata_copyable_section",sectionid:sectionId,sectionnumber:sectionNumber,courseid:courseId},sectionName,'

    '+str("backup_heavy_load_warning_message")+"

    "+str("confirm_backup_section"),!0)},_jquery.default.init_bulk_delete=function(isspeciallayout){const bulkdelete=$block.find(".editing_bulkdelete");bulkdelete.length&&(isspeciallayout?(bulkdelete.attr("role","menuitem").addClass("dropdown-item menu-action"),bulkdelete.append((0,_jquery.default)("").append(bulkdelete.attr("title"))),$block.find(".menubar .dropdown .dropdown-menu").append(bulkdelete)):$block.find(".header .commands").append(bulkdelete))},_jquery.default.init_help_icon=function(isspeciallayout){const helpicon=$block.find(".header-commands > .help-icon");isspeciallayout?$block.find(".header-commands").parent().css("display","block"):$block.find(".header .commands").append(helpicon)},_jquery.default.init_block_header=function(){const isspeciallayout=$block.find(".menubar .dropdown .dropdown-menu").length;_jquery.default.init_bulk_delete(isspeciallayout),_jquery.default.init_help_icon(isspeciallayout)},_jquery.default.init_item_tree=function(){function add_actions(item,actions){const $item=(0,_jquery.default)(item),isCopying="1"===$item.attr("data-is-copying"),$commands=$item.find(".commands").first();_jquery.default.each(actions,(function(index,action){if("restore"===action&&isCopying)return;const $command=create_command(action);$command.on("click",(function(e){_jquery.default["on_"+action](e)})),$commands.append($command)}),this)}const activity_actions=["movedir","move","delete"];course&&activity_actions.push("restore");const directory_actions=["delete","restore"];$block.find("li.activity").each((function(index,item){1!=(0,_jquery.default)(item).attr("data-disable-copy")?add_actions(item,activity_actions):add_actions(item,["movedir","move","delete"])})),$block.find("li.directory").each((function(index,item){add_actions(item,directory_actions)})),directories.init()},_jquery.default.init_activity_commands=function(){function create_backup_icon(){const $backupIcon=(0,_jquery.default)('
    ').append((0,_jquery.default)('')).attr("title",str("backup"));return"click_to_add"!==addMethod&&$backupIcon.addClass("d-none"),$backupIcon}function add_activity_backup_control($activity){const activityClass=$activity[0].className,modtype=activityClass.substr(activityClass.indexOf("modtype_")+8);let activityName=str("activity_string");"label"!==modtype&&(activityName=(0,_jquery.default)(".activity#"+$activity[0].id).find(".mod-indent-outer .activityinstance span.instancename").html());const $backupIcon=create_backup_icon();$backupIcon.on("click",(function(e){_jquery.default.on_backup(e,activityName)}));const $actionMenuItem=$activity.find(".action-menu.section-cm-edit-actions").parent(".actions");$actionMenuItem.find(".add-to-sharing-cart").length||$actionMenuItem.append($backupIcon)}(0,_jquery.default)(document).ajaxComplete((function(event,xhr,settings){const url=settings.url,lastslashindex=url.lastIndexOf("="),result=url.substring(lastslashindex+1);if("core_course_edit_module"===result||"core_course_get_module"===result){const data=JSON.parse(settings.data),action=data[0].args.action;if("delete"===action)return;setTimeout((function(){const activity_id=data[0].args.id,activity=(0,_jquery.default)("#module-"+activity_id);if(add_activity_backup_control(activity),"duplicate"===action){add_activity_backup_control(activity.next())}}),1)}})),(0,_jquery.default)("body.editing .course-content li.section").each((function(){!function($section){let sectionId=$section.find(".section_action_menu").data("sectionid");const sectionNumber=parseInt(String($section.attr("id")).match(/\d+/)[0]);let sectionName=$section.attr("aria-label")||$section.find(".sectionname").text().trim();const isFlexibleCourseFormat=(0,_jquery.default)("body[id$=flexsections]").length;isFlexibleCourseFormat&&null==sectionId&&(sectionId=$section.data("section-id"));const courseId=parseInt(String((0,_jquery.default)("body").attr("class")).match(/course-([0-9]*)( |$)/)[1]),$backupIcon=create_backup_icon();$backupIcon.on("click",(function(){const inPlaceEditSectionName=in_place_edit_section_name($section);sectionName=""!==inPlaceEditSectionName?inPlaceEditSectionName:sectionName,_jquery.default.on_section_backup(sectionId,sectionNumber,courseId,sectionName)}));let $sectionTitle=$section.find("h3.sectionname").first().find("a").last();const $inPlaceEditable=$section.find("h3.sectionname .inplaceeditable").first();$inPlaceEditable.length&&($sectionTitle=$inPlaceEditable),isFlexibleCourseFormat&&0===sectionNumber?($sectionTitle=$section.find("> .controls"),$sectionTitle.prepend($backupIcon)):$backupIcon.insertAfter($sectionTitle);const $activities=$section.find("li.activity");(0,_jquery.default)($activities).each((function(){add_activity_backup_control((0,_jquery.default)(this))}))}((0,_jquery.default)(this))}))},_jquery.default.init=function(){M.str.block_sharing_cart.pluginname=this.get_plugin_name(),_jquery.default.init_block_header(),_jquery.default.init_item_tree(),_jquery.default.init_activity_commands(),"drag_and_drop"===addMethod&&init_footer_basket()};var $spinner=(0,_jquery.default)("").addClass("spinner fa fa-3x fa-circle-o-notch fa-spin");(0,_jquery.default)("div#sharing-cart-spinner-modal div.spinner-container").prepend($spinner),_jquery.default.init()})),(0,_jquery.default)(".copy_section").on("click",(function(){const $section_selected=(0,_jquery.default)(".section-dropdown option:selected"),sectionId=$section_selected.data("section-id"),sectionNumber=$section_selected.data("section-number"),courseId=$section_selected.data("course-id"),sectionName=$section_selected.data("section-name");_jquery.default.on_section_backup(sectionId,sectionNumber,courseId,sectionName)})),(0,_jquery.default)(".copy_activity").on("click",(function(e){const activitySelected=(0,_jquery.default)(".activity-dropdown option:selected"),activityId=activitySelected.data("activity-id"),activityName=activitySelected.data("activity-name");_jquery.default.on_backup(e,activityName,activityId)}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events);_exports.init=function(addMethod){(0,_jquery.default)(document).ready((function(){let isDragging=!1;function str(identifier){return M.str.block_sharing_cart[identifier]||M.str.moodle[identifier]}function get_action_url(name,args){let url=M.cfg.wwwroot+"/blocks/sharing_cart/"+name+".php";if(args){const q=[];for(let k in args)q.push(k+"="+encodeURIComponent(args[k]));url+="?"+q.join("&")}return url}function remove_basket(){if("drag_and_drop"===addMethod&&!isDragging){const footerIconContainer=document.getElementById("page-footer").querySelector('div[data-region="footer-container-popover"]'),sharingCartBasket=document.querySelector("button.sharing_cart_basket");sharingCartBasket&&(null==footerIconContainer||footerIconContainer.removeChild(sharingCartBasket),sharingCartBasket.classList.remove("shake_basket"))}}function confirm_modal(obj){obj.checkbox&&(obj.body+='"),_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,title:obj.title,body:obj.body}).done((function(modal){let is_submitted=!1;modal.setSaveButtonText(obj.save_button),modal.getRoot().on(_modal_events.default.save,(function(e){const response={checkbox:(0,_jquery.default)(e.target).find(".modal-checkbox").is(":checked")};obj.next(response),is_submitted=!0})),modal.getRoot().on(_modal_events.default.cancel,(function(){remove_basket()})),modal.getRoot().on(_modal_events.default.hidden,(function(){(0,_jquery.default)("body").removeClass("modal-open"),is_submitted||remove_basket()})),modal.show()}))}function in_place_edit_section_name($section){let sectionName="";const $inPlaceEditable=$section.find("h3.sectionname .inplaceeditable");return $inPlaceEditable.length&&(sectionName=$inPlaceEditable.data("value")),sectionName}function on_backup_modal(post_data,title_str,body_str,isSection){_jquery.default.post(get_action_url("rest"),post_data,(function(response){!function(response){let checkbox=!1;"1"===response&&(checkbox=!0),confirm_modal({title:title_str,body:body_str,save_button:str("modal_confirm_backup"),checkbox:checkbox,next:function(data){!0===isSection?function(sectionId,sectionNumber,courseId,userdata){const $commands=(0,_jquery.default)("span.inplaceeditable[data-itemtype=sectionname][data-itemid="+sectionId+"]"),$section=$commands.closest("li.section.main");let sectionName=$section.attr("aria-label")||$section.find(".sectionname").text().trim();null===sectionName&&(sectionName=String((0,_jquery.default)("#region-main .section_action_menu[data-sectionid='"+sectionId+"']").parent().parent().find("h3.sectionname").text()));const inPlaceEditSectionName=in_place_edit_section_name($section);sectionName=""!==inPlaceEditSectionName?inPlaceEditSectionName:sectionName;const $spinner=add_spinner(),$node_spinner=add_node_spinner($commands);_jquery.default.post(get_action_url("rest"),{action:"backup_section",sectionid:sectionId,sectionnumber:sectionNumber,courseid:courseId,sectionname:sectionName,userdata:userdata,sesskey:M.cfg.sesskey},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide(),$node_spinner.hide(),remove_basket()}))}(post_data.sectionid,post_data.sectionnumber,post_data.courseid,data.checkbox):function(cmid,userdata){let $commands=(0,_jquery.default)("#module-"+cmid+" .actions");$commands.length||($commands=(0,_jquery.default)('[data-owner="#module-'+cmid+'"]'));const $spinner=add_spinner(),$node_spinner=add_node_spinner($commands);_jquery.default.post(get_action_url("rest"),{action:"backup",cmid:cmid,userdata:userdata,sesskey:M.cfg.sesskey,courseid:course.id},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$node_spinner.hide(),$spinner.hide(),remove_basket()}))}(post_data.cmid,data.checkbox),function(){if("drag_and_drop"===addMethod){const sharingCartBasket=document.querySelector("button.sharing_cart_basket");null==sharingCartBasket||sharingCartBasket.classList.add("shake_basket")}}()}})}(response)}),"text").fail((function(response){show_error(response)}))}const icon={backup:{css:"editing_backup",iconClass:"fa fa-frown-o"},movedir:{css:"editing_right",iconClass:"fa fa-arrow-right"},move:{css:"editing_move_",iconClass:"fa fa-arrows-v"},edit:{css:"editing_update",iconClass:"fa fa-pencil"},cancel:{css:"editing_cancel",iconClass:"fa fa-ban"},delete:{css:"editing_update",iconClass:"fa fa-trash"},restore:{css:"editing_restore",iconClass:"fa fa-clone"},"dir-open":{iconClass:"fa fa-folder-open-o"},"dir-closed":{iconClass:"fa fa-folder-o"}},$block=(0,_jquery.default)(".block_sharing_cart"),course=new function(){const body=(0,_jquery.default)("body");this.id=body.attr("class").match(/course-(\d+)/)[1],this.is_frontpage=body.hasClass("pagelayout-frontpage")};function show_error(response){try{const ex=JSON.parse(response.responseText);new M.core.exception({name:str("pluginname")+" - "+str("error"),message:ex.message})}catch(e){new M.core.exception({name:str("pluginname")+" - "+str("error"),message:response.responseText})}}function create_command(name){const iconElement=(0,_jquery.default)("").attr("alt",str(name)).attr("class",icon[name].iconClass);return(0,_jquery.default)('').addClass(icon[name].css).attr("title",str(name)).append(iconElement)}function add_spinner(){const $spinner=(0,_jquery.default)('
    ');return(0,_jquery.default)("section.block_sharing_cart").append($spinner),$spinner}function add_node_spinner($node){const $node_spinner=(0,_jquery.default)('');return $node.append($node_spinner),$node_spinner}function reload_tree(){_jquery.default.post(get_action_url("rest"),{action:"render_tree",courseid:course.id},(function(response){$block.find(".tree").replaceWith((0,_jquery.default)(response)),_jquery.default.init_item_tree()}),"html").fail((function(response){show_error(response)}))}(0,_jquery.default)(document).on("click","a.restore",(function(){add_spinner()}));const directories=new function(){const KEY="block_sharing_cart-dirs";let opens=function(param){const readCookie=document.cookie.match("(^|;)\\s*"+param+"\\s*=\\s*([^;]+)");return readCookie?readCookie.pop():""}(KEY).split(",").map((function(v){return parseInt(v)}));function save(){const expires=new Date;expires.setDate(expires.getDate()+30),function(name,value,expireTimeInMillisecond){const d=new Date;d.setTime(d.getTime()+expireTimeInMillisecond);const expires="expires="+d.toUTCString();document.cookie=name+"="+value+";"+expires}(KEY,opens.join(","),expires)}function open($dir,visible){const iconElement=icon[visible?"dir-open":"dir-closed"].iconClass;$dir.find("> div i.icon").attr("class","icon "+iconElement),$dir.find("> ul.list")[visible?"show":"hide"]()}this.init=function(){let i=0;$block.find("li.directory").each((function(index,dir){const $dir=(0,_jquery.default)(dir);$dir.attr("id","block_sharing_cart-dir-"+i),i>=opens.length?opens.push(0):opens[i]&&open($dir,!0),$dir.find("> div div.toggle-wrapper").css("cursor","pointer").on("click",(function(e){!function(e){const $dir=(0,_jquery.default)(e.target).closest("li.directory"),i=$dir.attr("id").match(/(\d+)$/)[1],v="none"===$dir.find("> ul.list").css("display");open($dir,v),opens[i]=v?1:0,save()}(e)})),i++}))},this.reset=function(){opens=[],this.init(),save()}},move_targets=new function(){let $cancel=null,targets=[];this.hide=function(){if(null!==$cancel){const $commands=$cancel.closest(".commands");$cancel.remove(),$cancel=null,$commands.closest("li.activity").css("opacity",1),$commands.find("a").each((function(){(0,_jquery.default)(this).show()})),_jquery.default.each(targets,(function(index,$target){$target.remove()})),targets=[]}},this.show=function(item_id){this.hide();const $current=$block.find("#block_sharing_cart-item-"+item_id),$next=$current.next(),$list=$current.closest("ul");let next_id=0;function create_target(item_id,area_to){const $anchor=(0,_jquery.default)('
    ').addClass("move-"+item_id+"-to-"+area_to).attr("title",str("movehere")).append((0,_jquery.default)("

    "+str("clicktomove")+"

    ").attr("alt",str("movehere"))),$target=(0,_jquery.default)('
  • ').append($anchor);return $anchor.on("click",(function(e){!function(e){const m=(0,_jquery.default)(e.target).closest("a").attr("class").match(/move-(\d+)-to-(\d+)/),item_id=m[1],area_to=m[2],$spinner=add_spinner();_jquery.default.post(get_action_url("rest"),{action:"move",item_id:item_id,area_to:area_to,sesskey:M.cfg.sesskey},(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()}))}(e)})),$target}if($next.length&&(next_id=$next.attr("id").match(/item-(\d+)$/)[1]),$list.find("> li.activity").each((function(index,item){const $item=(0,_jquery.default)(item),to=$item.attr("id").match(/item-(\d+)$/)[1];if(to===item_id){$cancel=create_command("cancel"),$cancel.on("click",(function(){move_targets.hide()}));const $commands=$item.find(".commands");$commands.find("a").each((function(){(0,_jquery.default)(this).hide()})),$commands.append($cancel),$item.css("opacity",.5)}else if(to!==next_id){const $target=create_target(item_id,to);$item.before($target),targets.push($target)}}),this),$next){var $target=create_target(item_id,0);$list.append($target),targets.push($target)}}},restore_targets=new function(){this.is_directory=null;let $clipboard=null,targets=[];function create_target(id,section){const href=get_action_url("restore",{directory:!0===restore_targets.is_directory,target:id,course:course.id,section:section,in_section:(0,_jquery.default)("#copy-section-form").data("in-section"),sesskey:M.cfg.sesskey,returnurl:document.URL});let $target=(0,_jquery.default)("").attr("href",href).attr("title",str("copyhere")).append((0,_jquery.default)('').attr("alt",str("copyhere")).attr("src",M.util.image_url("dropzone_arrow","block_sharing_cart")));return targets.push($target),$target}this.hide=function(){null!==$clipboard&&($clipboard.remove(),$clipboard=null,_jquery.default.each(targets,(function(index,$target){$target.remove()})),targets=[])},this.show=function(id){this.hide();let $view=(0,_jquery.default)("");if(this.is_directory)$view.html(id).css("display","inline"),$view.prepend((0,_jquery.default)("").addClass("icon").attr("alt",id));else{const $item=$block.find("#block_sharing_cart-item-"+id);$view=(0,_jquery.default)($item.find("div")[0].cloneNode(!0)).css("display","inline"),$view.attr("class",$view.attr("class").replace(/mod-indent-\d+/,"")),$view.find(".commands").remove()}const $cancel=create_command("cancel");if($cancel.on("click",this.hide),$clipboard=(0,_jquery.default)('
    '),$clipboard.append(str("clipboard")+": ").append($view).append($cancel),course.is_frontpage){const $sitetopic=(0,_jquery.default)(".sitetopic"),$mainmenu=(0,_jquery.default)(".block_site_main_menu");$sitetopic?$sitetopic.find("*").before($clipboard):$mainmenu&&$mainmenu.find(".content").before($clipboard),$mainmenu&&$mainmenu.find(".footer").before(create_target(id,0)),$sitetopic&&$sitetopic.find("ul.section").append(create_target(id,1))}else{const $container=(0,_jquery.default)(".course-content");$container.prepend($clipboard),$container.find("li.section").each((function(index,sectionDOM){const $section=(0,_jquery.default)(sectionDOM),section=$section.attr("id").match(/(\d+)$/)[1];$section.find("ul.section").first().append(create_target(id,section))}),this)}}};function init_footer_basket(){let currentDragging;const activities=document.querySelectorAll(".activity.activity-wrapper"),sections=document.querySelectorAll(".course-section-header"),sharingCartBlock=document.querySelector('section[data-block="sharing_cart"]');!function(){var _document$getElements;const courseSectionHeader=null!==(_document$getElements=document.getElementsByClassName("course-section-header")[0])&&void 0!==_document$getElements?_document$getElements:null;courseSectionHeader instanceof HTMLElement&&(courseSectionHeader.classList.add("draggable"),courseSectionHeader.setAttribute("draggable",!0))}();const footerIconContainer=document.getElementById("page-footer").querySelector('div[data-region="footer-container-popover"]'),basket=document.createElement("i");basket.setAttribute("class","fa fa-shopping-basket");const basketButton=document.createElement("button");basketButton.setAttribute("class","btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket"),basketButton.setAttribute("style","z-index: 1001;"),basketButton.append(basket);const dropAreaText=document.createElement("p");dropAreaText.setAttribute("class","font-weight-bold text-white"),dropAreaText.innerText=str("drop_here");const dropArea=document.createElement("div");function drag_event_listeners(draggable){draggable.addEventListener("dragstart",(e=>{basketButton.classList.remove("shake_basket"),null==footerIconContainer||footerIconContainer.prepend(basketButton),sharingCartBlock.children[0].classList.add("dragging_item"),sharingCartBlock.append(dropArea),currentDragging=e.target,isDragging=!0})),draggable.addEventListener("dragend",(()=>{currentDragging instanceof HTMLElement&&(null==footerIconContainer||footerIconContainer.removeChild(basketButton)),sharingCartBlock.children[0].classList.remove("dragging_item"),sharingCartBlock.removeChild(dropArea),isDragging=!1}))}dropArea.setAttribute("class","h-100 w-100 position-absolute d-flex justify-content-center align-items-center"),dropArea.append(dropAreaText),sections.forEach((section=>{drag_event_listeners(section)})),activities.forEach((activity=>{drag_event_listeners(activity)})),[basketButton,sharingCartBlock].forEach((dropzone=>{dropzone.addEventListener("dragover",(e=>{e.preventDefault(),dropzone.classList.add("drag_over")})),dropzone.addEventListener("dragenter",(e=>{e.preventDefault(),dropzone.classList.add("drag_over")})),dropzone.addEventListener("dragleave",(()=>{dropzone.classList.remove("drag_over")})),dropzone.addEventListener("drop",(()=>{currentDragging instanceof HTMLElement&¤tDragging.querySelector(".add-to-sharing-cart").click(),dropzone.classList.remove("drag_over"),currentDragging=void 0,isDragging=!1}))}))}_jquery.default.get_plugin_name=function(){let $blockheader=$block.find("h2");return $blockheader.length?$blockheader.html():($blockheader=$block.find("h3"),$blockheader.length?$blockheader.html():"")},_jquery.default.on_backup=function(e,activityName){let cmId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;0===cmId&&(cmId=function($backup){const $activity=$backup.closest("li.activity");if($activity.length)return $activity.attr("id").match(/(\d+)$/)[1];const $commands=$backup.closest(".commands"),dataowner=$commands.attr("data-owner");return dataowner.length?dataowner.match(/(\d+)$/)[1]:$commands.find("a.editing_delete").attr("href").match(/delete=(\d+)/)[1]}((0,_jquery.default)(e.target)));const data={action:"is_userdata_copyable",cmid:cmId};on_backup_modal(data,activityName,str("confirm_backup"),!1)},_jquery.default.on_movedir=function(e){const $commands=(0,_jquery.default)(e.target).closest(".commands"),$current_dir=$commands.closest("li.directory"),current_path=$current_dir.length?$current_dir.attr("directory-path"):"/",item_id=(0,_jquery.default)(e.target).closest("li.activity").attr("id").match(/(\d+)$/)[1],dirs=[];$block.find("li.directory").each((function(){dirs.push((0,_jquery.default)(this).attr("directory-path"))}));const $form=(0,_jquery.default)("");function submit(){const folder_to=$form.find('[name="to"]').val(),$spinner=add_spinner();_jquery.default.post(get_action_url("rest"),{action:"movedir",item_id:item_id,folder_to:folder_to,sesskey:M.cfg.sesskey},(function(){reload_tree(),directories.reset()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()}))}if($form.attr("action","javascript:void(0)"),$form.submit(submit),0===dirs.length){var $input=(0,_jquery.default)('').val(current_path);setTimeout((function(){$input.focus()}),1),$form.append($input)}else{dirs.unshift("/");const $select=(0,_jquery.default)('').val(current_path);$select.remove(),$edit.replaceWith($input),$input.focus()})),$form.append($edit)}const $cancel=create_command("cancel");$cancel.on("click",(function(){$form.remove(),$commands.find("a").show()})),$form.append($cancel),$commands.find("a").each((function(){(0,_jquery.default)(this).hide()})),$commands.append($form)},_jquery.default.on_move=function(e){const id=(0,_jquery.default)(e.target).closest("li.activity").attr("id").match(/(\d+)$/)[1];move_targets.show(id)},_jquery.default.on_delete=function(e){const $item=(0,_jquery.default)(e.target).closest("li"),liText=$item[0].innerText;let modalBody,item,isDirectory=!1,description_text="";$item.hasClass("directory")?(isDirectory=!0,item=str("folder_string"),description_text=str("delete_folder")):item=str("activity_string"),modalBody='

    '+item+" "+liText+description_text+"

    ",confirm_modal({title:str("confirm_delete"),body:modalBody,save_button:str("modal_confirm_delete"),checkbox:!1,next:function(){let data={};!0===isDirectory?data={action:"delete_directory",path:$item.attr("directory-path"),sesskey:M.cfg.sesskey}:$item.hasClass("activity")&&(data={action:"delete",id:$item.attr("id").match(/(\d+)$/)[1],sesskey:M.cfg.sesskey});const $spinner=add_spinner();_jquery.default.post(get_action_url("rest"),data,(function(){reload_tree()})).fail((function(response){show_error(response)})).always((function(){$spinner.hide()})),e.stopPropagation()}})},_jquery.default.on_restore=function(e){const $item=(0,_jquery.default)(e.target).closest("li");let id=null;$item.hasClass("directory")?(id=$item.attr("directory-path"),restore_targets.is_directory=!0):$item.hasClass("activity")&&(id=$item.attr("id").match(/(\d+)$/)[1],restore_targets.is_directory=!1),restore_targets.show(id)},_jquery.default.on_section_backup=function(sectionId,sectionNumber,courseId,sectionName){on_backup_modal({action:"is_userdata_copyable_section",sectionid:sectionId,sectionnumber:sectionNumber,courseid:courseId},sectionName,'

    '+str("backup_heavy_load_warning_message")+"

    "+str("confirm_backup_section"),!0)},_jquery.default.init_bulk_delete=function(isspeciallayout){const bulkdelete=$block.find(".editing_bulkdelete");bulkdelete.length&&(isspeciallayout?(bulkdelete.attr("role","menuitem").addClass("dropdown-item menu-action"),bulkdelete.append((0,_jquery.default)("").append(bulkdelete.attr("title"))),$block.find(".menubar .dropdown .dropdown-menu").append(bulkdelete)):$block.find(".header .commands").append(bulkdelete))},_jquery.default.init_help_icon=function(isspeciallayout){const helpicon=$block.find(".header-commands > .help-icon");isspeciallayout?$block.find(".header-commands").parent().css("display","block"):$block.find(".header .commands").append(helpicon)},_jquery.default.init_block_header=function(){const isspeciallayout=$block.find(".menubar .dropdown .dropdown-menu").length;_jquery.default.init_bulk_delete(isspeciallayout),_jquery.default.init_help_icon(isspeciallayout)},_jquery.default.init_item_tree=function(){function add_actions(item,actions){const $item=(0,_jquery.default)(item),isCopying="1"===$item.attr("data-is-copying"),$commands=$item.find(".commands").first();_jquery.default.each(actions,(function(index,action){if("restore"===action&&isCopying)return;const $command=create_command(action);$command.on("click",(function(e){_jquery.default["on_"+action](e)})),$commands.append($command)}),this)}const activity_actions=["movedir","move","delete"];course&&activity_actions.push("restore");const directory_actions=["delete","restore"];$block.find("li.activity").each((function(index,item){1!=(0,_jquery.default)(item).attr("data-disable-copy")?add_actions(item,activity_actions):add_actions(item,["movedir","move","delete"])})),$block.find("li.directory").each((function(index,item){add_actions(item,directory_actions)})),directories.init()},_jquery.default.init_activity_commands=function(){function create_backup_icon(){const $backupIcon=(0,_jquery.default)('
    ').append((0,_jquery.default)('')).attr("title",str("backup"));return"click_to_add"!==addMethod&&$backupIcon.addClass("d-none"),$backupIcon}function add_activity_backup_control($activity){const activityClass=$activity[0].className,modtype=activityClass.substr(activityClass.indexOf("modtype_")+8);let activityName=str("activity_string");"label"!==modtype&&(activityName=(0,_jquery.default)(".activity#"+$activity[0].id).find(".mod-indent-outer .activityinstance span.instancename").html());const $backupIcon=create_backup_icon();$backupIcon.on("click",(function(e){_jquery.default.on_backup(e,activityName)}));const $actionMenuItem=$activity.find(".action-menu.section-cm-edit-actions").parent(".actions");$actionMenuItem.find(".add-to-sharing-cart").length||$actionMenuItem.append($backupIcon)}(0,_jquery.default)(document).ajaxComplete((function(event,xhr,settings){const url=settings.url,lastslashindex=url.lastIndexOf("="),result=url.substring(lastslashindex+1);if("core_course_edit_module"===result||"core_course_get_module"===result){const data=JSON.parse(settings.data),action=data[0].args.action;if("delete"===action)return;setTimeout((function(){const activity_id=data[0].args.id,activity=(0,_jquery.default)("#module-"+activity_id);if(add_activity_backup_control(activity),"duplicate"===action){add_activity_backup_control(activity.next())}}),1)}})),(0,_jquery.default)("body.editing .course-content li.section").each((function(){!function($section){let sectionId=$section.find(".section_action_menu").data("sectionid");const sectionNumber=parseInt(String($section.attr("id")).match(/\d+/)[0]);let sectionName=$section.attr("aria-label")||$section.find(".sectionname").text().trim();const isFlexibleCourseFormat=(0,_jquery.default)("body[id$=flexsections]").length;isFlexibleCourseFormat&&null==sectionId&&(sectionId=$section.data("section-id"));const courseId=parseInt(String((0,_jquery.default)("body").attr("class")).match(/course-([0-9]*)( |$)/)[1]),$backupIcon=create_backup_icon();$backupIcon.on("click",(function(){const inPlaceEditSectionName=in_place_edit_section_name($section);sectionName=""!==inPlaceEditSectionName?inPlaceEditSectionName:sectionName,_jquery.default.on_section_backup(sectionId,sectionNumber,courseId,sectionName)}));let $sectionTitle=$section.find("h3.sectionname").first().find("a").last();const $inPlaceEditable=$section.find("h3.sectionname .inplaceeditable").first();$inPlaceEditable.length&&($sectionTitle=$inPlaceEditable),isFlexibleCourseFormat&&0===sectionNumber?($sectionTitle=$section.find("> .controls"),$sectionTitle.prepend($backupIcon)):$backupIcon.insertAfter($sectionTitle);const $activities=$section.find("li.activity");(0,_jquery.default)($activities).each((function(){add_activity_backup_control((0,_jquery.default)(this))}))}((0,_jquery.default)(this))}))},_jquery.default.init=function(){M.str.block_sharing_cart.pluginname=this.get_plugin_name(),_jquery.default.init_block_header(),_jquery.default.init_item_tree(),_jquery.default.init_activity_commands(),"drag_and_drop"===addMethod&&init_footer_basket()};var $spinner=(0,_jquery.default)("").addClass("spinner fa fa-3x fa-circle-o-notch fa-spin");(0,_jquery.default)("div#sharing-cart-spinner-modal div.spinner-container").prepend($spinner),_jquery.default.init()})),(0,_jquery.default)(".copy_section").on("click",(function(){const $section_selected=(0,_jquery.default)(".section-dropdown option:selected"),sectionId=$section_selected.data("section-id"),sectionNumber=$section_selected.data("section-number"),courseId=$section_selected.data("course-id"),sectionName=$section_selected.data("section-name");_jquery.default.on_section_backup(sectionId,sectionNumber,courseId,sectionName)})),(0,_jquery.default)(".copy_activity").on("click",(function(e){const activitySelected=(0,_jquery.default)(".activity-dropdown option:selected"),activityId=activitySelected.data("activity-id"),activityName=activitySelected.data("activity-name");_jquery.default.on_backup(e,activityName,activityId)}))}})); //# sourceMappingURL=script.min.js.map \ No newline at end of file diff --git a/amd/build/script.min.js.map b/amd/build/script.min.js.map index f1010f0..9eb5e23 100644 --- a/amd/build/script.min.js.map +++ b/amd/build/script.min.js.map @@ -1 +1 @@ -{"version":3,"file":"script.min.js","sources":["../src/script.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 * Sharing Cart\n *\n * @package block_sharing_cart\n * @copyright 2017 (C) VERSION2, INC.\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\n\n/**\n * @param {string} addMethod\n */\nexport const init = function(addMethod) {\n $(document).ready(function() {\n let isDragging = false;\n\n /**\n * Returns a localized string\n *\n * @param {String} identifier\n * @return {String}\n */\n function str(identifier) {\n return M.str.block_sharing_cart[identifier] || M.str.moodle[identifier];\n }\n\n /**\n * Get an action URL\n *\n * @param {String} name The action name\n * @param {Object} [args] The action parameters\n * @return {String}\n */\n function get_action_url(name, args) {\n let url = M.cfg.wwwroot + '/blocks/sharing_cart/' + name + '.php';\n if (args) {\n const q = [];\n for (let k in args) {\n q.push(k + '=' + encodeURIComponent(args[k]));\n }\n url += '?' + q.join('&');\n }\n return url;\n }\n\n /**\n * Shake the basket to indicate cancel/submit\n */\n function shake_basket() {\n if (addMethod === 'drag_and_drop') {\n const sharingCartBasket = document.querySelector('button.sharing_cart_basket');\n sharingCartBasket?.classList.add('shake_basket');\n }\n }\n\n /**\n * Remove the shake effect and basket icon\n */\n function remove_basket() {\n if (addMethod === 'drag_and_drop' && !isDragging) {\n const footer = document.getElementById('page-footer');\n const footerIconContainer = footer.querySelector('div[data-region=\"footer-container-popover\"]');\n const sharingCartBasket = document.querySelector('button.sharing_cart_basket');\n\n if (sharingCartBasket) {\n footerIconContainer?.removeChild(sharingCartBasket);\n sharingCartBasket.classList.remove('shake_basket');\n }\n }\n }\n\n /**\n * Modal called when confirming an action.\n *\n * @param obj\n */\n function confirm_modal(obj) {\n\n // Checkbox for copying userdata confirmation.\n if (obj.checkbox) {\n obj.body +=\n '
    ' +\n '
    ' +\n '' +\n '' +\n '
    ' +\n '
    ' +\n '' +\n '' +\n '
    ' +\n '
    ';\n }\n\n\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: obj.title,\n body: obj.body,\n }).done(function(modal) {\n let is_submitted = false;\n modal.setSaveButtonText(obj.save_button);\n\n const userdata_checkbox = $(modal.getRoot()).find('#modal-userdata-checkbox');\n const anonymize_checkbox = $(modal.getRoot()).find('#modal-anonymize-checkbox');\n\n userdata_checkbox.on('change', (e) => {\n if (e.currentTarget.checked) {\n anonymize_checkbox.attr('disabled', null);\n } else {\n anonymize_checkbox.prop('checked', false).attr('disabled', true);\n }\n });\n\n // On save save check - if checkbox is checked.\n modal.getRoot().on(ModalEvents.save, function(e) {\n const response = {\n 'userdata': userdata_checkbox.is(':checked'),\n 'anonymize': anonymize_checkbox.is(':checked'),\n };\n\n obj.next(response);\n is_submitted = true;\n });\n\n modal.getRoot().on(ModalEvents.cancel, function() {\n remove_basket();\n });\n\n // Remove modal from html.\n modal.getRoot().on(ModalEvents.hidden, function() {\n $('body').removeClass('modal-open');\n\n if (!is_submitted) {\n remove_basket();\n }\n });\n\n modal.show();\n });\n }\n\n /**\n * Get the section name from the section when\n * it's changed with the in place editor\n *\n * @param $section\n * @returns {*}\n */\n function in_place_edit_section_name($section) {\n let sectionName = '';\n const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable');\n if ($inPlaceEditable.length) {\n sectionName = $inPlaceEditable.data('value');\n }\n return sectionName;\n }\n\n /**\n * @param post_data\n * @param title_str\n * @param body_str\n * @param isSection\n */\n function on_backup_modal(post_data, title_str, body_str, isSection) {\n (function(on_success) {\n $.post(get_action_url('rest'), post_data,\n function(response) {\n on_success(response);\n }, \"text\")\n .fail(function(response) {\n show_error(response);\n });\n })(function(response) {\n const copyable = response === '1';\n let checkbox = false;\n\n if (copyable) {\n checkbox = true;\n }\n\n confirm_modal({\n 'title': title_str,\n 'body': body_str,\n 'save_button': str('modal_confirm_backup'),\n 'checkbox': checkbox,\n 'next': function(data) {\n if (isSection === true) {\n backup_section(post_data.sectionid, post_data.sectionnumber, post_data.courseid, data.userdata, data.anonymize);\n } else {\n backup(post_data.cmid, data.userdata, data.anonymize);\n }\n\n shake_basket();\n }\n });\n });\n }\n\n /** @var {Object} The icon configurations */\n const icon = {\n // Actions\n 'backup': {\n css: 'editing_backup',\n iconClass: 'fa fa-frown-o',\n },\n 'movedir': {\n css: 'editing_right',\n iconClass: 'fa fa-arrow-right',\n },\n 'move': {\n css: 'editing_move_',\n iconClass: 'fa fa-arrows-v',\n },\n 'edit': {\n css: 'editing_update',\n iconClass: 'fa fa-pencil',\n },\n 'cancel': {\n css: 'editing_cancel',\n iconClass: 'fa fa-ban',\n },\n 'delete': {\n css: 'editing_update',\n iconClass: 'fa fa-trash',\n },\n 'restore': {\n css: 'editing_restore',\n iconClass: 'fa fa-clone',\n },\n // Directories\n 'dir-open': {\n iconClass: 'fa fa-folder-open-o'\n },\n 'dir-closed': {\n iconClass: 'fa fa-folder-o'\n },\n };\n\n /** @var {Node} The Sharing Cart block container node */\n const $block = $('.block_sharing_cart');\n\n /** @var {Object} The current course */\n const course = new function () {\n const body = $('body');\n this.id = body.attr('class').match(/course-(\\d+)/)[1];\n this.is_frontpage = body.hasClass('pagelayout-frontpage');\n }();\n\n /**\n * Shows an error message with given Ajax error\n *\n * @param {Object} response The Ajax response\n */\n function show_error(response) {\n try {\n const ex = JSON.parse(response.responseText);\n new M.core.exception({\n name: str('pluginname') + ' - ' + str('error'),\n message: ex.message\n });\n } catch (e) {\n new M.core.exception({\n name: str('pluginname') + ' - ' + str('error'),\n message: response.responseText\n });\n }\n }\n\n /**\n * Check special layout (theme boost)\n *\n * @return {Boolean}\n */\n function verify_layout() {\n const menuelement = $block.find('.menubar .dropdown .dropdown-menu');\n return (menuelement.length);\n }\n\n /**\n * Set Cookie\n * @param name\n * @param value\n * @param expireTimeInMillisecond\n */\n function setCookie(name, value, expireTimeInMillisecond) {\n const d = new Date();\n d.setTime(d.getTime() + expireTimeInMillisecond);\n const expires = 'expires=' + d.toUTCString();\n document.cookie = name + '=' + value + ';' + expires + '';\n }\n\n /**\n * Get Cookie Value\n * @param param\n * @returns {*}\n */\n function getCookieValue(param) {\n const readCookie = document.cookie.match('(^|;)\\\\s*' + param + '\\\\s*=\\\\s*([^;]+)');\n return readCookie ? readCookie.pop() : '';\n }\n\n /**\n * Create a command icon\n *\n * @param {String} name The command name, predefined in icon\n * @param {String} [pix] The icon pix name to override\n */\n function create_command(name) {\n const iconElement = $('')\n .attr('alt', str(name))\n .attr('class', icon[name].iconClass);\n // If (verify_layout()) {\n // iconElement.addClass('iconcustom');\n // }\n\n return $('
    ')\n .addClass(icon[name].css)\n .attr('title', str(name))\n .append(iconElement);\n }\n\n /**\n * Create a spinner\n * @param $node\n * @returns {*|jQuery}\n */\n function add_spinner() {\n const $spinner = ($('
    '));\n $('section.block_sharing_cart').append($spinner);\n return $spinner;\n }\n\n /**\n *\n * @param $node\n * @returns {jQuery.fn.init}\n */\n function add_node_spinner($node) {\n const $node_spinner = $('');\n $node.append($node_spinner);\n return $node_spinner;\n }\n\n $(document).on('click', 'a.restore', function() {\n add_spinner();\n });\n\n /**\n *\n * Reload the Sharing Cart item tree\n */\n function reload_tree() {\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"render_tree\",\n \"courseid\": course.id\n },\n function(response) {\n $block.find(\".tree\").replaceWith($(response));\n $.init_item_tree();\n }, \"html\")\n .fail(function(response) {\n show_error(response);\n });\n }\n\n /**\n * Backup an activity\n *\n * @param {int} cmid\n * @param {Boolean} userdata\n * @param {Boolean} anonymize\n */\n function backup(cmid, userdata, anonymize) {\n let $commands = $('#module-' + cmid + ' .actions');\n if (!$commands.length) {\n $commands = $('[data-owner=\"#module-' + cmid + '\"]');\n }\n\n const $spinner = add_spinner();\n const $node_spinner = add_node_spinner($commands);\n\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"backup\",\n \"cmid\": cmid,\n \"userdata\": userdata,\n \"anonymize\": anonymize,\n \"sesskey\": M.cfg.sesskey,\n \"courseid\": course.id\n },\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $node_spinner.hide();\n $spinner.hide();\n remove_basket();\n });\n }\n\n /**\n * Backup an activities in a section\n *\n * @param {int} sectionId\n * @param {int} sectionNumber\n * @param {int} courseId\n * @param {Boolean} userdata\n * @param {Boolean} anonymize\n */\n function backup_section(sectionId, sectionNumber, courseId, userdata, anonymize) {\n const $commands = $('span.inplaceeditable[data-itemtype=sectionname][data-itemid=' + sectionId + ']');\n const $section = $commands.closest(\"li.section.main\");\n let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim();\n\n if (sectionName === null) {\n sectionName = String($('#region-main .section_action_menu[data-sectionid=\\'' + sectionId + '\\']')\n .parent().parent().find('h3.sectionname').text());\n }\n\n const inPlaceEditSectionName = in_place_edit_section_name($section);\n sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName;\n\n const $spinner = add_spinner();\n const $node_spinner = add_node_spinner($commands);\n\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"backup_section\",\n \"sectionid\": sectionId,\n \"sectionnumber\": sectionNumber,\n \"courseid\": courseId,\n \"sectionname\": sectionName,\n \"userdata\": userdata,\n \"anonymize\": anonymize,\n \"sesskey\": M.cfg.sesskey\n },\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n $node_spinner.hide();\n remove_basket();\n });\n }\n\n\n // /////// CLASSES /////////\n\n /**\n * @class Directory states manager\n */\n const directories = new function () {\n const KEY = 'block_sharing_cart-dirs';\n\n let opens = getCookieValue(KEY).split(',').map(function (v) {\n return parseInt(v);\n });\n\n function save() {\n const expires = new Date();\n expires.setDate(expires.getDate() + 30);\n setCookie(KEY, opens.join(','), expires);\n }\n\n function open($dir, visible) {\n const iconIndex = visible ? 'dir-open' : 'dir-closed';\n const iconElement = icon[iconIndex].iconClass;\n $dir.find('> div i.icon').attr('class', 'icon ' + iconElement);\n $dir.find('> ul.list')[visible ? 'show' : 'hide']();\n }\n\n function toggle(e) {\n const $dir = $(e.target).closest('li.directory');\n const i = $dir.attr('id').match(/(\\d+)$/)[1];\n const v = $dir.find('> ul.list').css('display') === 'none';\n\n open($dir, v);\n opens[i] = v ? 1 : 0;\n save();\n }\n\n /**\n * Initialize directory states\n */\n this.init = function () {\n let i = 0;\n $block.find('li.directory').each(function (index, dir) {\n const $dir = $(dir);\n $dir.attr('id', 'block_sharing_cart-dir-' + i);\n if (i >= opens.length) {\n opens.push(0);\n } else if (opens[i]) {\n open($dir, true);\n }\n $dir.find('> div div.toggle-wrapper').css('cursor', 'pointer').on('click', function (e) {\n toggle(e);\n });\n i++;\n });\n };\n\n /**\n * Reset directory states\n */\n this.reset = function () {\n opens = [];\n this.init();\n save();\n };\n }();\n\n /**\n * @class Targets for moving an item directory\n */\n const move_targets = new function () {\n let $cancel = null,\n targets = [];\n\n /**\n * Hide move targets\n */\n this.hide = function () {\n if ($cancel !== null) {\n const $commands = $cancel.closest('.commands');\n $cancel.remove();\n $cancel = null;\n $commands.closest('li.activity').css('opacity', 1.0);\n $commands.find('a').each(function () {\n $(this).show();\n });\n $.each(targets, function (index, $target) {\n $target.remove();\n });\n targets = [];\n }\n };\n\n /**\n * Show move targets for a given item\n *\n * @param {int} id The item ID\n */\n this.show = function (item_id) {\n this.hide();\n\n function move(e) {\n\n const m = $(e.target).closest('a').attr('class').match(/move-(\\d+)-to-(\\d+)/);\n const item_id = m[1],\n area_to = m[2];\n\n const $spinner = add_spinner();\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"move\",\n \"item_id\": item_id,\n \"area_to\": area_to,\n \"sesskey\": M.cfg.sesskey\n },\n function () {\n reload_tree();\n })\n .fail(function (response) {\n show_error(response);\n })\n .always(function () {\n $spinner.hide();\n });\n }\n\n const $current = $block.find('#block_sharing_cart-item-' + item_id);\n const $next = $current.next();\n const $list = $current.closest('ul');\n\n let next_id = 0;\n if ($next.length) {\n next_id = $next.attr('id').match(/item-(\\d+)$/)[1];\n }\n\n /**\n *\n * @param item_id\n * @param area_to\n * @returns {jQuery}\n */\n function create_target(item_id, area_to) {\n const $anchor = $('
    ')\n .addClass('move-' + item_id + '-to-' + area_to)\n .attr('title', str('movehere'))\n .append(\n $('

    ' + str('clicktomove') + '

    ')\n .attr('alt', str('movehere'))\n );\n\n const $target = $('
  • ')\n .append($anchor);\n $anchor.on('click', function (e) {\n move(e);\n });\n\n return $target;\n }\n\n $list.find('> li.activity').each(function (index, item) {\n const $item = $(item);\n const to = $item.attr('id').match(/item-(\\d+)$/)[1];\n if (to === item_id) {\n $cancel = create_command('cancel', 't/left');\n $cancel.on('click', function () {\n move_targets.hide();\n });\n const $commands = $item.find('.commands');\n $commands.find('a').each(function () {\n $(this).hide();\n });\n $commands.append($cancel);\n $item.css('opacity', 0.5);\n } else if (to !== next_id) {\n const $target = create_target(item_id, to);\n $item.before($target);\n targets.push($target);\n }\n }, this);\n\n if ($next) {\n var $target = create_target(item_id, 0);\n $list.append($target);\n targets.push($target);\n }\n };\n }();\n\n /**\n * @class Targets for restoring an item\n */\n const restore_targets = new function () {\n this.is_directory = null;\n let $clipboard = null,\n targets = [];\n\n /**\n *\n * @param id\n * @param section\n * @returns {jQuery}\n */\n\n function create_target(id, section) {\n const href = get_action_url('restore', {\n 'directory': (restore_targets.is_directory === true),\n 'target': id,\n 'course': course.id,\n 'section': section,\n 'in_section': $('#copy-section-form').data('in-section'),\n 'sesskey': M.cfg.sesskey,\n 'returnurl': document.URL,\n });\n\n let $target = $('').attr('href', href).attr('title', str('copyhere')).append(\n $('').attr('alt', str('copyhere')).attr('src', M.util.image_url('dropzone_arrow', 'block_sharing_cart'))\n );\n\n targets.push($target);\n\n return $target;\n }\n\n /**\n * Hide restore targets\n */\n this.hide = function () {\n if ($clipboard !== null) {\n $clipboard.remove();\n $clipboard = null;\n $.each(targets, function (index, $target) {\n $target.remove();\n });\n targets = [];\n }\n };\n\n /**\n *\n *\n * @param {int} id The item ID\n */\n this.show = function (id) {\n this.hide();\n\n let $view = $(\"\");\n\n if (this.is_directory) {\n $view.html(id).css('display', 'inline');\n $view.prepend(\n $(\"\").addClass(\"icon\")\n .attr(\"alt\", id)\n // .attr(\"src\", M.util.image_url(icon['dir-closed'].pix, null))\n );\n } else {\n const $item = $block.find('#block_sharing_cart-item-' + id);\n $view = $($item.find('div')[0].cloneNode(true)).css('display', 'inline');\n $view.attr('class', $view.attr('class').replace(/mod-indent-\\d+/, ''));\n $view.find('.commands').remove();\n }\n\n const $cancel = create_command('cancel');\n\n $cancel.on('click', this.hide);\n\n $clipboard = $('
    ');\n $clipboard.append(str('clipboard') + \": \").append($view).append($cancel);\n\n if (course.is_frontpage) {\n const $sitetopic = $('.sitetopic');\n const $mainmenu = $('.block_site_main_menu');\n if ($sitetopic) {\n $sitetopic.find('*').before($clipboard);\n } else if ($mainmenu) {\n $mainmenu.find('.content').before($clipboard);\n }\n\n // Mainmenu = section #0, sitetopic = section #1\n if ($mainmenu) {\n $mainmenu.find('.footer').before(create_target(id, 0));\n }\n if ($sitetopic) {\n $sitetopic.find('ul.section').append(create_target(id, 1));\n }\n } else {\n const $container = $('.course-content');\n $container.prepend($clipboard);\n $container.find(M.course.format.get_section_wrapper(null)).each(function (index, sectionDOM) {\n const $section = $(sectionDOM);\n const section = $section.attr('id').match(/(\\d+)$/)[1];\n $section.find('ul.section').first().append(create_target(id, section));\n }, this);\n }\n };\n }();\n\n // /////// INITIALIZATION /////////\n\n /**\n *\n * @returns {string|*}\n */\n $.get_plugin_name = function() {\n let $blockheader = $block.find(\"h2\");\n\n if (!$blockheader.length) {\n $blockheader = $block.find(\"h3\");\n\n if ($blockheader.length) {\n return $blockheader.html();\n }\n } else {\n return $blockheader.html();\n }\n\n return \"\";\n };\n\n /**\n *\n * @param e\n * @param activityName\n * @param {int} cmId\n */\n $.on_backup = function(e, activityName, cmId = 0) {\n if (cmId === 0) {\n cmId = (function ($backup) {\n const $activity = $backup.closest('li.activity');\n if ($activity.length) {\n return $activity.attr('id').match(/(\\d+)$/)[1];\n }\n const $commands = $backup.closest('.commands');\n const dataowner = $commands.attr('data-owner');\n if (dataowner.length) {\n return dataowner.match(/(\\d+)$/)[1];\n }\n return $commands.find('a.editing_delete').attr('href').match(/delete=(\\d+)/)[1];\n })($(e.target));\n }\n\n const data =\n {\n \"action\": \"is_userdata_copyable\",\n \"cmid\": cmId\n };\n\n on_backup_modal(data, activityName, str('confirm_backup'), false);\n };\n\n /**\n * On movedir command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_movedir = function(e) {\n const $commands = $(e.target).closest('.commands');\n\n const $current_dir = $commands.closest('li.directory');\n const current_path = $current_dir.length ? $current_dir.attr('directory-path') : '/';\n\n const item_id = $(e.target).closest('li.activity').attr('id').match(/(\\d+)$/)[1];\n\n const dirs = [];\n $block.find('li.directory').each(function() {\n dirs.push($(this).attr('directory-path'));\n });\n\n const $form = $('');\n // eslint-disable-next-line no-script-url\n $form.attr('action', 'javascript:void(0)');\n\n function submit() {\n const folder_to = $form.find('[name=\"to\"]').val();\n const $spinner = add_spinner();\n $.post(get_action_url('rest'),\n {\n \"action\": \"movedir\",\n \"item_id\": item_id,\n \"folder_to\": folder_to,\n \"sesskey\": M.cfg.sesskey\n },\n function() {\n reload_tree();\n directories.reset();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n });\n }\n\n $form.submit(submit);\n\n if (dirs.length === 0) {\n var $input = $('').val(current_path);\n setTimeout(function() {\n $input.focus();\n }, 1);\n $form.append($input);\n } else {\n dirs.unshift('/');\n\n const $select = $('').val(current_path);\n $select.remove();\n $edit.replaceWith($input);\n $input.focus();\n });\n\n $form.append($edit);\n }\n\n const $cancel = create_command('cancel');\n $cancel.on('click', function() {\n $form.remove();\n $commands.find('a').show();\n });\n $form.append($cancel);\n\n $commands.find('a').each(function() {\n $(this).hide();\n });\n $commands.append($form);\n };\n\n /**\n * On move command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_move = function(e) {\n const $item = $(e.target).closest('li.activity');\n const id = $item.attr('id').match(/(\\d+)$/)[1];\n\n move_targets.show(id);\n };\n\n /**\n * On delete command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_delete = function(e) {\n const $item = $(e.target).closest('li');\n const liText = $item[0].innerText;\n\n let isDirectory = false;\n let modalBody;\n let item;\n let description_text = '';\n\n if ($item.hasClass(\"directory\")) {\n isDirectory = true;\n item = str('folder_string');\n description_text = str('delete_folder');\n } else {\n item = str('activity_string');\n }\n\n modalBody = '

    ' + item + ' ' + liText + description_text + '

    ';\n\n confirm_modal({\n 'title': str('confirm_delete'),\n 'body': modalBody,\n 'save_button': str('modal_confirm_delete'),\n 'checkbox': false,\n 'next': function() {\n\n let data = {};\n\n if (isDirectory === true) {\n data = {\n \"action\": \"delete_directory\",\n \"path\": $item.attr(\"directory-path\"),\n \"sesskey\": M.cfg.sesskey\n };\n } else if ($item.hasClass(\"activity\")) {\n data = {\n \"action\": \"delete\",\n \"id\": $item.attr('id').match(/(\\d+)$/)[1],\n \"sesskey\": M.cfg.sesskey\n };\n }\n\n const $spinner = add_spinner();\n\n $.post(get_action_url(\"rest\"), data,\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n });\n\n e.stopPropagation();\n }\n });\n };\n\n /**\n * On restore command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_restore = function(e) {\n const $item = $(e.target).closest('li');\n let id = null;\n\n if ($item.hasClass(\"directory\")) {\n id = $item.attr(\"directory-path\");\n restore_targets.is_directory = true;\n } else if ($item.hasClass(\"activity\")) {\n id = $item.attr('id').match(/(\\d+)$/)[1];\n restore_targets.is_directory = false;\n }\n\n restore_targets.show(id);\n };\n\n /**\n * On backup the whole section as a folder\n *\n * @param {int} sectionId\n * @param {int} sectionNumber\n * @param {int} courseId\n * @param {string} sectionName\n */\n $.on_section_backup = function(sectionId, sectionNumber, courseId, sectionName) {\n const data =\n {\n \"action\": \"is_userdata_copyable_section\",\n \"sectionid\": sectionId,\n \"sectionnumber\": sectionNumber,\n \"courseid\": courseId,\n };\n\n const body_html = '

    ' + str('backup_heavy_load_warning_message') +\n '

    ' + str('confirm_backup_section');\n\n on_backup_modal(data, sectionName, body_html, true);\n };\n\n /**\n * Initialize the delete bulk\n */\n $.init_bulk_delete = function(isspeciallayout) {\n const bulkdelete = $block.find('.editing_bulkdelete');\n if (bulkdelete.length) {\n if (isspeciallayout) {\n bulkdelete.attr('role', 'menuitem').addClass('dropdown-item menu-action');\n bulkdelete.append($(\"\").append(bulkdelete.attr('title')));\n\n $block.find('.menubar .dropdown .dropdown-menu').append(bulkdelete);\n } else {\n $block.find('.header .commands').append(bulkdelete);\n }\n }\n };\n\n /**\n * Initialize the help icon\n */\n $.init_help_icon = function(isspeciallayout) {\n const helpicon = $block.find('.header-commands > .help-icon');\n\n if (isspeciallayout) {\n $block.find('.header-commands').parent().css('display', 'block');\n } else {\n $block.find('.header .commands').append(helpicon);\n }\n };\n\n /**\n * Initialize the Sharing Cart block header\n */\n $.init_block_header = function() {\n const isspeciallayout = verify_layout();\n $.init_bulk_delete(isspeciallayout);\n $.init_help_icon(isspeciallayout);\n };\n\n /**\n * Initialize the Sharing Cart item tree\n */\n $.init_item_tree = function() {\n function add_actions(item, actions) {\n const $item = $(item);\n const isCopying = $item.attr('data-is-copying') === '1';\n const $commands = $item.find('.commands').first();\n\n $.each(actions, function(index, action) {\n if (action === 'restore' && isCopying) {\n return;\n }\n const $command = create_command(action);\n $command.on('click', function(e) {\n $['on_' + action](e);\n });\n $commands.append($command);\n }, this);\n }\n\n const activity_actions = ['movedir', 'move', 'delete'];\n if (course) {\n activity_actions.push('restore');\n }\n\n const directory_actions = ['delete', 'restore'];\n\n // Initialize items\n $block.find('li.activity').each(function(index, item) {\n if($(item).attr('data-disable-copy') == 1) {\n add_actions(item, ['movedir', 'move', 'delete']);\n return;\n }\n add_actions(item, activity_actions);\n });\n\n // Initialize directory items\n $block.find('li.directory').each(function(index, item) {\n add_actions(item, directory_actions);\n });\n\n // Initialize directories\n directories.init();\n };\n\n /**\n * Extract html object from area where moodle ajax was called.\n *\n * Call add_activity_backup_control to re append sharing cart icon.\n */\n $.init_activity_commands = function() {\n $(document).ajaxComplete(function(event, xhr, settings) {\n\n const url = settings.url;\n const lastslashindex = url.lastIndexOf('=');\n const result = url.substring(lastslashindex + 1);\n\n if (result === 'core_course_edit_module' || result === 'core_course_get_module') {\n\n const data = JSON.parse(settings.data);\n const action = data[0].args.action;\n\n // Don't try to add icon if activity has been deleted.\n if (action === 'delete') {\n return;\n }\n\n setTimeout(function() {\n const activity_id = data[0].args.id;\n const activity = $('#module-' + activity_id);\n add_activity_backup_control(activity);\n\n if (action === 'duplicate') {\n const duplicated = activity.next();\n add_activity_backup_control(duplicated);\n }\n }, 1);\n }\n });\n\n /**\n * Create the backup icon\n *\n * @returns $backupIcon\n */\n function create_backup_icon() {\n\n const $backupIcon = $('
    ')\n .append($(''))\n .attr('title', str('backup'));\n\n if (addMethod !== 'click_to_add') {\n $backupIcon.addClass('d-none');\n }\n\n return $backupIcon;\n }\n\n /**\n * Add backup control with a click event to an activity\n * Added fix for copying an activity without backup routine\n *\n * @param $activity\n */\n function add_activity_backup_control($activity) {\n\n const activityClass = $activity[0].className;\n\n // Selecting modtype without prefix.\n const modtype = activityClass.substr(activityClass.indexOf('modtype_') + 8);\n\n // Default activity name.\n let activityName = str('activity_string');\n\n // Label is using a different html / css layout, so it's needed to get the name by using another $find.\n if (modtype !== 'label') {\n activityName = $('.activity#' + $activity[0].id)\n .find('.mod-indent-outer .activityinstance span.instancename')\n .html();\n }\n\n const $backupIcon = create_backup_icon();\n\n $backupIcon.on('click', function(e) {\n $.on_backup(e, activityName);\n });\n\n const $actionMenuItem = $activity.find('.action-menu.section-cm-edit-actions').parent('.actions');\n\n if (!$actionMenuItem.find('.add-to-sharing-cart').length) {\n $actionMenuItem.append($backupIcon);\n }\n }\n\n /**\n * Add backup control with a click event to a section\n *\n * @param $section\n */\n function add_section_backup_control($section) {\n\n let sectionId = $section.find('.section_action_menu').data('sectionid');\n const sectionNumber = parseInt(String($section.attr('id')).match(/\\d+/)[0]);\n let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim();\n\n const isFlexibleCourseFormat = $('body[id$=flexsections]').length;\n\n // Extract the section ID from the section if this is a Flexible\n // course format (since this format doesn't have an action menu)\n if (isFlexibleCourseFormat && (typeof sectionId === 'undefined' || sectionId === null)) {\n sectionId = $section.data('section-id');\n }\n\n // A bit unsafe to extract the course ID from the body but it's the best option we got at the moment\n const courseId = parseInt(String($('body').attr('class')).match(/course-([0-9]*)( |$)/)[1]);\n\n const $backupIcon = create_backup_icon();\n\n $backupIcon.on('click', function() {\n const inPlaceEditSectionName = in_place_edit_section_name($section);\n sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName;\n $.on_section_backup(sectionId, sectionNumber, courseId, sectionName);\n });\n\n let $sectionTitle = $section.find('h3.sectionname').first().find('a').last();\n\n const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable').first();\n if ($inPlaceEditable.length) {\n $sectionTitle = $inPlaceEditable;\n }\n\n // Add the backup icon after the cog wheel if this is a Flexible course format\n if (isFlexibleCourseFormat && sectionNumber === 0) {\n $sectionTitle = $section.find('> .controls');\n $sectionTitle.prepend($backupIcon);\n } else {\n $backupIcon.insertAfter($sectionTitle);\n }\n\n const activitySelector = 'li.activity';\n\n const $activities = $section.find(activitySelector);\n\n $($activities).each(function() {\n add_activity_backup_control($(this));\n });\n }\n\n $(\"body.editing .course-content li.section\").each(function() {\n add_section_backup_control($(this));\n });\n };\n\n /**\n * Initialize the Sharing Cart footer basket for 4.0+.\n */\n function init_footer_basket() {\n let currentDragging;\n const activities = document.querySelectorAll(\".activity.activity-wrapper\");\n const sections = document.querySelectorAll(\".course-section-header\");\n const sharingCartBlock = document.querySelector('section[data-block=\"sharing_cart\"]');\n\n add_draggable_to_first_section();\n\n const footer = document.getElementById('page-footer');\n const footerIconContainer = footer.querySelector('div[data-region=\"footer-container-popover\"]');\n\n const basket = document.createElement('i');\n basket.setAttribute('class', 'fa fa-shopping-basket');\n\n const basketButton = document.createElement('button');\n basketButton.setAttribute('class', 'btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket');\n basketButton.setAttribute('style', 'z-index: 1001;');\n basketButton.append(basket);\n\n const dropAreaText = document.createElement('p');\n dropAreaText.setAttribute('class', 'font-weight-bold text-white');\n dropAreaText.innerText = str('drop_here');\n\n const dropArea = document.createElement('div');\n dropArea.setAttribute('class',\n 'h-100 w-100 position-absolute d-flex justify-content-center align-items-center');\n dropArea.append(dropAreaText);\n\n sections.forEach(section => {\n drag_event_listeners(section);\n });\n\n activities.forEach(activity => {\n drag_event_listeners(activity);\n });\n\n /**\n * Initialize events for dragging\n * @param {object} draggable\n */\n function drag_event_listeners(draggable) {\n draggable.addEventListener('dragstart', (e) => {\n basketButton.classList.remove('shake_basket');\n\n footerIconContainer?.prepend(basketButton);\n sharingCartBlock.children[0].classList.add('dragging_item');\n sharingCartBlock.append(dropArea);\n currentDragging = e.target;\n isDragging = true;\n });\n\n draggable.addEventListener('dragend', () => {\n if (currentDragging instanceof HTMLElement) {\n footerIconContainer?.removeChild(basketButton);\n }\n\n sharingCartBlock.children[0].classList.remove('dragging_item');\n sharingCartBlock.removeChild(dropArea);\n isDragging = false;\n });\n }\n\n [basketButton, sharingCartBlock].forEach((dropzone) => {\n dropzone.addEventListener(\"dragover\", (e) => {\n e.preventDefault();\n dropzone.classList.add('drag_over');\n });\n\n dropzone.addEventListener(\"dragenter\", (e) => {\n e.preventDefault();\n dropzone.classList.add('drag_over');\n });\n\n dropzone.addEventListener(\"dragleave\", () => {\n dropzone.classList.remove('drag_over');\n });\n\n dropzone.addEventListener(\"drop\", () => {\n if (currentDragging instanceof HTMLElement) {\n currentDragging.querySelector('.add-to-sharing-cart').click();\n }\n\n dropzone.classList.remove('drag_over');\n currentDragging = undefined;\n isDragging = false;\n });\n });\n }\n\n /**\n * Make the first section (General) draggable\n */\n function add_draggable_to_first_section() {\n const courseSectionHeader = document.getElementsByClassName(\"course-section-header\")[0] ?? null;\n\n if (courseSectionHeader instanceof HTMLElement) {\n courseSectionHeader.classList.add('draggable');\n courseSectionHeader.setAttribute('draggable', true);\n }\n }\n\n /**\n * Initialize the Sharing Cart block\n */\n $.init = function() {\n M.str.block_sharing_cart.pluginname = this.get_plugin_name();\n\n // Arrange header icons (bulkdelete, help)\n $.init_block_header();\n $.init_item_tree();\n $.init_activity_commands();\n\n if (addMethod === 'drag_and_drop') {\n init_footer_basket();\n }\n };\n var $spinner = $('').addClass('spinner fa fa-3x fa-circle-o-notch fa-spin');\n $('div#sharing-cart-spinner-modal div.spinner-container').prepend($spinner);\n\n $.init();\n });\n\n $('.copy_section').on('click', function() {\n\n const $section_selected = ($('.section-dropdown option:selected'));\n const sectionId = $section_selected.data('section-id');\n const sectionNumber = $section_selected.data('section-number');\n const courseId = $section_selected.data('course-id');\n const sectionName = $section_selected.data('section-name');\n\n $.on_section_backup(sectionId, sectionNumber, courseId, sectionName);\n });\n\n $('.copy_activity').on('click', function(e) {\n const activitySelected = ($('.activity-dropdown option:selected'));\n const activityId = activitySelected.data('activity-id');\n const activityName = activitySelected.data('activity-name');\n\n $.on_backup(e, activityName, activityId);\n });\n};\n"],"names":["addMethod","document","ready","isDragging","str","identifier","M","block_sharing_cart","moodle","get_action_url","name","args","url","cfg","wwwroot","q","k","push","encodeURIComponent","join","remove_basket","footerIconContainer","getElementById","querySelector","sharingCartBasket","removeChild","classList","remove","confirm_modal","obj","checkbox","body","create","type","ModalFactory","types","SAVE_CANCEL","title","done","modal","is_submitted","setSaveButtonText","save_button","userdata_checkbox","getRoot","find","anonymize_checkbox","on","e","currentTarget","checked","attr","prop","ModalEvents","save","response","is","next","cancel","hidden","removeClass","show","in_place_edit_section_name","$section","sectionName","$inPlaceEditable","length","data","on_backup_modal","post_data","title_str","body_str","isSection","post","sectionId","sectionNumber","courseId","userdata","anonymize","$commands","closest","text","trim","String","parent","inPlaceEditSectionName","$spinner","add_spinner","$node_spinner","add_node_spinner","sesskey","reload_tree","fail","show_error","always","hide","backup_section","sectionid","sectionnumber","courseid","cmid","course","id","backup","add","shake_basket","on_success","icon","css","iconClass","$block","match","is_frontpage","hasClass","ex","JSON","parse","responseText","core","exception","message","create_command","iconElement","addClass","append","$node","replaceWith","init_item_tree","directories","KEY","opens","param","readCookie","cookie","pop","getCookieValue","split","map","v","parseInt","expires","Date","setDate","getDate","value","expireTimeInMillisecond","d","setTime","getTime","toUTCString","setCookie","open","$dir","visible","init","i","each","index","dir","target","toggle","reset","move_targets","$cancel","targets","this","$target","item_id","$current","$next","$list","next_id","create_target","area_to","$anchor","m","move","item","$item","to","before","restore_targets","is_directory","$clipboard","section","href","URL","util","image_url","$view","html","prepend","cloneNode","replace","$sitetopic","$mainmenu","$container","format","get_section_wrapper","sectionDOM","first","init_footer_basket","currentDragging","activities","querySelectorAll","sections","sharingCartBlock","courseSectionHeader","getElementsByClassName","HTMLElement","setAttribute","add_draggable_to_first_section","basket","createElement","basketButton","dropAreaText","innerText","dropArea","drag_event_listeners","draggable","addEventListener","children","forEach","activity","dropzone","preventDefault","click","undefined","get_plugin_name","$blockheader","on_backup","activityName","cmId","$backup","$activity","dataowner","on_movedir","$current_dir","current_path","dirs","$form","submit","folder_to","val","$input","setTimeout","focus","unshift","$select","change","$edit","on_move","on_delete","liText","modalBody","isDirectory","description_text","stopPropagation","on_restore","on_section_backup","init_bulk_delete","isspeciallayout","bulkdelete","init_help_icon","helpicon","init_block_header","add_actions","actions","isCopying","action","$command","activity_actions","directory_actions","init_activity_commands","create_backup_icon","$backupIcon","add_activity_backup_control","activityClass","className","modtype","substr","indexOf","$actionMenuItem","ajaxComplete","event","xhr","settings","lastslashindex","lastIndexOf","result","substring","activity_id","isFlexibleCourseFormat","$sectionTitle","last","insertAfter","$activities","add_section_backup_control","pluginname","$section_selected","activitySelected","activityId"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"script.min.js","sources":["../src/script.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 * Sharing Cart\n *\n * @package block_sharing_cart\n * @copyright 2017 (C) VERSION2, INC.\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\n\n/**\n * @param {string} addMethod\n */\nexport const init = function(addMethod) {\n $(document).ready(function() {\n let isDragging = false;\n\n /**\n * Returns a localized string\n *\n * @param {String} identifier\n * @return {String}\n */\n function str(identifier) {\n return M.str.block_sharing_cart[identifier] || M.str.moodle[identifier];\n }\n\n /**\n * Get an action URL\n *\n * @param {String} name The action name\n * @param {Object} [args] The action parameters\n * @return {String}\n */\n function get_action_url(name, args) {\n let url = M.cfg.wwwroot + '/blocks/sharing_cart/' + name + '.php';\n if (args) {\n const q = [];\n for (let k in args) {\n q.push(k + '=' + encodeURIComponent(args[k]));\n }\n url += '?' + q.join('&');\n }\n return url;\n }\n\n /**\n * Shake the basket to indicate cancel/submit\n */\n function shake_basket() {\n if (addMethod === 'drag_and_drop') {\n const sharingCartBasket = document.querySelector('button.sharing_cart_basket');\n sharingCartBasket?.classList.add('shake_basket');\n }\n }\n\n /**\n * Remove the shake effect and basket icon\n */\n function remove_basket() {\n if (addMethod === 'drag_and_drop' && !isDragging) {\n const footer = document.getElementById('page-footer');\n const footerIconContainer = footer.querySelector('div[data-region=\"footer-container-popover\"]');\n const sharingCartBasket = document.querySelector('button.sharing_cart_basket');\n\n if (sharingCartBasket) {\n footerIconContainer?.removeChild(sharingCartBasket);\n sharingCartBasket.classList.remove('shake_basket');\n }\n }\n }\n\n /**\n * Modal called when confirming an action.\n *\n * @param obj\n */\n function confirm_modal(obj) {\n\n // Checkbox for copying userdata confirmation.\n if (obj.checkbox) {\n obj.body +=\n '
    ' +\n '' +\n '' +\n '
    ';\n }\n\n\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: obj.title,\n body: obj.body,\n }).done(function(modal) {\n let is_submitted = false;\n modal.setSaveButtonText(obj.save_button);\n\n // On save save check - if checkbox is checked.\n modal.getRoot().on(ModalEvents.save, function(e) {\n\n const response = {\n 'checkbox': $(e.target).find('.modal-checkbox').is(':checked'),\n };\n\n obj.next(response);\n is_submitted = true;\n });\n\n modal.getRoot().on(ModalEvents.cancel, function() {\n remove_basket();\n });\n\n // Remove modal from html.\n modal.getRoot().on(ModalEvents.hidden, function() {\n $('body').removeClass('modal-open');\n\n if (!is_submitted) {\n remove_basket();\n }\n });\n\n modal.show();\n });\n }\n\n /**\n * Get the section name from the section when\n * it's changed with the in place editor\n *\n * @param $section\n * @returns {*}\n */\n function in_place_edit_section_name($section) {\n let sectionName = '';\n const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable');\n if ($inPlaceEditable.length) {\n sectionName = $inPlaceEditable.data('value');\n }\n return sectionName;\n }\n\n /**\n * @param post_data\n * @param title_str\n * @param body_str\n * @param isSection\n */\n function on_backup_modal(post_data, title_str, body_str, isSection) {\n (function(on_success) {\n $.post(get_action_url('rest'), post_data,\n function(response) {\n on_success(response);\n }, \"text\")\n .fail(function(response) {\n show_error(response);\n });\n })(function(response) {\n const copyable = response === '1';\n let checkbox = false;\n\n if (copyable) {\n checkbox = true;\n }\n\n confirm_modal({\n 'title': title_str,\n 'body': body_str,\n 'save_button': str('modal_confirm_backup'),\n 'checkbox': checkbox,\n 'next': function(data) {\n if (isSection === true) {\n backup_section(post_data.sectionid, post_data.sectionnumber, post_data.courseid, data.checkbox);\n } else {\n backup(post_data.cmid, data.checkbox);\n }\n\n shake_basket();\n }\n });\n });\n }\n\n /** @var {Object} The icon configurations */\n const icon = {\n // Actions\n 'backup': {\n css: 'editing_backup',\n iconClass: 'fa fa-frown-o',\n },\n 'movedir': {\n css: 'editing_right',\n iconClass: 'fa fa-arrow-right',\n },\n 'move': {\n css: 'editing_move_',\n iconClass: 'fa fa-arrows-v',\n },\n 'edit': {\n css: 'editing_update',\n iconClass: 'fa fa-pencil',\n },\n 'cancel': {\n css: 'editing_cancel',\n iconClass: 'fa fa-ban',\n },\n 'delete': {\n css: 'editing_update',\n iconClass: 'fa fa-trash',\n },\n 'restore': {\n css: 'editing_restore',\n iconClass: 'fa fa-clone',\n },\n // Directories\n 'dir-open': {\n iconClass: 'fa fa-folder-open-o'\n },\n 'dir-closed': {\n iconClass: 'fa fa-folder-o'\n },\n };\n\n /** @var {Node} The Sharing Cart block container node */\n const $block = $('.block_sharing_cart');\n\n /** @var {Object} The current course */\n const course = new function () {\n const body = $('body');\n this.id = body.attr('class').match(/course-(\\d+)/)[1];\n this.is_frontpage = body.hasClass('pagelayout-frontpage');\n }();\n\n /**\n * Shows an error message with given Ajax error\n *\n * @param {Object} response The Ajax response\n */\n function show_error(response) {\n try {\n const ex = JSON.parse(response.responseText);\n new M.core.exception({\n name: str('pluginname') + ' - ' + str('error'),\n message: ex.message\n });\n } catch (e) {\n new M.core.exception({\n name: str('pluginname') + ' - ' + str('error'),\n message: response.responseText\n });\n }\n }\n\n /**\n * Check special layout (theme boost)\n *\n * @return {Boolean}\n */\n function verify_layout() {\n const menuelement = $block.find('.menubar .dropdown .dropdown-menu');\n return (menuelement.length);\n }\n\n /**\n * Set Cookie\n * @param name\n * @param value\n * @param expireTimeInMillisecond\n */\n function setCookie(name, value, expireTimeInMillisecond) {\n const d = new Date();\n d.setTime(d.getTime() + expireTimeInMillisecond);\n const expires = 'expires=' + d.toUTCString();\n document.cookie = name + '=' + value + ';' + expires + '';\n }\n\n /**\n * Get Cookie Value\n * @param param\n * @returns {*}\n */\n function getCookieValue(param) {\n const readCookie = document.cookie.match('(^|;)\\\\s*' + param + '\\\\s*=\\\\s*([^;]+)');\n return readCookie ? readCookie.pop() : '';\n }\n\n /**\n * Create a command icon\n *\n * @param {String} name The command name, predefined in icon\n * @param {String} [pix] The icon pix name to override\n */\n function create_command(name) {\n const iconElement = $('')\n .attr('alt', str(name))\n .attr('class', icon[name].iconClass);\n // If (verify_layout()) {\n // iconElement.addClass('iconcustom');\n // }\n\n return $('
    ')\n .addClass(icon[name].css)\n .attr('title', str(name))\n .append(iconElement);\n }\n\n /**\n * Create a spinner\n * @param $node\n * @returns {*|jQuery}\n */\n function add_spinner() {\n const $spinner = ($('
    '));\n $('section.block_sharing_cart').append($spinner);\n return $spinner;\n }\n\n /**\n *\n * @param $node\n * @returns {jQuery.fn.init}\n */\n function add_node_spinner($node) {\n const $node_spinner = $('');\n $node.append($node_spinner);\n return $node_spinner;\n }\n\n $(document).on('click', 'a.restore', function() {\n add_spinner();\n });\n\n /**\n *\n * Reload the Sharing Cart item tree\n */\n function reload_tree() {\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"render_tree\",\n \"courseid\": course.id\n },\n function(response) {\n $block.find(\".tree\").replaceWith($(response));\n $.init_item_tree();\n }, \"html\")\n .fail(function(response) {\n show_error(response);\n });\n }\n\n /**\n * Backup an activity\n *\n * @param {int} cmid\n * @param {Boolean} userdata\n */\n function backup(cmid, userdata) {\n let $commands = $('#module-' + cmid + ' .actions');\n if (!$commands.length) {\n $commands = $('[data-owner=\"#module-' + cmid + '\"]');\n }\n\n const $spinner = add_spinner();\n const $node_spinner = add_node_spinner($commands);\n\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"backup\",\n \"cmid\": cmid,\n \"userdata\": userdata,\n \"sesskey\": M.cfg.sesskey,\n \"courseid\": course.id\n },\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $node_spinner.hide();\n $spinner.hide();\n remove_basket();\n });\n }\n\n /**\n * Backup an activities in a section\n *\n * @param {int} sectionId\n * @param {int} sectionNumber\n * @param {int} courseId\n * @param {Boolean} userdata\n */\n function backup_section(sectionId, sectionNumber, courseId, userdata) {\n const $commands = $('span.inplaceeditable[data-itemtype=sectionname][data-itemid=' + sectionId + ']');\n const $section = $commands.closest(\"li.section.main\");\n let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim();\n\n if (sectionName === null) {\n sectionName = String($('#region-main .section_action_menu[data-sectionid=\\'' + sectionId + '\\']')\n .parent().parent().find('h3.sectionname').text());\n }\n\n const inPlaceEditSectionName = in_place_edit_section_name($section);\n sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName;\n\n const $spinner = add_spinner();\n const $node_spinner = add_node_spinner($commands);\n\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"backup_section\",\n \"sectionid\": sectionId,\n \"sectionnumber\": sectionNumber,\n \"courseid\": courseId,\n \"sectionname\": sectionName,\n \"userdata\": userdata,\n \"sesskey\": M.cfg.sesskey\n },\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n $node_spinner.hide();\n remove_basket();\n });\n }\n\n\n // /////// CLASSES /////////\n\n /**\n * @class Directory states manager\n */\n const directories = new function () {\n const KEY = 'block_sharing_cart-dirs';\n\n let opens = getCookieValue(KEY).split(',').map(function (v) {\n return parseInt(v);\n });\n\n function save() {\n const expires = new Date();\n expires.setDate(expires.getDate() + 30);\n setCookie(KEY, opens.join(','), expires);\n }\n\n function open($dir, visible) {\n const iconIndex = visible ? 'dir-open' : 'dir-closed';\n const iconElement = icon[iconIndex].iconClass;\n $dir.find('> div i.icon').attr('class', 'icon ' + iconElement);\n $dir.find('> ul.list')[visible ? 'show' : 'hide']();\n }\n\n function toggle(e) {\n const $dir = $(e.target).closest('li.directory');\n const i = $dir.attr('id').match(/(\\d+)$/)[1];\n const v = $dir.find('> ul.list').css('display') === 'none';\n\n open($dir, v);\n opens[i] = v ? 1 : 0;\n save();\n }\n\n /**\n * Initialize directory states\n */\n this.init = function () {\n let i = 0;\n $block.find('li.directory').each(function (index, dir) {\n const $dir = $(dir);\n $dir.attr('id', 'block_sharing_cart-dir-' + i);\n if (i >= opens.length) {\n opens.push(0);\n } else if (opens[i]) {\n open($dir, true);\n }\n $dir.find('> div div.toggle-wrapper').css('cursor', 'pointer').on('click', function (e) {\n toggle(e);\n });\n i++;\n });\n };\n\n /**\n * Reset directory states\n */\n this.reset = function () {\n opens = [];\n this.init();\n save();\n };\n }();\n\n /**\n * @class Targets for moving an item directory\n */\n const move_targets = new function () {\n let $cancel = null,\n targets = [];\n\n /**\n * Hide move targets\n */\n this.hide = function () {\n if ($cancel !== null) {\n const $commands = $cancel.closest('.commands');\n $cancel.remove();\n $cancel = null;\n $commands.closest('li.activity').css('opacity', 1.0);\n $commands.find('a').each(function () {\n $(this).show();\n });\n $.each(targets, function (index, $target) {\n $target.remove();\n });\n targets = [];\n }\n };\n\n /**\n * Show move targets for a given item\n *\n * @param {int} id The item ID\n */\n this.show = function (item_id) {\n this.hide();\n\n function move(e) {\n\n const m = $(e.target).closest('a').attr('class').match(/move-(\\d+)-to-(\\d+)/);\n const item_id = m[1],\n area_to = m[2];\n\n const $spinner = add_spinner();\n $.post(get_action_url(\"rest\"),\n {\n \"action\": \"move\",\n \"item_id\": item_id,\n \"area_to\": area_to,\n \"sesskey\": M.cfg.sesskey\n },\n function () {\n reload_tree();\n })\n .fail(function (response) {\n show_error(response);\n })\n .always(function () {\n $spinner.hide();\n });\n }\n\n const $current = $block.find('#block_sharing_cart-item-' + item_id);\n const $next = $current.next();\n const $list = $current.closest('ul');\n\n let next_id = 0;\n if ($next.length) {\n next_id = $next.attr('id').match(/item-(\\d+)$/)[1];\n }\n\n /**\n *\n * @param item_id\n * @param area_to\n * @returns {jQuery}\n */\n function create_target(item_id, area_to) {\n const $anchor = $('
    ')\n .addClass('move-' + item_id + '-to-' + area_to)\n .attr('title', str('movehere'))\n .append(\n $('

    ' + str('clicktomove') + '

    ')\n .attr('alt', str('movehere'))\n );\n\n const $target = $('
  • ')\n .append($anchor);\n $anchor.on('click', function (e) {\n move(e);\n });\n\n return $target;\n }\n\n $list.find('> li.activity').each(function (index, item) {\n const $item = $(item);\n const to = $item.attr('id').match(/item-(\\d+)$/)[1];\n if (to === item_id) {\n $cancel = create_command('cancel', 't/left');\n $cancel.on('click', function () {\n move_targets.hide();\n });\n const $commands = $item.find('.commands');\n $commands.find('a').each(function () {\n $(this).hide();\n });\n $commands.append($cancel);\n $item.css('opacity', 0.5);\n } else if (to !== next_id) {\n const $target = create_target(item_id, to);\n $item.before($target);\n targets.push($target);\n }\n }, this);\n\n if ($next) {\n var $target = create_target(item_id, 0);\n $list.append($target);\n targets.push($target);\n }\n };\n }();\n\n /**\n * @class Targets for restoring an item\n */\n const restore_targets = new function () {\n this.is_directory = null;\n let $clipboard = null,\n targets = [];\n\n /**\n *\n * @param id\n * @param section\n * @returns {jQuery}\n */\n\n function create_target(id, section) {\n const href = get_action_url('restore', {\n 'directory': (restore_targets.is_directory === true),\n 'target': id,\n 'course': course.id,\n 'section': section,\n 'in_section': $('#copy-section-form').data('in-section'),\n 'sesskey': M.cfg.sesskey,\n 'returnurl': document.URL,\n });\n\n let $target = $('').attr('href', href).attr('title', str('copyhere')).append(\n $('').attr('alt', str('copyhere')).attr('src', M.util.image_url('dropzone_arrow', 'block_sharing_cart'))\n );\n\n targets.push($target);\n\n return $target;\n }\n\n /**\n * Hide restore targets\n */\n this.hide = function () {\n if ($clipboard !== null) {\n $clipboard.remove();\n $clipboard = null;\n $.each(targets, function (index, $target) {\n $target.remove();\n });\n targets = [];\n }\n };\n\n /**\n *\n *\n * @param {int} id The item ID\n */\n this.show = function (id) {\n this.hide();\n\n let $view = $(\"\");\n\n if (this.is_directory) {\n $view.html(id).css('display', 'inline');\n $view.prepend(\n $(\"\").addClass(\"icon\")\n .attr(\"alt\", id)\n // .attr(\"src\", M.util.image_url(icon['dir-closed'].pix, null))\n );\n } else {\n const $item = $block.find('#block_sharing_cart-item-' + id);\n $view = $($item.find('div')[0].cloneNode(true)).css('display', 'inline');\n $view.attr('class', $view.attr('class').replace(/mod-indent-\\d+/, ''));\n $view.find('.commands').remove();\n }\n\n const $cancel = create_command('cancel');\n\n $cancel.on('click', this.hide);\n\n $clipboard = $('
    ');\n $clipboard.append(str('clipboard') + \": \").append($view).append($cancel);\n\n if (course.is_frontpage) {\n const $sitetopic = $('.sitetopic');\n const $mainmenu = $('.block_site_main_menu');\n if ($sitetopic) {\n $sitetopic.find('*').before($clipboard);\n } else if ($mainmenu) {\n $mainmenu.find('.content').before($clipboard);\n }\n\n // Mainmenu = section #0, sitetopic = section #1\n if ($mainmenu) {\n $mainmenu.find('.footer').before(create_target(id, 0));\n }\n if ($sitetopic) {\n $sitetopic.find('ul.section').append(create_target(id, 1));\n }\n } else {\n const $container = $('.course-content');\n $container.prepend($clipboard);\n $container.find('li.section').each(function (index, sectionDOM) {\n const $section = $(sectionDOM);\n const section = $section.attr('id').match(/(\\d+)$/)[1];\n $section.find('ul.section').first().append(create_target(id, section));\n }, this);\n }\n };\n }();\n\n // /////// INITIALIZATION /////////\n\n /**\n *\n * @returns {string|*}\n */\n $.get_plugin_name = function() {\n let $blockheader = $block.find(\"h2\");\n\n if (!$blockheader.length) {\n $blockheader = $block.find(\"h3\");\n\n if ($blockheader.length) {\n return $blockheader.html();\n }\n } else {\n return $blockheader.html();\n }\n\n return \"\";\n };\n\n /**\n *\n * @param e\n * @param activityName\n * @param {int} cmId\n */\n $.on_backup = function(e, activityName, cmId = 0) {\n if (cmId === 0) {\n cmId = (function ($backup) {\n const $activity = $backup.closest('li.activity');\n if ($activity.length) {\n return $activity.attr('id').match(/(\\d+)$/)[1];\n }\n const $commands = $backup.closest('.commands');\n const dataowner = $commands.attr('data-owner');\n if (dataowner.length) {\n return dataowner.match(/(\\d+)$/)[1];\n }\n return $commands.find('a.editing_delete').attr('href').match(/delete=(\\d+)/)[1];\n })($(e.target));\n }\n\n const data =\n {\n \"action\": \"is_userdata_copyable\",\n \"cmid\": cmId\n };\n\n on_backup_modal(data, activityName, str('confirm_backup'), false);\n };\n\n /**\n * On movedir command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_movedir = function(e) {\n const $commands = $(e.target).closest('.commands');\n\n const $current_dir = $commands.closest('li.directory');\n const current_path = $current_dir.length ? $current_dir.attr('directory-path') : '/';\n\n const item_id = $(e.target).closest('li.activity').attr('id').match(/(\\d+)$/)[1];\n\n const dirs = [];\n $block.find('li.directory').each(function() {\n dirs.push($(this).attr('directory-path'));\n });\n\n const $form = $('');\n // eslint-disable-next-line no-script-url\n $form.attr('action', 'javascript:void(0)');\n\n function submit() {\n const folder_to = $form.find('[name=\"to\"]').val();\n const $spinner = add_spinner();\n $.post(get_action_url('rest'),\n {\n \"action\": \"movedir\",\n \"item_id\": item_id,\n \"folder_to\": folder_to,\n \"sesskey\": M.cfg.sesskey\n },\n function() {\n reload_tree();\n directories.reset();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n });\n }\n\n $form.submit(submit);\n\n if (dirs.length === 0) {\n var $input = $('').val(current_path);\n setTimeout(function() {\n $input.focus();\n }, 1);\n $form.append($input);\n } else {\n dirs.unshift('/');\n\n const $select = $('').val(current_path);\n $select.remove();\n $edit.replaceWith($input);\n $input.focus();\n });\n\n $form.append($edit);\n }\n\n const $cancel = create_command('cancel');\n $cancel.on('click', function() {\n $form.remove();\n $commands.find('a').show();\n });\n $form.append($cancel);\n\n $commands.find('a').each(function() {\n $(this).hide();\n });\n $commands.append($form);\n };\n\n /**\n * On move command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_move = function(e) {\n const $item = $(e.target).closest('li.activity');\n const id = $item.attr('id').match(/(\\d+)$/)[1];\n\n move_targets.show(id);\n };\n\n /**\n * On delete command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_delete = function(e) {\n const $item = $(e.target).closest('li');\n const liText = $item[0].innerText;\n\n let isDirectory = false;\n let modalBody;\n let item;\n let description_text = '';\n\n if ($item.hasClass(\"directory\")) {\n isDirectory = true;\n item = str('folder_string');\n description_text = str('delete_folder');\n } else {\n item = str('activity_string');\n }\n\n modalBody = '

    ' + item + ' ' + liText + description_text + '

    ';\n\n confirm_modal({\n 'title': str('confirm_delete'),\n 'body': modalBody,\n 'save_button': str('modal_confirm_delete'),\n 'checkbox': false,\n 'next': function() {\n\n let data = {};\n\n if (isDirectory === true) {\n data = {\n \"action\": \"delete_directory\",\n \"path\": $item.attr(\"directory-path\"),\n \"sesskey\": M.cfg.sesskey\n };\n } else if ($item.hasClass(\"activity\")) {\n data = {\n \"action\": \"delete\",\n \"id\": $item.attr('id').match(/(\\d+)$/)[1],\n \"sesskey\": M.cfg.sesskey\n };\n }\n\n const $spinner = add_spinner();\n\n $.post(get_action_url(\"rest\"), data,\n function() {\n reload_tree();\n })\n .fail(function(response) {\n show_error(response);\n })\n .always(function() {\n $spinner.hide();\n });\n\n e.stopPropagation();\n }\n });\n };\n\n /**\n * On restore command clicked\n *\n * @param {DOMEventFacade} e\n */\n $.on_restore = function(e) {\n const $item = $(e.target).closest('li');\n let id = null;\n\n if ($item.hasClass(\"directory\")) {\n id = $item.attr(\"directory-path\");\n restore_targets.is_directory = true;\n } else if ($item.hasClass(\"activity\")) {\n id = $item.attr('id').match(/(\\d+)$/)[1];\n restore_targets.is_directory = false;\n }\n\n restore_targets.show(id);\n };\n\n /**\n * On backup the whole section as a folder\n *\n * @param {int} sectionId\n * @param {int} sectionNumber\n * @param {int} courseId\n * @param {string} sectionName\n */\n $.on_section_backup = function(sectionId, sectionNumber, courseId, sectionName) {\n const data =\n {\n \"action\": \"is_userdata_copyable_section\",\n \"sectionid\": sectionId,\n \"sectionnumber\": sectionNumber,\n \"courseid\": courseId,\n };\n\n const body_html = '

    ' + str('backup_heavy_load_warning_message') +\n '

    ' + str('confirm_backup_section');\n\n on_backup_modal(data, sectionName, body_html, true);\n };\n\n /**\n * Initialize the delete bulk\n */\n $.init_bulk_delete = function(isspeciallayout) {\n const bulkdelete = $block.find('.editing_bulkdelete');\n if (bulkdelete.length) {\n if (isspeciallayout) {\n bulkdelete.attr('role', 'menuitem').addClass('dropdown-item menu-action');\n bulkdelete.append($(\"\").append(bulkdelete.attr('title')));\n\n $block.find('.menubar .dropdown .dropdown-menu').append(bulkdelete);\n } else {\n $block.find('.header .commands').append(bulkdelete);\n }\n }\n };\n\n /**\n * Initialize the help icon\n */\n $.init_help_icon = function(isspeciallayout) {\n const helpicon = $block.find('.header-commands > .help-icon');\n\n if (isspeciallayout) {\n $block.find('.header-commands').parent().css('display', 'block');\n } else {\n $block.find('.header .commands').append(helpicon);\n }\n };\n\n /**\n * Initialize the Sharing Cart block header\n */\n $.init_block_header = function() {\n const isspeciallayout = verify_layout();\n $.init_bulk_delete(isspeciallayout);\n $.init_help_icon(isspeciallayout);\n };\n\n /**\n * Initialize the Sharing Cart item tree\n */\n $.init_item_tree = function() {\n function add_actions(item, actions) {\n const $item = $(item);\n const isCopying = $item.attr('data-is-copying') === '1';\n const $commands = $item.find('.commands').first();\n\n $.each(actions, function(index, action) {\n if (action === 'restore' && isCopying) {\n return;\n }\n const $command = create_command(action);\n $command.on('click', function(e) {\n $['on_' + action](e);\n });\n $commands.append($command);\n }, this);\n }\n\n const activity_actions = ['movedir', 'move', 'delete'];\n if (course) {\n activity_actions.push('restore');\n }\n\n const directory_actions = ['delete', 'restore'];\n\n // Initialize items\n $block.find('li.activity').each(function(index, item) {\n if($(item).attr('data-disable-copy') == 1) {\n add_actions(item, ['movedir', 'move', 'delete']);\n return;\n }\n add_actions(item, activity_actions);\n });\n\n // Initialize directory items\n $block.find('li.directory').each(function(index, item) {\n add_actions(item, directory_actions);\n });\n\n // Initialize directories\n directories.init();\n };\n\n /**\n * Extract html object from area where moodle ajax was called.\n *\n * Call add_activity_backup_control to re append sharing cart icon.\n */\n $.init_activity_commands = function() {\n $(document).ajaxComplete(function(event, xhr, settings) {\n\n const url = settings.url;\n const lastslashindex = url.lastIndexOf('=');\n const result = url.substring(lastslashindex + 1);\n\n if (result === 'core_course_edit_module' || result === 'core_course_get_module') {\n\n const data = JSON.parse(settings.data);\n const action = data[0].args.action;\n\n // Don't try to add icon if activity has been deleted.\n if (action === 'delete') {\n return;\n }\n\n setTimeout(function() {\n const activity_id = data[0].args.id;\n const activity = $('#module-' + activity_id);\n add_activity_backup_control(activity);\n\n if (action === 'duplicate') {\n const duplicated = activity.next();\n add_activity_backup_control(duplicated);\n }\n }, 1);\n }\n });\n\n /**\n * Create the backup icon\n *\n * @returns $backupIcon\n */\n function create_backup_icon() {\n\n const $backupIcon = $('
    ')\n .append($(''))\n .attr('title', str('backup'));\n\n if (addMethod !== 'click_to_add') {\n $backupIcon.addClass('d-none');\n }\n\n return $backupIcon;\n }\n\n /**\n * Add backup control with a click event to an activity\n * Added fix for copying an activity without backup routine\n *\n * @param $activity\n */\n function add_activity_backup_control($activity) {\n\n const activityClass = $activity[0].className;\n\n // Selecting modtype without prefix.\n const modtype = activityClass.substr(activityClass.indexOf('modtype_') + 8);\n\n // Default activity name.\n let activityName = str('activity_string');\n\n // Label is using a different html / css layout, so it's needed to get the name by using another $find.\n if (modtype !== 'label') {\n activityName = $('.activity#' + $activity[0].id)\n .find('.mod-indent-outer .activityinstance span.instancename')\n .html();\n }\n\n const $backupIcon = create_backup_icon();\n\n $backupIcon.on('click', function(e) {\n $.on_backup(e, activityName);\n });\n\n const $actionMenuItem = $activity.find('.action-menu.section-cm-edit-actions').parent('.actions');\n\n if (!$actionMenuItem.find('.add-to-sharing-cart').length) {\n $actionMenuItem.append($backupIcon);\n }\n }\n\n /**\n * Add backup control with a click event to a section\n *\n * @param $section\n */\n function add_section_backup_control($section) {\n\n let sectionId = $section.find('.section_action_menu').data('sectionid');\n const sectionNumber = parseInt(String($section.attr('id')).match(/\\d+/)[0]);\n let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim();\n\n const isFlexibleCourseFormat = $('body[id$=flexsections]').length;\n\n // Extract the section ID from the section if this is a Flexible\n // course format (since this format doesn't have an action menu)\n if (isFlexibleCourseFormat && (typeof sectionId === 'undefined' || sectionId === null)) {\n sectionId = $section.data('section-id');\n }\n\n // A bit unsafe to extract the course ID from the body but it's the best option we got at the moment\n const courseId = parseInt(String($('body').attr('class')).match(/course-([0-9]*)( |$)/)[1]);\n\n const $backupIcon = create_backup_icon();\n\n $backupIcon.on('click', function() {\n const inPlaceEditSectionName = in_place_edit_section_name($section);\n sectionName = (inPlaceEditSectionName !== '') ? inPlaceEditSectionName : sectionName;\n $.on_section_backup(sectionId, sectionNumber, courseId, sectionName);\n });\n\n let $sectionTitle = $section.find('h3.sectionname').first().find('a').last();\n\n const $inPlaceEditable = $section.find('h3.sectionname .inplaceeditable').first();\n if ($inPlaceEditable.length) {\n $sectionTitle = $inPlaceEditable;\n }\n\n // Add the backup icon after the cog wheel if this is a Flexible course format\n if (isFlexibleCourseFormat && sectionNumber === 0) {\n $sectionTitle = $section.find('> .controls');\n $sectionTitle.prepend($backupIcon);\n } else {\n $backupIcon.insertAfter($sectionTitle);\n }\n\n const activitySelector = 'li.activity';\n\n const $activities = $section.find(activitySelector);\n\n $($activities).each(function() {\n add_activity_backup_control($(this));\n });\n }\n\n $(\"body.editing .course-content li.section\").each(function() {\n add_section_backup_control($(this));\n });\n };\n\n /**\n * Initialize the Sharing Cart footer basket for 4.0+.\n */\n function init_footer_basket() {\n let currentDragging;\n const activities = document.querySelectorAll(\".activity.activity-wrapper\");\n const sections = document.querySelectorAll(\".course-section-header\");\n const sharingCartBlock = document.querySelector('section[data-block=\"sharing_cart\"]');\n\n add_draggable_to_first_section();\n\n const footer = document.getElementById('page-footer');\n const footerIconContainer = footer.querySelector('div[data-region=\"footer-container-popover\"]');\n\n const basket = document.createElement('i');\n basket.setAttribute('class', 'fa fa-shopping-basket');\n\n const basketButton = document.createElement('button');\n basketButton.setAttribute('class', 'btn btn-icon bg-secondary icon-no-margin btn-footer-popover sharing_cart_basket');\n basketButton.setAttribute('style', 'z-index: 1001;');\n basketButton.append(basket);\n\n const dropAreaText = document.createElement('p');\n dropAreaText.setAttribute('class', 'font-weight-bold text-white');\n dropAreaText.innerText = str('drop_here');\n\n const dropArea = document.createElement('div');\n dropArea.setAttribute('class',\n 'h-100 w-100 position-absolute d-flex justify-content-center align-items-center');\n dropArea.append(dropAreaText);\n\n sections.forEach(section => {\n drag_event_listeners(section);\n });\n\n activities.forEach(activity => {\n drag_event_listeners(activity);\n });\n\n /**\n * Initialize events for dragging\n * @param {object} draggable\n */\n function drag_event_listeners(draggable) {\n draggable.addEventListener('dragstart', (e) => {\n basketButton.classList.remove('shake_basket');\n\n footerIconContainer?.prepend(basketButton);\n sharingCartBlock.children[0].classList.add('dragging_item');\n sharingCartBlock.append(dropArea);\n currentDragging = e.target;\n isDragging = true;\n });\n\n draggable.addEventListener('dragend', () => {\n if (currentDragging instanceof HTMLElement) {\n footerIconContainer?.removeChild(basketButton);\n }\n\n sharingCartBlock.children[0].classList.remove('dragging_item');\n sharingCartBlock.removeChild(dropArea);\n isDragging = false;\n });\n }\n\n [basketButton, sharingCartBlock].forEach((dropzone) => {\n dropzone.addEventListener(\"dragover\", (e) => {\n e.preventDefault();\n dropzone.classList.add('drag_over');\n });\n\n dropzone.addEventListener(\"dragenter\", (e) => {\n e.preventDefault();\n dropzone.classList.add('drag_over');\n });\n\n dropzone.addEventListener(\"dragleave\", () => {\n dropzone.classList.remove('drag_over');\n });\n\n dropzone.addEventListener(\"drop\", () => {\n if (currentDragging instanceof HTMLElement) {\n currentDragging.querySelector('.add-to-sharing-cart').click();\n }\n\n dropzone.classList.remove('drag_over');\n currentDragging = undefined;\n isDragging = false;\n });\n });\n }\n\n /**\n * Make the first section (General) draggable\n */\n function add_draggable_to_first_section() {\n const courseSectionHeader = document.getElementsByClassName(\"course-section-header\")[0] ?? null;\n\n if (courseSectionHeader instanceof HTMLElement) {\n courseSectionHeader.classList.add('draggable');\n courseSectionHeader.setAttribute('draggable', true);\n }\n }\n\n /**\n * Initialize the Sharing Cart block\n */\n $.init = function() {\n M.str.block_sharing_cart.pluginname = this.get_plugin_name();\n\n // Arrange header icons (bulkdelete, help)\n $.init_block_header();\n $.init_item_tree();\n $.init_activity_commands();\n\n if (addMethod === 'drag_and_drop') {\n init_footer_basket();\n }\n };\n var $spinner = $('').addClass('spinner fa fa-3x fa-circle-o-notch fa-spin');\n $('div#sharing-cart-spinner-modal div.spinner-container').prepend($spinner);\n\n $.init();\n });\n\n $('.copy_section').on('click', function() {\n\n const $section_selected = ($('.section-dropdown option:selected'));\n const sectionId = $section_selected.data('section-id');\n const sectionNumber = $section_selected.data('section-number');\n const courseId = $section_selected.data('course-id');\n const sectionName = $section_selected.data('section-name');\n\n $.on_section_backup(sectionId, sectionNumber, courseId, sectionName);\n });\n\n $('.copy_activity').on('click', function(e) {\n const activitySelected = ($('.activity-dropdown option:selected'));\n const activityId = activitySelected.data('activity-id');\n const activityName = activitySelected.data('activity-name');\n\n $.on_backup(e, activityName, activityId);\n });\n};\n"],"names":["addMethod","document","ready","isDragging","str","identifier","M","block_sharing_cart","moodle","get_action_url","name","args","url","cfg","wwwroot","q","k","push","encodeURIComponent","join","remove_basket","footerIconContainer","getElementById","querySelector","sharingCartBasket","removeChild","classList","remove","confirm_modal","obj","checkbox","body","create","type","ModalFactory","types","SAVE_CANCEL","title","done","modal","is_submitted","setSaveButtonText","save_button","getRoot","on","ModalEvents","save","e","response","target","find","is","next","cancel","hidden","removeClass","show","in_place_edit_section_name","$section","sectionName","$inPlaceEditable","length","data","on_backup_modal","post_data","title_str","body_str","isSection","post","sectionId","sectionNumber","courseId","userdata","$commands","closest","attr","text","trim","String","parent","inPlaceEditSectionName","$spinner","add_spinner","$node_spinner","add_node_spinner","sesskey","reload_tree","fail","show_error","always","hide","backup_section","sectionid","sectionnumber","courseid","cmid","course","id","backup","add","shake_basket","on_success","icon","css","iconClass","$block","match","is_frontpage","hasClass","ex","JSON","parse","responseText","core","exception","message","create_command","iconElement","addClass","append","$node","replaceWith","init_item_tree","directories","KEY","opens","param","readCookie","cookie","pop","getCookieValue","split","map","v","parseInt","expires","Date","setDate","getDate","value","expireTimeInMillisecond","d","setTime","getTime","toUTCString","setCookie","open","$dir","visible","init","i","each","index","dir","toggle","reset","move_targets","$cancel","targets","this","$target","item_id","$current","$next","$list","next_id","create_target","area_to","$anchor","m","move","item","$item","to","before","restore_targets","is_directory","$clipboard","section","href","URL","util","image_url","$view","html","prepend","cloneNode","replace","$sitetopic","$mainmenu","$container","sectionDOM","first","init_footer_basket","currentDragging","activities","querySelectorAll","sections","sharingCartBlock","courseSectionHeader","getElementsByClassName","HTMLElement","setAttribute","add_draggable_to_first_section","basket","createElement","basketButton","dropAreaText","innerText","dropArea","drag_event_listeners","draggable","addEventListener","children","forEach","activity","dropzone","preventDefault","click","undefined","get_plugin_name","$blockheader","on_backup","activityName","cmId","$backup","$activity","dataowner","on_movedir","$current_dir","current_path","dirs","$form","submit","folder_to","val","$input","setTimeout","focus","unshift","$select","change","$edit","on_move","on_delete","liText","modalBody","isDirectory","description_text","stopPropagation","on_restore","on_section_backup","init_bulk_delete","isspeciallayout","bulkdelete","init_help_icon","helpicon","init_block_header","add_actions","actions","isCopying","action","$command","activity_actions","directory_actions","init_activity_commands","create_backup_icon","$backupIcon","add_activity_backup_control","activityClass","className","modtype","substr","indexOf","$actionMenuItem","ajaxComplete","event","xhr","settings","lastslashindex","lastIndexOf","result","substring","activity_id","isFlexibleCourseFormat","$sectionTitle","last","insertAfter","$activities","add_section_backup_control","pluginname","$section_selected","activitySelected","activityId"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/script.js b/amd/src/script.js index b882b91..def7df9 100644 --- a/amd/src/script.js +++ b/amd/src/script.js @@ -98,14 +98,8 @@ export const init = function(addMethod) { if (obj.checkbox) { obj.body += ''; } @@ -118,22 +112,11 @@ export const init = function(addMethod) { let is_submitted = false; modal.setSaveButtonText(obj.save_button); - const userdata_checkbox = $(modal.getRoot()).find('#modal-userdata-checkbox'); - const anonymize_checkbox = $(modal.getRoot()).find('#modal-anonymize-checkbox'); - - userdata_checkbox.on('change', (e) => { - if (e.currentTarget.checked) { - anonymize_checkbox.attr('disabled', null); - } else { - anonymize_checkbox.prop('checked', false).attr('disabled', true); - } - }); - // On save save check - if checkbox is checked. modal.getRoot().on(ModalEvents.save, function(e) { + const response = { - 'userdata': userdata_checkbox.is(':checked'), - 'anonymize': anonymize_checkbox.is(':checked'), + 'checkbox': $(e.target).find('.modal-checkbox').is(':checked'), }; obj.next(response); @@ -203,9 +186,9 @@ export const init = function(addMethod) { 'checkbox': checkbox, 'next': function(data) { if (isSection === true) { - backup_section(post_data.sectionid, post_data.sectionnumber, post_data.courseid, data.userdata, data.anonymize); + backup_section(post_data.sectionid, post_data.sectionnumber, post_data.courseid, data.checkbox); } else { - backup(post_data.cmid, data.userdata, data.anonymize); + backup(post_data.cmid, data.checkbox); } shake_basket(); @@ -387,9 +370,8 @@ export const init = function(addMethod) { * * @param {int} cmid * @param {Boolean} userdata - * @param {Boolean} anonymize */ - function backup(cmid, userdata, anonymize) { + function backup(cmid, userdata) { let $commands = $('#module-' + cmid + ' .actions'); if (!$commands.length) { $commands = $('[data-owner="#module-' + cmid + '"]'); @@ -403,7 +385,6 @@ export const init = function(addMethod) { "action": "backup", "cmid": cmid, "userdata": userdata, - "anonymize": anonymize, "sesskey": M.cfg.sesskey, "courseid": course.id }, @@ -427,9 +408,8 @@ export const init = function(addMethod) { * @param {int} sectionNumber * @param {int} courseId * @param {Boolean} userdata - * @param {Boolean} anonymize */ - function backup_section(sectionId, sectionNumber, courseId, userdata, anonymize) { + function backup_section(sectionId, sectionNumber, courseId, userdata) { const $commands = $('span.inplaceeditable[data-itemtype=sectionname][data-itemid=' + sectionId + ']'); const $section = $commands.closest("li.section.main"); let sectionName = $section.attr('aria-label') || $section.find('.sectionname').text().trim(); @@ -453,7 +433,6 @@ export const init = function(addMethod) { "courseid": courseId, "sectionname": sectionName, "userdata": userdata, - "anonymize": anonymize, "sesskey": M.cfg.sesskey }, function() { @@ -755,7 +734,7 @@ export const init = function(addMethod) { } else { const $container = $('.course-content'); $container.prepend($clipboard); - $container.find(M.course.format.get_section_wrapper(null)).each(function (index, sectionDOM) { + $container.find('li.section').each(function (index, sectionDOM) { const $section = $(sectionDOM); const section = $section.attr('id').match(/(\d+)$/)[1]; $section.find('ul.section').first().append(create_target(id, section)); From c231f5f55e01e55934fc937f9023714af67fc5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sam=20M=C3=B8ller?= Date: Thu, 25 Jul 2024 15:21:33 +0200 Subject: [PATCH 43/43] #154: Fix non-exists adhoc task Fix unable to (Run now) on non-exists adhoc task Fix error when sharing cart try to poll non-exists item from plugin --- README.md | 3 +++ classes/app/repository.php | 10 ++++++---- classes/external/task/run_now.php | 11 ++++++----- version.php | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0dceabf..8b7f71c 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ GPL v3 Change Log ---------- +* 5.0, release 2 2024.07.25 + * Fixed issue with the sharing cart unable to handle non-exists task when pressing "Run now" button. + * Fixed error when sharing cart try to request a non-exists item from plugin. * 5.0, release 1 2024.03.21 * Total refactor of the whole plugin: * Improvements diff --git a/classes/app/repository.php b/classes/app/repository.php index 302ddbe..0f76131 100644 --- a/classes/app/repository.php +++ b/classes/app/repository.php @@ -33,9 +33,11 @@ public function get_all(): collection public function get_by_id(int $id): false|entity { - return $this->map_record_to_entity( - $this->db->get_record($this->get_table(), ['id' => $id]) - ); + $record = $this->db->get_record($this->get_table(), ['id' => $id]); + if (!$record) { + return false; + } + return $this->map_record_to_entity($record); } public function insert(entity $entity): int @@ -61,4 +63,4 @@ public function map_records_to_collection_of_entities(array|collection $records) array_map(fn($record) => $this->map_record_to_entity($record), $records) ); } -} \ No newline at end of file +} diff --git a/classes/external/task/run_now.php b/classes/external/task/run_now.php index 756c2fd..aac7c4c 100644 --- a/classes/external/task/run_now.php +++ b/classes/external/task/run_now.php @@ -10,8 +10,6 @@ use core_external\external_api; use core_external\external_description; use core_external\external_function_parameters; -use core_external\external_single_structure; -use core_external\external_multiple_structure; use core_external\external_value; class run_now extends external_api @@ -44,10 +42,13 @@ public static function execute( 'faildelay' => 0, 'timestarted' => null, 'userid' => $USER->id - ], - strictness: MUST_EXIST + ] ); + if (!$task) { + return false; + } + ob_start(); \core\task\manager::run_adhoc_from_cli($task->id); ob_end_clean(); @@ -59,4 +60,4 @@ public static function execute_returns(): external_description { return new external_value(PARAM_BOOL, '', VALUE_REQUIRED); } -} \ No newline at end of file +} diff --git a/version.php b/version.php index 69788a8..82c5198 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ /** @var object $plugin */ $plugin->component = 'block_sharing_cart'; -$plugin->version = 2024032115; +$plugin->version = 2024072500; $plugin->requires = 2023042400; // Moodle 4.2.0 -$plugin->release = '5.0, release 1'; +$plugin->release = '5.0, release 2'; $plugin->maturity = MATURITY_STABLE;