@@ -240,6 +240,8 @@ function get_course_data(
240240 ),
241241 $ path .'course_copy/create_backup.php? ' .api_get_cidreq_params ($ courseId )
242242 );
243+
244+ // Single course delete: ask if exclusive documents should also be removed.
243245 $ actions [] = Display::url (
244246 Display::getMdiIcon (
245247 ActionIcon::DELETE ,
@@ -251,12 +253,12 @@ function get_course_data(
251253 $ path .'admin/course_list.php? '
252254 .http_build_query ([
253255 'delete_course ' => $ course ['col0 ' ],
256+ // Default: keep documents; JS will toggle this param to 1 if admin agrees.
257+ 'delete_docs ' => 0 ,
254258 'sec_token ' => Security::getTokenFromSession (),
255259 ]),
256260 [
257- 'onclick ' => "javascript: if (!confirm(' "
258- .addslashes (api_htmlentities (get_lang ('Please confirm your choice ' ), \ENT_QUOTES ))
259- ."')) return false; " ,
261+ 'onclick ' => 'return confirmDeleteCourseWithDocs(this); ' ,
260262 ]
261263 );
262264
@@ -361,9 +363,11 @@ function get_course_visibility_icon(int $visibility): string
361363 if ('delete_courses ' == $ _POST ['action ' ]) {
362364 if (!empty ($ _POST ['course ' ])) {
363365 $ course_codes = $ _POST ['course ' ];
366+ $ deleteDocs = isset ($ _POST ['delete_docs ' ]) && (int ) $ _POST ['delete_docs ' ] === 1 ;
367+
364368 if (count ($ course_codes ) > 0 ) {
365369 foreach ($ course_codes as $ course_code ) {
366- CourseManager::delete_course ($ course_code );
370+ CourseManager::delete_course ($ course_code, $ deleteDocs );
367371 }
368372 }
369373
@@ -459,8 +463,11 @@ function get_course_visibility_icon(int $visibility): string
459463 $ content .= $ form ->returnForm ();
460464} else {
461465 $ tool_name = get_lang ('Course list ' );
466+
467+ // Single course deletion (from action icon)
462468 if (isset ($ _GET ['delete_course ' ]) && Security::check_token ('get ' )) {
463- $ result = CourseManager::delete_course ($ _GET ['delete_course ' ]);
469+ $ deleteDocs = isset ($ _GET ['delete_docs ' ]) && (int ) $ _GET ['delete_docs ' ] === 1 ;
470+ $ result = CourseManager::delete_course ($ _GET ['delete_course ' ], $ deleteDocs );
464471 if ($ result ) {
465472 Display::addFlash (Display::return_message (get_lang ('Deleted ' )));
466473 }
@@ -573,6 +580,7 @@ function get_course_visibility_icon(int $visibility): string
573580 </script> ' ;
574581
575582 $ actions = Display::toolbarAction ('toolbar ' , [$ actions1 , $ actions3 .$ actions4 .$ actions2 ]);
583+
576584 // Create a sortable table with the course data
577585 $ table = new SortableTable (
578586 'courses ' ,
@@ -584,10 +592,12 @@ function get_course_visibility_icon(int $visibility): string
584592 'course-list '
585593 );
586594
587- $ parameters = [];
588- $ parameters ['sec_token ' ] = Security::get_token ();
595+ $ parameters = [
596+ 'sec_token ' => Security::get_token (),
597+ ];
598+
589599 if (isset ($ _GET ['keyword ' ])) {
590- $ parameters = ['keyword ' => Security::remove_XSS ($ _GET ['keyword ' ])] ;
600+ $ parameters ['keyword ' ] = Security::remove_XSS ($ _GET ['keyword ' ]);
591601 } elseif (isset ($ _GET ['keyword_code ' ])) {
592602 $ parameters ['keyword_code ' ] = Security::remove_XSS ($ _GET ['keyword_code ' ]);
593603 $ parameters ['keyword_title ' ] = Security::remove_XSS ($ _GET ['keyword_title ' ]);
@@ -624,6 +634,114 @@ function get_course_visibility_icon(int $visibility): string
624634 $ tab = CourseManager::getCourseListTabs ('simple ' );
625635
626636 $ content .= $ tab .$ table ->return_table ();
637+
638+ // JS helper to ask for exclusive document deletion both for single and bulk delete.
639+ $ deleteDocsMessage = addslashes (
640+ get_lang (
641+ 'When deleting a course or multiple selected courses, any documents that are only used in those course(s) (if any) will normally be kept as orphan files and will remain visible in the "File information" tool (platform admin only). Click "OK" if you also want to permanently delete those orphan files from disk; click "Cancel" to keep them as orphan files. '
642+ )
643+ );
644+
645+ // Fallback confirmation text; SortableTable uses data-confirm on the link.
646+ $ baseConfirmMessage = addslashes (get_lang ('Please confirm your choice ' ));
647+
648+ $ content .= '<script>
649+ (function () {
650+ var docsMsg = " ' .$ deleteDocsMessage .'";
651+ var defaultConfirmMsg = " ' .$ baseConfirmMessage .'";
652+
653+ // Single-course delete (trash icon per row)
654+ window.confirmDeleteCourseWithDocs = function (link) {
655+ var baseMsg = link.getAttribute("data-confirm") || defaultConfirmMsg;
656+
657+ // Confirm course deletion.
658+ if (!window.confirm(baseMsg)) {
659+ return false;
660+ }
661+
662+ // Ask about orphan documents on disk.
663+ if (window.confirm(docsMsg)) {
664+ if (link.href.indexOf("delete_docs=0") !== -1) {
665+ link.href = link.href.replace("delete_docs=0", "delete_docs=1");
666+ } else if (link.href.indexOf("delete_docs=") === -1) {
667+ var sep = link.href.indexOf("?") === -1 ? "?" : "&";
668+ link.href = link.href + sep + "delete_docs=1";
669+ }
670+ }
671+
672+ return true;
673+ };
674+
675+ // Bulk delete (SortableTable dropdown)
676+ function wrapActionClick() {
677+ // Ensure we only wrap once and only if action_click exists.
678+ if (!window.action_click || window.action_click.__wrappedForCourseList) {
679+ return;
680+ }
681+
682+ var originalActionClick = window.action_click;
683+ var docsMsgLocal = docsMsg;
684+
685+ window.action_click = function (el, formId) {
686+ var action = el.getAttribute("data-action");
687+ var confirmMsg = el.getAttribute("data-confirm") || defaultConfirmMsg;
688+
689+ // Intercept only the bulk delete of this page.
690+ if (formId === "form_courses_id" && action === "delete_courses") {
691+ var form = document.getElementById(formId);
692+ if (!form) {
693+ return false;
694+ }
695+
696+ // 1) Confirm deletion of selected courses.
697+ if (confirmMsg && !window.confirm(confirmMsg)) {
698+ return false;
699+ }
700+
701+ // 2) Ask if orphan documents should also be deleted from disk.
702+ var deleteDocs = window.confirm(docsMsgLocal);
703+
704+ // Ensure "action" hidden field exists and is set.
705+ var actionInput = form.querySelector( \'input[name="action"] \');
706+ if (!actionInput) {
707+ actionInput = document.createElement("input");
708+ actionInput.type = "hidden";
709+ actionInput.name = "action";
710+ form.appendChild(actionInput);
711+ }
712+ actionInput.value = action;
713+
714+ // If user accepted the docs deletion, set the delete_docs flag.
715+ if (deleteDocs) {
716+ var deleteDocsInput = form.querySelector( \'input[name="delete_docs"] \');
717+ if (!deleteDocsInput) {
718+ deleteDocsInput = document.createElement("input");
719+ deleteDocsInput.type = "hidden";
720+ deleteDocsInput.name = "delete_docs";
721+ form.appendChild(deleteDocsInput);
722+ }
723+ deleteDocsInput.value = "1";
724+ }
725+
726+ form.submit();
727+ return false;
728+ }
729+
730+ // Fallback: keep original behavior for any other action/form.
731+ return originalActionClick(el, formId);
732+ };
733+
734+ window.action_click.__wrappedForCourseList = true;
735+ }
736+
737+ if (document.readyState === "loading") {
738+ document.addEventListener("DOMContentLoaded", wrapActionClick);
739+ } else {
740+ wrapActionClick();
741+ }
742+ })();
743+ </script> ' ;
744+
627745}
628746
629747$ tpl = new Template ($ tool_name );
0 commit comments