[settings] postToSettings
Trying to keep the weird POST-formatting to a minimum, using real types where possible
This commit is contained in:
parent
7353683a13
commit
edb4ca5e74
3 changed files with 100 additions and 33 deletions
|
@ -16,11 +16,11 @@ require_once "config.php";
|
|||
class ConfigSaveEvent extends Event
|
||||
{
|
||||
public Config $config;
|
||||
/** @var array<string, mixed> $values */
|
||||
/** @var array<string, null|string|int|boolean|array<string>> $values */
|
||||
public array $values;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $values
|
||||
* @param array<string, null|string|int|boolean|array<string>> $values
|
||||
*/
|
||||
public function __construct(Config $config, array $values)
|
||||
{
|
||||
|
@ -28,6 +28,55 @@ class ConfigSaveEvent extends Event
|
|||
$this->config = $config;
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert POST data to settings data, eg
|
||||
*
|
||||
* $_POST = [
|
||||
* "_type_mynull" => "string",
|
||||
* "_type_mystring" => "string",
|
||||
* "_config_mystring" => "hello world!",
|
||||
* "_type_myint" => "int",
|
||||
* "_config_myint" => "42KB",
|
||||
* ]
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* $config = [
|
||||
* "mynull" => null,
|
||||
* "mystring" => "hello world!",
|
||||
* "myint" => 43008,
|
||||
* ]
|
||||
*
|
||||
* @param array<string, string|string[]> $post
|
||||
* @return array<string, null|string|int|boolean|array<string>>
|
||||
*/
|
||||
public static function postToSettings(array $post): array
|
||||
{
|
||||
$settings = [];
|
||||
foreach ($post as $key => $type) {
|
||||
if (str_starts_with($key, "_type_")) {
|
||||
$key = str_replace("_type_", "", $key);
|
||||
$value = $post["_config_$key"] ?? null;
|
||||
if ($type === "string") {
|
||||
$settings[$key] = $value;
|
||||
} elseif ($type === "int") {
|
||||
assert(is_string($value));
|
||||
$settings[$key] = $value ? parse_shorthand_int($value) : null;
|
||||
} elseif ($type === "bool") {
|
||||
$settings[$key] = $value === "on";
|
||||
} elseif ($type === "array") {
|
||||
$settings[$key] = $value;
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
$value = implode(", ", $value);
|
||||
}
|
||||
throw new InvalidInput("Invalid type '$value' for key '$key'");
|
||||
}
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -343,7 +392,7 @@ class Setup extends Extension
|
|||
send_event(new SetupBuildingEvent($panel));
|
||||
$this->theme->display_page($page, $panel);
|
||||
} elseif ($event->page_matches("setup/save", method: "POST", permission: Permissions::CHANGE_SETTING)) {
|
||||
send_event(new ConfigSaveEvent($config, $event->POST));
|
||||
send_event(new ConfigSaveEvent($config, ConfigSaveEvent::postToSettings($event->POST)));
|
||||
$page->flash("Config saved");
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("setup"));
|
||||
|
@ -385,35 +434,14 @@ class Setup extends Extension
|
|||
public function onConfigSave(ConfigSaveEvent $event): void
|
||||
{
|
||||
$config = $event->config;
|
||||
foreach ($event->values as $_name => $junk) {
|
||||
if (substr($_name, 0, 6) == "_type_") {
|
||||
$name = substr($_name, 6);
|
||||
$type = $event->values["_type_$name"];
|
||||
if (isset($event->values["_config_$name"])) {
|
||||
$value = $event->values["_config_$name"];
|
||||
switch ($type) {
|
||||
case "string":
|
||||
$config->set_string($name, $value);
|
||||
break;
|
||||
case "int":
|
||||
$config->set_int($name, parse_shorthand_int((string)$value));
|
||||
break;
|
||||
case "bool":
|
||||
$config->set_bool($name, bool_escape($value));
|
||||
break;
|
||||
case "array":
|
||||
$config->set_array($name, $value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// browsers don't send empty checkboxes, false value must be stored in case default is true
|
||||
if ($type == "bool") {
|
||||
$config->set_bool($name, false);
|
||||
} else {
|
||||
$config->delete($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($event->values as $key => $value) {
|
||||
match(true) {
|
||||
is_null($value) => $config->delete($key),
|
||||
is_string($value) => $config->set_string($key, $value),
|
||||
is_int($value) => $config->set_int($key, $value),
|
||||
is_bool($value) => $config->set_bool($key, $value),
|
||||
is_array($value) => $config->set_array($key, $value),
|
||||
};
|
||||
}
|
||||
log_warning("setup", "Configuration updated");
|
||||
}
|
||||
|
|
|
@ -6,6 +6,45 @@ namespace Shimmie2;
|
|||
|
||||
class SetupTest extends ShimmiePHPUnitTestCase
|
||||
{
|
||||
public function testParseSettings(): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
[
|
||||
"mynull" => null,
|
||||
"mystring" => "hello world!",
|
||||
"myint" => 42 * 1024,
|
||||
"mybool_true" => true,
|
||||
"mybool_false" => false,
|
||||
"myarray" => ["hello", "world"],
|
||||
],
|
||||
ConfigSaveEvent::postToSettings([
|
||||
// keys in POST that don't start with _type or _config are ignored
|
||||
"some_post" => "value",
|
||||
// _type with no _config means the value is null
|
||||
"_type_mynull" => "string",
|
||||
// strings left as-is
|
||||
"_type_mystring" => "string",
|
||||
"_config_mystring" => "hello world!",
|
||||
// ints parsed from human-readable form
|
||||
"_type_myint" => "int",
|
||||
"_config_myint" => "42KB",
|
||||
// HTML booleans (HTML checkboxes are "on" or undefined, there is no "off")
|
||||
"_type_mybool_true" => "bool",
|
||||
"_config_mybool_true" => "on",
|
||||
"_type_mybool_false" => "bool",
|
||||
// Arrays are... passed as arrays? Does this work?
|
||||
"_type_myarray" => "array",
|
||||
"_config_myarray" => ["hello", "world"],
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertException(InvalidInput::class, function () {
|
||||
ConfigSaveEvent::postToSettings([
|
||||
"_type_myint" => "cake",
|
||||
"_config_myint" => "tasty",
|
||||
]);
|
||||
});
|
||||
}
|
||||
public function testNiceUrlsTest(): void
|
||||
{
|
||||
# XXX: this only checks that the text is "ok", to check
|
||||
|
|
|
@ -146,7 +146,7 @@ class UserConfig extends Extension
|
|||
}
|
||||
|
||||
$target_config = UserConfig::get_for_user($duser->id);
|
||||
send_event(new ConfigSaveEvent($target_config, $event->POST));
|
||||
send_event(new ConfigSaveEvent($target_config, ConfigSaveEvent::postToSettings($event->POST)));
|
||||
$page->flash("Config saved");
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("user_config"));
|
||||
|
|
Reference in a new issue