From 27623339a50b22aa6a6cf1471696dc6aab796f6c Mon Sep 17 00:00:00 2001 From: erseco Date: Sat, 9 May 2026 16:57:21 +0100 Subject: [PATCH] fix: stop upgradesettings loop on styles upload widget (#47, #51) `admin_setting_stylesupload` extends `admin_setting_configstoredfile`, whose `get_setting()` reads the stored file id from plugin config. Our `write_setting()` consumes the dropped ZIP and removes it, never persisting anything in `config_plugins`, so `get_setting()` kept returning `null`. Moodle's `admin_output_new_settings_by_page()` flags any setting whose `get_setting()` is null as "new", redirecting every admin login to `admin/upgradesettings.php` and re-rendering the widget no matter how many times "Save changes" was clicked. Override `get_setting()` to return an empty string so the setting is never reported as new. The widget always builds a fresh draft area, so no stored value is needed for rendering, and `write_setting()` keeps behaving exactly as before. --- classes/admin/admin_setting_stylesupload.php | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/classes/admin/admin_setting_stylesupload.php b/classes/admin/admin_setting_stylesupload.php index 559bc35..b55302c 100644 --- a/classes/admin/admin_setting_stylesupload.php +++ b/classes/admin/admin_setting_stylesupload.php @@ -56,6 +56,28 @@ class admin_setting_stylesupload extends \admin_setting_configstoredfile { /** @var string Filearea used to receive drops before extraction. */ public const FILEAREA = 'styles_drops'; + /** + * Report a non-null stored value so this setting is never flagged as + * "new" on `admin/upgradesettings.php`. + * + * `admin_setting_configstoredfile` reads the file id from plugin config, + * but our `write_setting()` deletes the staged ZIP after extraction and + * never persists anything in `config_plugins`. Without this override, + * `get_setting()` keeps returning `null`, so Moodle's + * `admin_output_new_settings_by_page()` re-renders the upload widget on + * every plugin upgrade, trapping the admin in an upgrade-settings loop + * (issues #47 / #51). + * + * The widget itself does not need a stored value to render — it always + * starts from a fresh draft area — so returning an empty string is + * functionally equivalent to "nothing pending". + * + * @return string + */ + public function get_setting() { + return ''; + } + /** * Stage any uploaded drafts into the plugin filearea and extract them. *