Skip to content

Commit a05c53f

Browse files
committed
Added the ability to test settings before saving.
1 parent 8719698 commit a05c53f

File tree

6 files changed

+305
-28
lines changed

6 files changed

+305
-28
lines changed

public/admin/rollbar.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*
44
* @var {{
55
* nonce: string,
6+
* rest_nonce: string,
67
* rest_root: string,
78
* plugin_url: string,
89
* }} rollbarSettings Declared in SettingsPage::enqueueAdminScripts().
@@ -108,6 +109,38 @@ document.addEventListener('DOMContentLoaded', () => {
108109
return el.firstChild;
109110
};
110111

112+
/**
113+
* Collects the Rollbar settings from the form.
114+
*
115+
* @returns {object} The Rollbar settings.
116+
*/
117+
const collectSettings = () => {
118+
const settings = {};
119+
document.querySelectorAll('.wrap form [name^="rollbar_wp"]').forEach(input => {
120+
const isArray = input.name.endsWith('[]');
121+
let name = input.name.slice(input.name.indexOf('[') + 1, input.name.indexOf(']'));
122+
let value = input.value;
123+
if (input.type === 'checkbox' && !isArray) {
124+
value = input.checked;
125+
}
126+
if (input.type === 'number') {
127+
value = parseInt(value);
128+
}
129+
if (isArray) {
130+
if (!(name in settings)) {
131+
settings[name] = [];
132+
}
133+
if (input.type === 'checkbox' && !input.checked) {
134+
return;
135+
}
136+
settings[name].push(value);
137+
return;
138+
}
139+
settings[name] = value;
140+
});
141+
return settings;
142+
};
143+
111144
/**
112145
* Request a test of the Rollbar config in PHP via the REST API.
113146
*/
@@ -122,8 +155,10 @@ document.addEventListener('DOMContentLoaded', () => {
122155
fetch(`${rollbarSettings.rest_root}rollbar/v1/test-php-logging`, {
123156
method: 'POST',
124157
headers: {
125-
'X-WP-Nonce': rollbarSettings.nonce,
158+
'X-WP-Nonce': rollbarSettings.rest_nonce,
159+
'Content-Type': 'application/json',
126160
},
161+
body: JSON.stringify(collectSettings()),
127162
}).then(response => {
128163
response.json().then(data => {
129164
if (data.success) {

src/API/AdminAPI.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Rollbar\Rollbar;
77
use Rollbar\WordPress\Lib\AbstractSingleton;
88
use Rollbar\WordPress\Plugin;
9+
use Rollbar\WordPress\Settings;
910
use Throwable;
1011
use WP_REST_Request;
1112
use WP_REST_Response;
@@ -61,11 +62,25 @@ protected function registerRoutes(): void
6162
*/
6263
return apply_filters(
6364
hook_name: 'rollbar_api_admin_permission',
64-
value: current_user_can('manage_options'),
65+
value: is_user_logged_in() && current_user_can('manage_options'),
6566
route: 'test-php-logging',
6667
request: $request,
6768
);
6869
},
70+
'args' => [
71+
'server_side_access_token' => [
72+
'required' => true,
73+
'sanitize_callback' => 'sanitize_text_field',
74+
],
75+
'environment' => [
76+
'required' => true,
77+
'sanitize_callback' => 'sanitize_text_field',
78+
],
79+
'included_errno' => [
80+
'required' => true,
81+
'sanitize_callback' => 'sanitize_text_field',
82+
],
83+
],
6984
],
7085
);
7186
}
@@ -80,7 +95,19 @@ protected function registerRoutes(): void
8095
*/
8196
public function handleTestPhpLogging(WP_REST_Request $request): WP_REST_Response
8297
{
98+
99+
// Get the settings from the request.
100+
$settings = [];
101+
foreach (Settings::listOptions() as $option) {
102+
$settings[$option] = $request->get_param($option);
103+
}
104+
105+
$settings = Settings::normalizeSettings($settings);
106+
83107
$plugin = Plugin::getInstance();
108+
foreach ($settings as $key => $value) {
109+
$plugin->setSetting($key, $value);
110+
}
84111

85112
try {
86113
$plugin->initPhpLogging(ignoreEnabledSetting: true);

src/Admin/SettingsPage.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ public function enqueueAdminScripts(string $hook): void
208208
handle: 'rollbar-admin-js',
209209
object_name: 'rollbarSettings',
210210
l10n: [
211-
'nonce' => wp_create_nonce('wp_rest'),
211+
'nonce' => wp_create_nonce('rollbar_wp_api_test_logging'),
212+
'rest_nonce' => wp_create_nonce('wp_rest'),
212213
'rest_root' => esc_url_raw(rest_url()),
213214
'plugin_url' => plugin_dir_url(ROLLBAR_PLUGIN_FILE),
214215
],

src/Setting.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ public function render(array $args): void
8484
echo $this->input;
8585
}
8686

87+
/**
88+
* Coerces the value of the setting to the appropriate type.
89+
*
90+
* @param mixed $value The value to be coerced.
91+
* @return mixed The coerced value.
92+
*/
93+
public function coerceValue(mixed $value): mixed
94+
{
95+
return match ($this->type) {
96+
SettingType::Boolean => Settings::toBoolean($value),
97+
SettingType::CheckBox => Settings::toStringArray($value),
98+
SettingType::Integer => Settings::toInteger($value),
99+
SettingType::Select, SettingType::Text => Settings::toString($value),
100+
SettingType::Skip => null,
101+
};
102+
}
103+
87104
/**
88105
* Creates and returns the input object otherwise null if it should be skipped.
89106
*

src/Settings.php

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,24 @@ public static function settings(): array
8181
default: false,
8282
section: 'rollbar_wp_general',
8383
),
84+
'server_side_access_token' => new Setting(
85+
id: 'server_side_access_token',
86+
type: SettingType::Text,
87+
default: '',
88+
section: 'rollbar_wp_general',
89+
),
8490
'js_logging_enabled' => new Setting(
8591
id: 'js_logging_enabled',
8692
type: SettingType::Boolean,
8793
default: false,
8894
section: 'rollbar_wp_general',
8995
),
96+
'client_side_access_token' => new Setting(
97+
id: 'client_side_access_token',
98+
type: SettingType::Text,
99+
default: '',
100+
section: 'rollbar_wp_general',
101+
),
90102
'environment' => new Setting(
91103
id: 'environment',
92104
type: SettingType::Text,
@@ -505,7 +517,7 @@ public function saveSettings(array $settings): void
505517
*
506518
* @return void
507519
*/
508-
public function restoreDefaults()
520+
public function restoreDefaults(): void
509521
{
510522
$settings = [];
511523

@@ -549,6 +561,69 @@ public static function toBoolean(mixed $value): bool
549561
return boolval($value);
550562
}
551563

564+
/**
565+
* Returns a reasonable integer from a given value.
566+
*
567+
* @param mixed $value The value to convert to an integer.
568+
* @return int
569+
*/
570+
public static function toInteger(mixed $value): int
571+
{
572+
if (is_int($value)) {
573+
return $value;
574+
}
575+
if (is_string($value)) {
576+
return intval($value);
577+
}
578+
if (is_bool($value)) {
579+
return $value ? 1 : 0;
580+
}
581+
if (is_numeric($value)) {
582+
return intval($value);
583+
}
584+
return 0;
585+
}
586+
587+
/**
588+
* Returns a reasonable string from a given value.
589+
*
590+
* @param mixed $value The value to convert to a string.
591+
* @return string
592+
*/
593+
public static function toString(mixed $value): string
594+
{
595+
if (is_string($value)) {
596+
return $value;
597+
}
598+
if (is_bool($value)) {
599+
return $value ? 'true' : 'false';
600+
}
601+
if (is_int($value)) {
602+
return (string) $value;
603+
}
604+
if (is_numeric($value)) {
605+
return (string) $value;
606+
}
607+
return '';
608+
}
609+
610+
/**
611+
* Returns an array of strings from a given value.
612+
*
613+
* @param mixed $value The value to convert to an array of strings.
614+
* @return string[]
615+
*/
616+
public static function toStringArray(mixed $value): array
617+
{
618+
if (is_array($value)) {
619+
return array_map(fn($v) => self::toString($v), $value);
620+
}
621+
if (is_string($value)) {
622+
return [$value];
623+
}
624+
return [];
625+
}
626+
552627
/**
553628
* Fetch settings provided in Admin -> Tools -> Rollbar
554629
*
@@ -574,6 +649,17 @@ private function fetchSettings(): void
574649
$options['client_side_access_token'] ?? null,
575650
);
576651

652+
$this->settings = self::normalizeSettings($options);
653+
}
654+
655+
/**
656+
* Normalizes the settings array to ensure all required fields are set and properly formatted.
657+
*
658+
* @param array<string, mixed> $options
659+
* @return array
660+
*/
661+
public static function normalizeSettings(array $options): array
662+
{
577663
$settings = [
578664
'php_logging_enabled' => (!empty($options['php_logging_enabled'])) ? 1 : 0,
579665
'js_logging_enabled' => (!empty($options['js_logging_enabled'])) ? 1 : 0,
@@ -591,20 +677,20 @@ private function fetchSettings(): void
591677
// Filter out options that are not in the list of options.
592678
$options = array_intersect_key($options, array_flip(self::listOptions()));
593679

594-
foreach (self::listOptions() as $option) {
680+
foreach (self::settings() as $key => $setting) {
595681
// 'access_token' and 'enabled' are different in WordPress plugin
596682
// look for 'server_side_access_token' and 'php_logging_enabled' above
597-
if (in_array($option, ['access_token', 'enabled'])) {
683+
if (in_array($key, ['access_token', 'enabled'])) {
598684
continue;
599685
}
600686

601-
if (!isset($options[$option])) {
602-
$value = $this->getDefaultOption($option);
603-
} else {
604-
$value = $options[$option];
687+
$value = $options[$key] ?? ($setting?->default ?? null);
688+
if ($setting !== null) {
689+
$value = $setting->coerceValue($value);
690+
} elseif (!isset($options[$key])) {
691+
continue;
605692
}
606-
607-
$settings[$option] = $value;
693+
$settings[$key] = $value;
608694
}
609695

610696
/**
@@ -614,7 +700,7 @@ private function fetchSettings(): void
614700
* @since 3.0.0
615701
*
616702
*/
617-
$this->settings = apply_filters('rollbar_plugin_settings', $settings);
703+
return apply_filters('rollbar_plugin_settings', $settings);
618704
}
619705

620706
/**

0 commit comments

Comments
 (0)