diff --git a/backup/upgrade.txt b/backup/upgrade.txt
index a07e0b4e2b5..d07cc39886b 100644
--- a/backup/upgrade.txt
+++ b/backup/upgrade.txt
@@ -1,6 +1,10 @@
This files describes API changes in /backup/*,
information provided here is intended especially for developers.
+=== 3.10.10 ===
+
+* Backup UI labels now accept empty/whitespace-only contents.
+
=== 3.10 ===
* Local plugins can now hook into a backup and restore process of grade items by
using define_grade_item_plugin_structure method (See MDL-69418).
diff --git a/backup/util/ui/backup_ui_setting.class.php b/backup/util/ui/backup_ui_setting.class.php
index da74cfec88e..d5f8287f01e 100644
--- a/backup/util/ui/backup_ui_setting.class.php
+++ b/backup/util/ui/backup_ui_setting.class.php
@@ -146,7 +146,12 @@ class base_setting_ui {
* @throws base_setting_ui_exception when the label is not valid.
* @param string $label
*/
- public function set_label(string $label) :void {
+ public function set_label(string $label): void {
+ // Let's avoid empty/whitespace-only labels, so the html clean (that makes trim()) doesn't fail.
+ if (trim($label) === '') {
+ $label = ' '; // Will be converted to non-breaking utf-8 char 0xc2a0 by PARAM_CLEANHTML.
+ }
+
$label = clean_param($label, PARAM_CLEANHTML);
if ($label === '') {
diff --git a/backup/util/ui/tests/base_setting_ui_test.php b/backup/util/ui/tests/base_setting_ui_test.php
index 6aa3da6c09b..61960023ca1 100644
--- a/backup/util/ui/tests/base_setting_ui_test.php
+++ b/backup/util/ui/tests/base_setting_ui_test.php
@@ -58,13 +58,21 @@ class base_setting_ui_test extends advanced_testcase {
$bsui->set_label(123);
$this->assertSame('123', $bsui->get_label());
- // Should raise an exception when label is empty.
- try {
- $bsui->set_label('');
- $this->assertTrue(false, 'base_setting_ui_exception');
- } catch (Exception $exception) {
- $this->assertTrue($exception instanceof base_setting_ui_exception);
- $this->assertEquals($exception->errorcode, 'setting_invalid_ui_label');
- }
+ // Should be converted to non-breaking space (U+00A0) when label is empty.
+ $bsui->set_label('');
+ $this->assertSame("\u{00A0}", $bsui->get_label());
+
+ // Should be converted to non-breaking space (U+00A0) when the trimmed label is empty.
+ $bsui->set_label(" \t\t\n\n\t\t ");
+ $this->assertSame("\u{00A0}", $bsui->get_label());
+
+ // Should clean partially the wrong bits.
+ $bsui->set_label('label');
+ $this->assertSame('label', $bsui->get_label());
+
+ // Should raise an exception when cleaning ends with 100% empty.
+ $this->expectException(base_setting_ui_exception::class);
+ $this->expectExceptionMessage('error/setting_invalid_ui_label');
+ $bsui->set_label('');
}
}