From 9de91fdb2535c0e63854dbde57a49213caa96efd Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 10 Feb 2025 14:42:04 +0000 Subject: [PATCH] s/global $user_config/$user->get_config()/ Global variables are a pain, doubly so when you have two global variables which need to be kept in sync or things will go very very badly (`$user` and `$user_config`). Having a global `$user` feels like an acceptable tradeoff, but `$user_config` is redundant. --- core/user.php | 11 +++++++++ ext/avatar_post/main.php | 10 ++++---- ext/avatar_post/theme.php | 2 +- ext/biography/main.php | 7 +++--- ext/cron_uploader/main.php | 46 ++++++++++++++++++------------------- ext/cron_uploader/theme.php | 4 ++-- ext/filter/theme.php | 4 ++-- ext/private_image/main.php | 14 +++++------ ext/rating/main.php | 4 ++-- ext/rating/test.php | 16 ++++++------- ext/ratings_blur/main.php | 4 ++-- ext/ratings_blur/test.php | 12 +++++----- ext/user/main.php | 4 +--- ext/user_config/main.php | 28 ++++------------------ 14 files changed, 77 insertions(+), 89 deletions(-) diff --git a/core/user.php b/core/user.php index 60b058ac9..ec6a1cc3c 100644 --- a/core/user.php +++ b/core/user.php @@ -27,6 +27,7 @@ class User public ?string $passhash; #[Field] public UserClass $class; + private ?Config $config = null; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Initialisation * @@ -58,6 +59,16 @@ public function __construct(array $row) } } + public function get_config(): Config + { + global $database; + if (is_null($this->config)) { + $this->config = new DatabaseConfig($database, "user_config", "user_id", "{$this->id}"); + send_event(new InitUserConfigEvent($this, $this->config)); + } + return $this->config; + } + #[Query] public static function me(): User { diff --git a/ext/avatar_post/main.php b/ext/avatar_post/main.php index 231a19a26..79a807c23 100644 --- a/ext/avatar_post/main.php +++ b/ext/avatar_post/main.php @@ -25,7 +25,7 @@ public function onInitUserConfig(InitUserConfigEvent $event): void public function onPageRequest(PageRequestEvent $event): void { - global $page, $user, $user_config; + global $page, $user; if ($event->page_matches("set_avatar/{image_id}", method: "POST", permission: Permissions::CHANGE_USER_SETTING)) { $image_id = int_escape($event->get_arg('image_id')); $page->set_mode(PageMode::REDIRECT); @@ -35,7 +35,7 @@ public function onPageRequest(PageRequestEvent $event): void $this->theme->display_avatar_edit_page($page, $image_id); } elseif ($event->page_matches("save_avatar", method: "POST", permission: Permissions::CHANGE_USER_SETTING)) { $settings = ConfigSaveEvent::postToSettings($event->POST); - send_event(new ConfigSaveEvent($user_config, $settings)); + send_event(new ConfigSaveEvent($user->get_config(), $settings)); $page->flash("Image set as avatar"); $page->set_mode(PageMode::REDIRECT); if (key_exists(AvatarPostConfig::AVATAR_ID, $settings) && is_int($settings[AvatarPostConfig::AVATAR_ID])) { @@ -48,10 +48,10 @@ public function onPageRequest(PageRequestEvent $event): void public function onUserOptionsBuilding(UserOptionsBuildingEvent $event): void { - global $config, $user_config; + global $config, $user; $sb = $event->panel->create_new_block("Avatar"); $sb->add_int_option(AvatarPostConfig::AVATAR_ID, 'Avatar post ID: '); - $image_id = $user_config->get_int(AvatarPostConfig::AVATAR_ID, null); + $image_id = $user->get_config()->get_int(AvatarPostConfig::AVATAR_ID, null); if (!is_null($image_id)) { $sb->add_label("
Change cropping"); } @@ -85,7 +85,7 @@ public function avatar_html(User $user): HTMLElement|null public function get_avatar_html(User $user): HTMLElement|null { global $database, $config; - $user_config = UserConfig::get_for_user($user); + $user_config = $user->get_config(); $id = $user_config->get_int(AvatarPostConfig::AVATAR_ID, 0); if ($id === 0) { return null; diff --git a/ext/avatar_post/theme.php b/ext/avatar_post/theme.php index 1974de66e..e4bb879ee 100644 --- a/ext/avatar_post/theme.php +++ b/ext/avatar_post/theme.php @@ -12,7 +12,7 @@ class AvatarPostTheme extends Themelet { public function display_avatar_edit_page(Page $page, int $image_id): void { - global $user, $user_config; + global $user; /** @var BuildAvatarEvent $avatar_e */ $avatar_e = send_event(new BuildAvatarEvent($user)); $current = $avatar_e->html; diff --git a/ext/biography/main.php b/ext/biography/main.php index 13de9b3ce..1d2773caf 100644 --- a/ext/biography/main.php +++ b/ext/biography/main.php @@ -13,8 +13,7 @@ public function onUserPageBuilding(UserPageBuildingEvent $event): void { global $page, $user; $duser = $event->display_user; - $duser_config = UserConfig::get_for_user($event->display_user); - $bio = $duser_config->get_string("biography", ""); + $bio = $duser->get_config()->get_string("biography", ""); if ($user->id == $duser->id) { $this->theme->display_composer($page, $bio); @@ -25,11 +24,11 @@ public function onUserPageBuilding(UserPageBuildingEvent $event): void public function onPageRequest(PageRequestEvent $event): void { - global $page, $user, $user_config; + global $page, $user; if ($event->page_matches("biography", method: "POST")) { $bio = $event->get_POST('biography'); log_info("biography", "Set biography to $bio"); - $user_config->set_string("biography", $bio); + $user->get_config()->set_string("biography", $bio); $page->flash("Bio Updated"); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(referer_or(make_link())); diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index a73676fe0..f268ef3e9 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -110,11 +110,11 @@ public function onAdminAction(AdminActionEvent $event): void public function onLog(LogEvent $event): void { - global $user_config; + global $user; if (self::$IMPORT_RUNNING) { - $all = $user_config->get_bool(CronUploaderConfig::INCLUDE_ALL_LOGS); - if ($event->priority >= $user_config->get_int(CronUploaderConfig::LOG_LEVEL) && + $all = $user->get_config()->get_bool(CronUploaderConfig::INCLUDE_ALL_LOGS); + if ($event->priority >= $user->get_config()->get_int(CronUploaderConfig::LOG_LEVEL) && ($event->section == self::NAME || $all)) { $output = "[" . date('Y-m-d H:i:s') . "] " . ($all ? '[' . $event->section . '] ' : '') . "[" . LOGGING_LEVEL_NAMES[$event->priority] . "] " . $event->message; @@ -186,8 +186,8 @@ private function restage_folder(string $folder): void private function clear_folder(string $folder): void { - global $page, $user_config; - $path = join_path($user_config->get_string(CronUploaderConfig::DIR), $folder); + global $page, $user; + $path = join_path($user->get_config()->get_string(CronUploaderConfig::DIR), $folder); deltree($path); $page->flash("Cleared $path"); } @@ -195,9 +195,9 @@ private function clear_folder(string $folder): void private function get_cron_url(): string { - global $user_config; + global $user; - $user_api_key = $user_config->get_string(UserConfig::API_KEY, "API_KEY"); + $user_api_key = $user->get_config()->get_string(UserConfig::API_KEY, "API_KEY"); return make_http(make_link("/cron_upload/run", "api_key=".urlencode($user_api_key))); } @@ -255,34 +255,34 @@ private function display_documentation(): void public function get_queue_dir(): string { - global $user_config; + global $user; - $dir = $user_config->get_string(CronUploaderConfig::DIR); + $dir = $user->get_config()->get_string(CronUploaderConfig::DIR); return join_path($dir, self::QUEUE_DIR); } public function get_uploaded_dir(): string { - global $user_config; + global $user; - $dir = $user_config->get_string(CronUploaderConfig::DIR); + $dir = $user->get_config()->get_string(CronUploaderConfig::DIR); return join_path($dir, self::UPLOADED_DIR); } public function get_failed_dir(): string { - global $user_config; + global $user; - $dir = $user_config->get_string(CronUploaderConfig::DIR); + $dir = $user->get_config()->get_string(CronUploaderConfig::DIR); return join_path($dir, self::FAILED_DIR); } private function prep_root_dir(): string { - global $user_config; + global $user; // Determine directory (none = default) - $dir = $user_config->get_string(CronUploaderConfig::DIR); + $dir = $user->get_config()->get_string(CronUploaderConfig::DIR); // Make the directory if it doesn't exist yet if (!is_dir($this->get_queue_dir())) { @@ -300,9 +300,9 @@ private function prep_root_dir(): string private function get_lock_file(): string { - global $user_config; + global $user; - $root_dir = $user_config->get_string(CronUploaderConfig::DIR); + $root_dir = $user->get_config()->get_string(CronUploaderConfig::DIR); return join_path($root_dir, ".lock"); } @@ -311,7 +311,7 @@ private function get_lock_file(): string */ public function process_upload(): bool { - global $database, $user, $user_config, $config, $_shm_load_start; + global $database, $user, $config, $_shm_load_start; $max_time = intval(ini_get('max_execution_time')) * .8; @@ -375,7 +375,7 @@ public function process_upload(): bool $failed++; $this->log_message(SCORE_LOG_ERROR, "(" . gettype($e) . ") " . $e->getMessage()); $this->log_message(SCORE_LOG_ERROR, $e->getTraceAsString()); - if ($user_config->get_bool(CronUploaderConfig::STOP_ON_ERROR)) { + if ($user->get_config()->get_bool(CronUploaderConfig::STOP_ON_ERROR)) { break; } else { $this->move_uploaded($img[0], $img[1], $output_subdir, true); @@ -404,9 +404,9 @@ public function process_upload(): bool private function move_uploaded(string $path, string $filename, string $output_subdir, bool $corrupt = false): void { - global $user_config; + global $user; - $rootDir = $user_config->get_string(CronUploaderConfig::DIR); + $rootDir = $user->get_config()->get_string(CronUploaderConfig::DIR); $rootLength = strlen($rootDir); if ($rootDir[$rootLength - 1] == "/" || $rootDir[$rootLength - 1] == "\\") { $rootLength--; @@ -514,9 +514,9 @@ private function log_message(int $severity, string $message): void private function get_log_file(): string { - global $user_config; + global $user; - $dir = $user_config->get_string(CronUploaderConfig::DIR); + $dir = $user->get_config()->get_string(CronUploaderConfig::DIR); return join_path($dir, "uploads.log"); } diff --git a/ext/cron_uploader/theme.php b/ext/cron_uploader/theme.php index 201fbe03e..1b716544e 100644 --- a/ext/cron_uploader/theme.php +++ b/ext/cron_uploader/theme.php @@ -34,7 +34,7 @@ public function display_documentation( string $cron_url, ?array $log_entries ): void { - global $page, $config, $user_config; + global $page, $config, $user; $info_html = ""; @@ -109,7 +109,7 @@ public function display_documentation(
  • If an import is already running, another cannot start until it is done.
  • Each time it runs it will import for up to ".number_format($max_time)." seconds. This is controlled by the PHP max execution time.
  • Uploaded images will be moved to the 'uploaded' directory into a subfolder named after the time the import started. It's recommended that you remove everything out of this directory from time to time. If you have admin controls enabled, this can be done from Board Admin.
  • -
  • If you enable the db logging extension, you can view the log output on this screen. Otherwise the log will be written to a file at ".$user_config->get_string(CronUploaderConfig::DIR).DIRECTORY_SEPARATOR."uploads.log
  • +
  • If you enable the db logging extension, you can view the log output on this screen. Otherwise the log will be written to a file at ".$user->get_config()->get_string(CronUploaderConfig::DIR).DIRECTORY_SEPARATOR."uploads.log
  • "; diff --git a/ext/filter/theme.php b/ext/filter/theme.php index b362a6e13..0980fa188 100644 --- a/ext/filter/theme.php +++ b/ext/filter/theme.php @@ -10,11 +10,11 @@ class FilterTheme extends Themelet { public function addFilterBox(): void { - global $config, $page, $user, $user_config; + global $config, $page, $user; // If user is not able to set their own filters, use the default filters. if ($user->can(Permissions::CHANGE_USER_SETTING)) { - $tags = $user_config->get_string("filter_tags"); + $tags = $user->get_config()->get_string("filter_tags"); } else { $tags = $config->get_string("filter_tags"); } diff --git a/ext/private_image/main.php b/ext/private_image/main.php index 235bbbefe..5f83ec3c8 100644 --- a/ext/private_image/main.php +++ b/ext/private_image/main.php @@ -34,7 +34,7 @@ public function onUserOptionsBuilding(UserOptionsBuildingEvent $event): void public function onPageRequest(PageRequestEvent $event): void { - global $page, $user, $user_config; + global $page, $user; if ($event->page_matches("privatize_image/{image_id}", method: "POST", permission: Permissions::SET_PRIVATE_IMAGE)) { $image_id = $event->get_iarg('image_id'); @@ -68,8 +68,8 @@ public function onPageRequest(PageRequestEvent $event): void $set_default = array_key_exists("set_default", $event->POST); $view_default = array_key_exists("view_default", $event->POST); - $user_config->set_bool(PrivateImageConfig::USER_SET_DEFAULT, $set_default); - $user_config->set_bool(PrivateImageConfig::USER_VIEW_DEFAULT, $view_default); + $user->get_config()->set_bool(PrivateImageConfig::USER_SET_DEFAULT, $set_default); + $user->get_config()->set_bool(PrivateImageConfig::USER_VIEW_DEFAULT, $view_default); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("user")); @@ -89,8 +89,8 @@ public function onDisplayingImage(DisplayingImageEvent $event): void public const SEARCH_REGEXP = "/^private:(yes|no|any)/i"; public function onSearchTermParse(SearchTermParseEvent $event): void { - global $user, $user_config; - $show_private = $user_config->get_bool(PrivateImageConfig::USER_VIEW_DEFAULT); + global $user; + $show_private = $user->get_config()->get_bool(PrivateImageConfig::USER_VIEW_DEFAULT); if (is_null($event->term) && $this->no_private_query($event->context)) { if ($show_private) { @@ -190,8 +190,8 @@ public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event): public function onImageAddition(ImageAdditionEvent $event): void { - global $user, $user_config; - if ($user_config->get_bool(PrivateImageConfig::USER_SET_DEFAULT) && $user->can(Permissions::SET_PRIVATE_IMAGE)) { + global $user; + if ($user->get_config()->get_bool(PrivateImageConfig::USER_SET_DEFAULT) && $user->can(Permissions::SET_PRIVATE_IMAGE)) { self::privatize_image($event->image->id); } } diff --git a/ext/rating/main.php b/ext/rating/main.php index 52d7f3584..94266aa7f 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -464,10 +464,10 @@ public static function get_user_class_privs(User $user): array */ public static function get_user_default_ratings(): array { - global $user_config, $user; + global $user; $available = self::get_user_class_privs($user); - $selected = $user_config->get_array(RatingsConfig::USER_DEFAULTS); + $selected = $user->get_config()->get_array(RatingsConfig::USER_DEFAULTS); return array_intersect($available, $selected); } diff --git a/ext/rating/test.php b/ext/rating/test.php index 0f5e24ea4..a5c16da59 100644 --- a/ext/rating/test.php +++ b/ext/rating/test.php @@ -41,7 +41,7 @@ public function testRatingExplicit(): void public function testUserConfig(): void { - global $config, $user_config; + global $config, $user; // post a safe image and an explicit image $this->log_in_as_user(); @@ -56,7 +56,7 @@ public function testUserConfig(): void $config->set_array("ext_rating_user_privs", ["s", "q", "e"]); // user prefers safe-only by default - $user_config->set_array(RatingsConfig::USER_DEFAULTS, ["s"]); + $user->get_config()->set_array(RatingsConfig::USER_DEFAULTS, ["s"]); // search with no tags should return only safe image $this->assert_search_results([], [$image_id_s]); @@ -67,7 +67,7 @@ public function testUserConfig(): void // If user prefers to see all images, going to the safe image // and clicking next should show the explicit image - $user_config->set_array(RatingsConfig::USER_DEFAULTS, ["s", "q", "e"]); + $user->get_config()->set_array(RatingsConfig::USER_DEFAULTS, ["s", "q", "e"]); $next = $image_s->get_next(); $this->assertNotNull($next); $this->assertEquals($next->id, $image_id_e); @@ -75,13 +75,13 @@ public function testUserConfig(): void // If the user prefers to see only safe images by default, then // going to the safe image and clicking next should not show // the explicit image (See bug #984) - $user_config->set_array(RatingsConfig::USER_DEFAULTS, ["s"]); + $user->get_config()->set_array(RatingsConfig::USER_DEFAULTS, ["s"]); $this->assertEquals($image_s->get_next(), null); } public function testCountImages(): void { - global $config, $user_config; + global $config, $user; $this->log_in_as_user(); @@ -96,7 +96,7 @@ public function testCountImages(): void send_event(new RatingSetEvent($image_e, "e")); $config->set_array("ext_rating_user_privs", ["s", "q"]); - $user_config->set_array(RatingsConfig::USER_DEFAULTS, ["s"]); + $user->get_config()->set_array(RatingsConfig::USER_DEFAULTS, ["s"]); $this->assertEquals(1, Search::count_images(["rating=s"]), "UserClass has access to safe, show safe"); $this->assertEquals(2, Search::count_images(["rating=*"]), "UserClass has access to s/q - if user asks for everything, show those two but hide e"); @@ -107,10 +107,10 @@ public function testCountImages(): void // that it doesn't mess with other unrelated tests public function tearDown(): void { - global $user_config; + global $user; $this->log_in_as_user(); - $user_config->set_array(RatingsConfig::USER_DEFAULTS, ["?", "s", "q", "e"]); + $user->get_config()->set_array(RatingsConfig::USER_DEFAULTS, ["?", "s", "q", "e"]); parent::tearDown(); } diff --git a/ext/ratings_blur/main.php b/ext/ratings_blur/main.php index 821ec0f23..2cc8a9f54 100644 --- a/ext/ratings_blur/main.php +++ b/ext/ratings_blur/main.php @@ -59,9 +59,9 @@ public function onSetupBuilding(SetupBuildingEvent $event): void public function blur(string $rating): bool { - global $user_config; + global $user; - $blur_ratings = $user_config->get_array(RatingsBlurConfig::USER_DEFAULTS); + $blur_ratings = $user->get_config()->get_array(RatingsBlurConfig::USER_DEFAULTS); if (in_array(RatingsBlurConfig::NULL_OPTION, $blur_ratings)) { return false; } diff --git a/ext/ratings_blur/test.php b/ext/ratings_blur/test.php index 801502551..cceeba792 100644 --- a/ext/ratings_blur/test.php +++ b/ext/ratings_blur/test.php @@ -30,7 +30,7 @@ public function testRatingBlurDefault(): void public function testRatingBlurGlobalConfig(): void { - global $config, $user_config; + global $config; // change global setting: don't blur explict, only blur safe $config->set_array(RatingsBlurConfig::GLOBAL_DEFAULTS, ["s"]); @@ -67,14 +67,14 @@ public function testRatingBlurGlobalConfig(): void public function testRatingBlurUserConfig(): void { - global $config, $user_config; + global $config, $user; // set global default to blur all, so we can test it is overriden $config->set_array(RatingsBlurConfig::GLOBAL_DEFAULTS, array_keys(ImageRating::$known_ratings)); $this->log_in_as_user(); // don't blur explict, blur safe - $user_config->set_array(RatingsBlurConfig::USER_DEFAULTS, ["s"]); + $user->get_config()->set_array(RatingsBlurConfig::USER_DEFAULTS, ["s"]); $image_id_e = $this->post_image("tests/bedroom_workshop.jpg", "bedroom"); $image_e = Image::by_id_ex($image_id_e); @@ -93,7 +93,7 @@ public function testRatingBlurUserConfig(): void $this->assert_text("blur"); // don't blur any - $user_config->set_array(RatingsBlurConfig::USER_DEFAULTS, [RatingsBlurConfig::NULL_OPTION]); + $user->get_config()->set_array(RatingsBlurConfig::USER_DEFAULTS, [RatingsBlurConfig::NULL_OPTION]); $this->get_page("post/list"); $this->assert_no_text("blur"); @@ -122,10 +122,10 @@ private function delete_test_user(string $username): void // that it doesn't mess with other unrelated tests public function tearDown(): void { - global $user_config; + global $user; $this->log_in_as_user(); - $user_config->set_array(RatingsBlurConfig::USER_DEFAULTS, RatingsBlurConfig::DEFAULT_OPTIONS); + $user->get_config()->set_array(RatingsBlurConfig::USER_DEFAULTS, RatingsBlurConfig::DEFAULT_OPTIONS); parent::tearDown(); } diff --git a/ext/user/main.php b/ext/user/main.php index a0bfc752d..049272c79 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -386,9 +386,7 @@ private function display_stats(UserPageBuildingEvent $event): void if (!$user->is_anonymous()) { if ($user->id == $event->display_user->id || $user->can("edit_user_info")) { - $user_config = UserConfig::get_for_user($event->display_user); - - $uobe = send_event(new UserOperationsBuildingEvent($event->display_user, $user_config)); + $uobe = send_event(new UserOperationsBuildingEvent($event->display_user, $event->display_user->get_config())); $page->add_block(new Block("Operations", $this->theme->build_operations($event->display_user, $uobe), "main", 60)); } } diff --git a/ext/user_config/main.php b/ext/user_config/main.php index 2ad69f7f6..66192ff63 100644 --- a/ext/user_config/main.php +++ b/ext/user_config/main.php @@ -4,10 +4,6 @@ namespace Shimmie2; -/** @var Config */ -global $user_config; - - // The user object doesn't exist until after database setup operations and the first wave of InitExtEvents, // so we can't reliably access this data until then. This event is triggered by the system after all of that is done. class InitUserConfigEvent extends Event @@ -54,21 +50,6 @@ public function onInitExt(InitExtEvent $event): void $config->set_default_bool(self::ENABLE_API_KEYS, false); } - public function onUserLogin(UserLoginEvent $event): void - { - global $user_config; - - $user_config = self::get_for_user($event->user); - } - - public static function get_for_user(User $user): Config - { - global $database; - $user_config = new DatabaseConfig($database, "user_config", "user_id", "{$user->id}"); - send_event(new InitUserConfigEvent($user, $user_config)); - return $user_config; - } - public function onDatabaseUpgrade(DatabaseUpgradeEvent $event): void { global $database; @@ -105,7 +86,7 @@ public function onUserBlockBuilding(UserBlockBuildingEvent $event): void public function onPageRequest(PageRequestEvent $event): void { - global $user, $database, $config, $page, $user_config; + global $user, $database, $config, $page; if ($config->get_bool(self::ENABLE_API_KEYS)) { if ($event->get_GET("api_key") && $user->is_anonymous()) { @@ -121,7 +102,7 @@ public function onPageRequest(PageRequestEvent $event): void } if ($event->page_matches("user_admin/reset_api_key", method: "POST")) { - $user_config->set_string(self::API_KEY, ""); + $user->get_config()->set_string(self::API_KEY, ""); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("user")); @@ -129,7 +110,7 @@ public function onPageRequest(PageRequestEvent $event): void } if ($event->page_matches("user_config", method: "GET", permission: Permissions::CHANGE_USER_SETTING)) { - $uobe = send_event(new UserOptionsBuildingEvent($user, new SetupPanel($user_config))); + $uobe = send_event(new UserOptionsBuildingEvent($user, new SetupPanel($user->get_config()))); $this->theme->display_user_config_page($page, $uobe->user, $uobe->panel); } if ($event->page_matches("user_config/save", method: "POST", permission: Permissions::CHANGE_USER_SETTING)) { @@ -142,8 +123,7 @@ public function onPageRequest(PageRequestEvent $event): void throw new PermissionDenied("You do not have permission to change other user's settings"); } - $target_config = UserConfig::get_for_user($duser); - send_event(new ConfigSaveEvent($target_config, ConfigSaveEvent::postToSettings($event->POST))); + send_event(new ConfigSaveEvent($duser->get_config(), ConfigSaveEvent::postToSettings($event->POST))); $page->flash("Config saved"); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("user_config"));