diff --git a/public/mod/subsection/cleandescriptions.php b/public/mod/subsection/cleandescriptions.php
new file mode 100644
index 00000000000..f5170e0323a
--- /dev/null
+++ b/public/mod/subsection/cleandescriptions.php
@@ -0,0 +1,52 @@
+.
+
+/**
+ * Remove or migrate subsection descriptions
+ *
+ * @copyright 2026 Sara Arjona
+ * @package mod_subsection
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../../config.php');
+
+require_admin();
+
+$action = required_param('action', PARAM_ALPHA);
+$count = optional_param('count', 0, PARAM_INT);
+$return = new moodle_url('/admin/settings.php', ['section' => 'mod_subsection_settings']);
+
+$PAGE->set_url('/mod/subsection/cleandescriptions.php');
+$PAGE->set_context(context_system::instance());
+
+require_sesskey();
+if ($action === 'delete') {
+ // Remove all existing subsection descriptions.
+ $DB->set_field('course_sections', 'summary', '', ['component' => 'mod_subsection']);
+ redirect(
+ $return,
+ get_string('descriptionsdeletedsuccess', 'mod_subsection', $count),
+ null,
+ \core\output\notification::NOTIFY_SUCCESS
+ );
+} else if ($action === 'migrate') {
+ // Schedule the ad-hoc task to migrate subsection descriptions.
+ \core\task\manager::queue_adhoc_task(new \mod_subsection\task\migrate_subsection_descriptions_task(), true);
+ redirect($return);
+} else {
+ throw new moodle_exception('invalidaction', 'mod_subsection');
+}
diff --git a/public/mod/subsection/lang/en/subsection.php b/public/mod/subsection/lang/en/subsection.php
index 3893fbe662a..b82aff3bd7a 100644
--- a/public/mod/subsection/lang/en/subsection.php
+++ b/public/mod/subsection/lang/en/subsection.php
@@ -23,6 +23,28 @@
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+$string['cleandescriptionsdetail'] = 'Subsection pages and descriptions are no longer supported in Moodle 5.2.
+This site has {$a->count} subsection descriptions that are no longer visible to users.
+You can choose to:
+
+ - Migrate these descriptions to Text and Media areas.
The Text and media areas will be displayed at the top of each subsection.
{$a->migratelink}
+
+ - Delete these descriptions permanently.
This will completely erase descriptions from the database.
{$a->deletelink}
+
+
+
';
+$string['deleteconfirmbutton'] = 'Delete all descriptions';
+$string['deleteconfirmtext'] = 'This will permanently delete {$a} subsection descriptions from the database.
You can\'t undo this. Are you sure you want to delete all descriptions?';
+$string['deleteconfirmtitle'] = 'Delete all subsection descriptions?';
+$string['deletelinktext'] = 'Delete descriptions';
+$string['descriptionsdeletedsuccess'] = '{$a} subsection descriptions deleted.';
+$string['descriptionsmigratedpending'] = 'Subsection descriptions waiting to be migrated: {$a}';
+$string['descriptionsmigratedsuccess'] = 'The migration task for all subsection descriptions has been created. This task will run in the background and may take a few minutes.';
+$string['invalidaction'] = 'Invalid action specified.';
+$string['migrateconfirmbutton'] = 'Migrate all descriptions';
+$string['migrateconfirmtext'] = 'This will migrate {$a} subsection descriptions to Text and Media areas.
You can\'t undo this. Are you sure you want to migrate all descriptions?';
+$string['migrateconfirmtitle'] = 'Migrate all subsection descriptions?';
+$string['migratelinktext'] = 'Migrate descriptions';
$string['modulename'] = 'Subsection';
$string['modulenameplural'] = 'Subsections';
$string['pluginadministration'] = 'Subsection administration';
diff --git a/public/mod/subsection/settings.php b/public/mod/subsection/settings.php
index 9025c8122d6..41ef83e9d16 100644
--- a/public/mod/subsection/settings.php
+++ b/public/mod/subsection/settings.php
@@ -28,7 +28,78 @@ defined('MOODLE_INTERNAL') || die();
if ($hassiteconfig) {
$settings = new admin_settingpage('mod_subsection_settings', new lang_string('pluginname', 'mod_subsection'));
- // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf
if ($ADMIN->fulltree) {
+ // Add description cleanup and migration links.
+ $count = $DB->count_records_select(
+ table: 'course_sections',
+ select: 'component = :component AND summary != :empty',
+ params: ['component' => 'mod_subsection', 'empty' => ''],
+ );
+ $task = \core\task\manager::get_queued_adhoc_task_record(new \mod_subsection\task\migrate_subsection_descriptions_task());
+ if ($task) {
+ // There is a pending migration task, show notification and pending count.
+ $notification = $OUTPUT->notification(
+ get_string('descriptionsmigratedsuccess', 'mod_subsection'),
+ \core\output\notification::NOTIFY_SUCCESS,
+ );
+ $settings->add(new admin_setting_heading(
+ 'migratedescriptionsnotification',
+ '',
+ $notification,
+ ));
+ $settings->add(new admin_setting_heading(
+ 'pendingcleandescriptions',
+ '',
+ new lang_string('descriptionsmigratedpending', 'mod_subsection', $count),
+ ));
+ } else if ($count > 0) {
+ // Show migration and deletion links.
+ $migrateaction = new \confirm_action(
+ message: get_string('migrateconfirmtext', 'mod_subsection', $count),
+ continuelabel: get_string('migrateconfirmbutton', 'mod_subsection'),
+ title: get_string('migrateconfirmtitle', 'mod_subsection'),
+ );
+ $migrateurl = new moodle_url(
+ '/mod/subsection/cleandescriptions.php',
+ ['action' => 'migrate', 'count' => $count, 'sesskey' => sesskey()],
+ );
+ $migratelink = $OUTPUT->action_link(
+ url: $migrateurl,
+ text: get_string('migratelinktext', 'mod_subsection'),
+ action: $migrateaction,
+ attributes: ['class' => 'btn btn-secondary'],
+ );
+
+ $deleteaction = new \confirm_action(
+ message: get_string('deleteconfirmtext', 'mod_subsection', $count),
+ continuelabel: get_string('deleteconfirmbutton', 'mod_subsection'),
+ title: get_string('deleteconfirmtitle', 'mod_subsection'),
+ dialogtype: 'delete',
+ );
+ $deleteurl = new moodle_url(
+ '/mod/subsection/cleandescriptions.php',
+ ['action' => 'delete', 'count' => $count, 'sesskey' => sesskey()],
+ );
+ $deletelink = $OUTPUT->action_link(
+ url: $deleteurl,
+ text: get_string('deletelinktext', 'mod_subsection'),
+ action: $deleteaction,
+ attributes: ['class' => 'btn btn-secondary'],
+ );
+
+ $settings->add(new admin_setting_heading(
+ 'cleandescriptions',
+ '',
+ new lang_string(
+ 'cleandescriptionsdetail',
+ 'mod_subsection',
+ [
+ 'count' => $count,
+ 'migratelink' => $migratelink,
+ 'deletelink' => $deletelink,
+ ],
+ ),
+ ));
+ }
}
}
diff --git a/public/mod/subsection/tests/behat/subsection_clean_descriptions.feature b/public/mod/subsection/tests/behat/subsection_clean_descriptions.feature
new file mode 100644
index 00000000000..7aa8f8ad2c8
--- /dev/null
+++ b/public/mod/subsection/tests/behat/subsection_clean_descriptions.feature
@@ -0,0 +1,46 @@
+@mod @mod_subsection @_file_upload
+Feature: Subsection clean descriptions
+ In order to manage subsection descriptions
+ As an administrator
+ I want to be able to delete or migrate subsection descriptions
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following config values are set as admin:
+ | enableasyncbackup | 0 |
+ And I am on the "Course 1" "restore" page logged in as "admin"
+ And I press "Manage course backups"
+ And I upload "mod/subsection/tests/fixtures/subsections_with_descriptions.mbz" file to "Files" filemanager
+ And I press "Save changes"
+ And I restore "subsections_with_descriptions.mbz" backup into a new course using this options:
+ | Schema | Course name | Course 2 |
+ | Schema | Course short name | C2 |
+
+ @javascript
+ Scenario: Migrate subsection descriptions
+ Given I navigate to "Plugins > Activity modules > Subsection" in site administration
+ And I should see "This site has 2 subsection descriptions that are no longer visible to users."
+ When I click on "Migrate descriptions" "link" in the "region-main" "region"
+ And I should see "This will migrate 2 subsection descriptions to Text and Media areas." in the "Migrate all subsection descriptions?" "dialogue"
+ And I click on "Migrate all descriptions" "button" in the "Migrate all subsection descriptions?" "dialogue"
+ Then I should see "The migration task for all subsection descriptions has been created." in the "region-main" "region"
+ And I should see "Subsection descriptions waiting to be migrated: 2" in the "region-main" "region"
+ And I reload the page
+ And I should see "The migration task for all subsection descriptions has been created." in the "region-main" "region"
+ And I should see "Subsection descriptions waiting to be migrated: 2" in the "region-main" "region"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should not see "Subsection descriptions waiting to be migrated:" in the "region-main" "region"
+
+ @javascript
+ Scenario: Delete subsection descriptions
+ Given I navigate to "Plugins > Activity modules > Subsection" in site administration
+ And I should see "This site has 2 subsection descriptions that are no longer visible to users."
+ When I click on "Delete descriptions" "link" in the "region-main" "region"
+ And I should see "This will permanently delete 2 subsection descriptions from the database." in the "Delete all subsection descriptions?" "dialogue"
+ And I click on "Delete all descriptions" "button" in the "Delete all subsection descriptions?" "dialogue"
+ Then I should see "2 subsection descriptions deleted." in the "region-main" "region"
+ And I reload the page
+ And I should not see "Subsection pages and descriptions are no longer supported in Moodle 5.2"
diff --git a/public/mod/subsection/tests/fixtures/subsections_with_descriptions.mbz b/public/mod/subsection/tests/fixtures/subsections_with_descriptions.mbz
new file mode 100644
index 00000000000..2550090994f
Binary files /dev/null and b/public/mod/subsection/tests/fixtures/subsections_with_descriptions.mbz differ