Skip to content

Commit 8719698

Browse files
committed
Addressed items in code review.
1 parent efe4a1c commit 8719698

File tree

16 files changed

+168
-72
lines changed

16 files changed

+168
-72
lines changed

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,15 @@ The plugin provides a number of filters that allow you to customize the behavior
111111

112112
### `apply_filters('rollbar_api_admin_permission', bool $value, string $route, WP_REST_Request $request)`
113113

114-
Filter to allow or deny access to a Rollbar route in the WordPress REST API used in the WordPress Admin.
114+
Filter to allow or deny access to a Rollbar route in the WordPress REST API used in the WordPress Admin. Generally,
115+
this should be the same as the `rollbar_user_can_view_admin` filter.
115116

116117
**Parameters**
117118

118119
* `bool $value` - The initial value. Defaults is `true` for admin users, `false` for non-admin users.
119120
* `string $route` - The route being accessed.
120121
* `WP_REST_Request $request` - The REST request object.
121122

122-
### `apply_filters('rollbar_disable_admin', bool $disable)`
123-
124-
Filter to disable the admin settings page of the plugin.
125-
126-
This filter cannot override the `ROLLBAR_DISABLE_ADMIN` constant.
127-
128-
**Parameters**
129-
130-
* `bool $disable` - `true` to disable the admin settings page, `false` to enable it.
131-
132123
### `apply_filters('rollbar_js_config', array $config)`
133124

134125
Filters the Rollbar JavaScript configuration.
@@ -174,6 +165,16 @@ select the action on the settings page, or add it to the list of actions using t
174165
* `array<string, callable(string, mixed...):string> $handlers` - An associative array where the keys are action names
175166
and the values are the custom event handler.
176167

168+
### `apply_filters('rollbar_user_can_view_admin', bool $disable)`
169+
170+
Filter to enable / disable the admin settings page of the plugin for the current user.
171+
172+
This filter cannot override the `ROLLBAR_DISABLE_ADMIN` constant.
173+
174+
**Parameters**
175+
176+
* `bool $allow` - `true` to enable the admin settings page, `false` to disable it.
177+
177178
### Telemetry
178179

179180
Starting in version 3.0.0 of the Rollbar plugin, Telemetry is enabled by default. Telemetry is a feature that allows

public/admin/rollbar.js

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,41 @@ document.addEventListener('DOMContentLoaded', () => {
7070

7171
const testMessageContainer = document.getElementById('rollbar_test_message_container');
7272

73+
/**
74+
* Escapes HTML characters in the string.
75+
*
76+
* @param {string} str The string to escape.
77+
* @returns {string}
78+
*/
79+
const escapeHtml = (str) => {
80+
return new Option(str).innerHTML;
81+
};
82+
83+
/**
84+
* Escapes HTML characters in the string for use in an attribute.
85+
*
86+
* @param {string} str The string to escape.
87+
* @returns {string}
88+
*/
89+
const escapeHtmlAttr = (str) => {
90+
return ('' + str)
91+
.replace(/&/g, '&amp;')
92+
.replace(/"/g, '&quot;')
93+
.replace(/'/g, '&#39;')
94+
.replace(/</g, '&lt;')
95+
.replace(/>/g, '&gt;');
96+
};
97+
7398
/**
7499
* Returns a formatted notice message HTML Node.
75100
*
76101
* @param {string} type The notice type class.
77-
* @param {string} message The notice message.
102+
* @param {string} message The notice message. The message should be escaped before being passed in.
78103
* @returns {ChildNode}
79104
*/
80105
const makeNotice = (type, message) => {
81106
const el = document.createElement('div');
82-
el.innerHTML = `<div class="notice ${type} is-dismissible">${message}</div>`;
107+
el.innerHTML = `<div class="notice ${escapeHtmlAttr(type)} is-dismissible">${message}</div>`;
83108
return el.firstChild;
84109
};
85110

@@ -114,8 +139,8 @@ document.addEventListener('DOMContentLoaded', () => {
114139
'notice-error',
115140
`<p><strong>PHP Test:</strong> There was a problem accessing Rollbar service.</p>
116141
<ul>
117-
<li>Code:<code>${data.code}</code></li>
118-
<li>Message:<code>${data.message}</code></li>
142+
<li>Code:<code>${escapeHtml(data.code)}</code></li>
143+
<li>Message:<code>${escapeHtml(data.message)}</code></li>
119144
</ul>`,
120145
));
121146
})
@@ -174,26 +199,46 @@ document.addEventListener('DOMContentLoaded', () => {
174199
}
175200

176201
// Depending on the plugin status prior to loading the page, the Rollbar object may not exist. So we fetch it.
177-
if (window.Rollbar === undefined) {
178-
179-
fetch(rollbarSettings.plugin_url + "public/js/rollbar.snippet.js")
180-
.then(response => {
181-
if (!response.ok) {
182-
throw new Error('Network response was not ok');
183-
}
184-
return response.text();
185-
})
186-
.then(data => {
187-
eval(data);
188-
sendTestJsMessage(clientSideAccessToken, environment);
189-
})
190-
.catch(() => {
191-
jsFailNotice();
192-
});
193-
} else {
194-
// This is inside an "else" since the fetch is async.
202+
if (window.Rollbar !== undefined) {
195203
sendTestJsMessage(clientSideAccessToken, environment);
204+
return;
205+
}
206+
// If the Rollbar object doesn't exist, we need to load and configure it.
207+
window._rollbarConfig = {
208+
accessToken: clientSideAccessToken,
209+
captureUncaught: true,
210+
captureUnhandledRejections: true,
211+
payload: {
212+
environment: environment
213+
}
196214
}
215+
// Load the Rollbar JS library.
216+
const script = document.createElement('script');
217+
script.src = rollbarSettings.plugin_url + "public/js/rollbar.snippet.js";
218+
let mainRollbarScript = document.querySelector('script[src$="rollbar.min.js"]');
219+
220+
// Wait for both Rollbar JS libraries to load before sending the test message.
221+
script.addEventListener('load', () => {
222+
if (window.Rollbar !== undefined) {
223+
sendTestJsMessage(clientSideAccessToken, environment);
224+
return;
225+
}
226+
document.querySelector('script[src$="rollbar.min.js"]')?.addEventListener('load', () => {
227+
sendTestJsMessage(clientSideAccessToken, environment);
228+
});
229+
});
230+
231+
// Handle error loading either the snippet or main Rollbar JS library.
232+
window.addEventListener('error', (e) => {
233+
if (e.target === script || e.target === mainRollbarScript) {
234+
testMessageContainer.appendChild(makeNotice(
235+
'notice-error',
236+
`<p><strong>JS Test:</strong> There was an error loading the Rollbar JS library.</p>`,
237+
));
238+
}
239+
}, true);
240+
241+
document.body.appendChild(script);
197242
};
198243

199244
// Send Test Message

readme.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ Yes. It's actually the recommended method of installation.
116116
* Added support for `ROLLBAR_CLIENT_ACCESS_TOKEN` constant or environment variable to set the client access token.
117117
* Added support for `WP_PROXY_BYPASS_HOSTS`, `WP_PROXY_USERNAME`, and `WP_PROXY_PASSWORD` for better proxy management.
118118
* Added `rollbar_api_admin_permission` filter to allow custom authorization of the admin API.
119-
* Added `rollbar_disable_admin` filter to allow custom disabling of the admin page.
119+
* Added `rollbar_user_can_view_admin` filter to allow custom disabling of the admin page.
120120
* Added `rollbar_php_config` filter to allow more exact control over Rollbar PHP configurations.
121121
* Added `rollbar_telemetry_actions` filter to allow control of which actions are logged via telemetry.
122122
* Added `rollbar_telemetry_custom_handlers` filter to allow custom control over what is logged in telemetry messages.

src/API/AdminAPI.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,31 +83,32 @@ public function handleTestPhpLogging(WP_REST_Request $request): WP_REST_Response
8383
$plugin = Plugin::getInstance();
8484

8585
try {
86-
$plugin->initPhpLogging();
87-
$response = Rollbar::report(
86+
$plugin->initPhpLogging(ignoreEnabledSetting: true);
87+
$rollbarResponse = Rollbar::report(
8888
Level::INFO,
8989
'Test message from Rollbar WordPress plugin using PHP: ' .
9090
'integration with WordPress successful',
9191
);
9292
} catch (Throwable $exception) {
9393
return new WP_REST_Response(
9494
[
95-
'message' => $exception->getMessage(),
95+
'code' => 500,
9696
'success' => false,
97+
// Send the exception message to the client to the admin user so they can debug the issue.
98+
'message' => $exception->getMessage(),
9799
],
98100
500,
99101
);
100102
}
101103

102-
$info = $response->getInfo();
103-
104104
$response = [
105-
'code' => $response->getStatus(),
106-
'success' => true,
105+
'code' => $rollbarResponse->getStatus(),
106+
'success' => $rollbarResponse->getStatus() === 200,
107+
'message' => '',
107108
];
108-
if (is_array($info)) {
109-
$response = array_merge($response, $info);
110-
} else {
109+
110+
$info = $rollbarResponse->getInfo();
111+
if (is_string($info)) {
111112
$response['message'] = $info;
112113
}
113114

src/Admin/SettingsPage.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ final class SettingsPage extends AbstractSingleton
3939
*/
4040
protected function __construct()
4141
{
42-
if (Plugin::hideAdmin()) {
42+
if (!Plugin::userCanViewAdmin()) {
4343
return;
4444
}
4545
$this->hooks();
@@ -283,12 +283,16 @@ public function optionsPage(): void
283283
*/
284284
public static function restoreDefaultsAction(): void
285285
{
286+
if (!check_admin_referer('rollbar_wp_restore_defaults')) {
287+
return;
288+
}
286289
Settings::getInstance()->restoreDefaults();
287290

288291
FlashMessages::addMessage(
289292
message: 'Default Rollbar settings restored.',
290293
);
291294

292295
wp_redirect(admin_url('/options-general.php?page=rollbar_wp'));
296+
exit();
293297
}
294298
}

src/Plugin.php

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Rollbar\Rollbar;
77
use Rollbar\RollbarJsHelper;
88
use Rollbar\WordPress\Admin\FlashMessages;
9+
use Rollbar\WordPress\Admin\SettingsPage;
910
use Rollbar\WordPress\API\AdminAPI;
1011
use Rollbar\WordPress\Lib\AbstractSingleton;
1112
use Rollbar\WordPress\Settings\SettingType;
@@ -68,14 +69,24 @@ protected function __construct()
6869
*/
6970
protected function postInit(): void
7071
{
71-
// Set up admin views and API.
72-
AdminAPI::getInstance();
7372
// Set up the telemetry listener.
7473
if ($this->getSetting('enable_telemetry_listener')) {
7574
$this->listener = Listener::getInstance();
7675
}
7776
}
7877

78+
/**
79+
* Handles the 'init' action hook.
80+
*
81+
* @return void
82+
*/
83+
public function onInit(): void
84+
{
85+
// Set up admin views and API.
86+
SettingsPage::getInstance();
87+
AdminAPI::getInstance();
88+
}
89+
7990
/**
8091
* Updates the plugin configuration
8192
*
@@ -115,21 +126,21 @@ public static function disabledAdmin(): bool
115126
* @return bool
116127
* @since 3.0.0
117128
*/
118-
public static function hideAdmin(): bool
129+
public static function userCanViewAdmin(): bool
119130
{
120131
if (self::disabledAdmin()) {
121132
return false;
122133
}
123134

124135
/**
125-
* Filter to disable the admin settings page of the plugin.
136+
* Filter to enable / disable the admin settings page of the plugin for the current user.
126137
*
127138
* This filter cannot override the `ROLLBAR_DISABLE_ADMIN` constant.
128139
*
129-
* @param bool $disable `true` to disable the admin settings page, `false` to enable it.
140+
* @param bool $allow `true` to enable the admin settings page, `false` to disable it.
130141
* @since 3.0.0
131142
*/
132-
return apply_filters('rollbar_disable_admin', !current_user_can('manage_options')) === true;
143+
return apply_filters('rollbar_user_can_view_admin', current_user_can('manage_options')) === true;
133144
}
134145

135146
/**
@@ -192,6 +203,7 @@ public function settingsInstance(): Settings
192203
*/
193204
private function hooks(): void
194205
{
206+
add_action('init', $this->onInit(...));
195207
add_action('wp_head', $this->initJsLogging(...));
196208
add_action('admin_head', $this->initJsLogging(...));
197209
}
@@ -242,12 +254,14 @@ public static function buildIncludedErrno(int $cutoff): int
242254
* Sets up the Rollbar PHP error handler if PHP logging is enabled.
243255
* Handles configuration errors by displaying appropriate error messages.
244256
*
257+
* @param bool $ignoreEnabledSetting If true, the plugin will not check the 'php_logging_enabled' setting first.
258+
*
245259
* @return void
246260
*/
247-
public function initPhpLogging(): void
261+
public function initPhpLogging(bool $ignoreEnabledSetting = false): void
248262
{
249263
// Return if logging is not enabled
250-
if (0 === $this->getSetting('php_logging_enabled')) {
264+
if (!$ignoreEnabledSetting && false === $this->getSetting('php_logging_enabled')) {
251265
return;
252266
}
253267

@@ -321,15 +335,15 @@ public function buildPHPConfig(): array
321335
public function initJsLogging(): void
322336
{
323337
// Return if logging is not enabled
324-
if ($this->getSetting('js_logging_enabled') === 0) {
338+
if (false === $this->getSetting('js_logging_enabled')) {
325339
return;
326340
}
327341

328342
// Return if access token is not set
329-
if ($this->getSetting('client_side_access_token') == '') {
343+
if (empty($this->getSetting('client_side_access_token'))) {
330344
FlashMessages::addMessage(
331345
message: 'Rollbar is misconfigured. Please, fix your configuration here: <a href="'
332-
. admin_url('/options-general.php?page=rollbar_wp') . '">',
346+
. admin_url('/options-general.php?page=rollbar_wp') . '">Rollbar Settings</a>.',
333347
type: 'error',
334348
);
335349
return;
@@ -352,7 +366,7 @@ public function buildJsConfig(): array
352366
{
353367
$config = [
354368
'accessToken' => $this->getSetting('client_side_access_token'),
355-
'captureUncaught' => true,
369+
'captureUncaught' => true,
356370
'payload' => [
357371
'environment' => $this->getSetting('environment'),
358372
],

src/Settings.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ public static function preUpdate(array $settings): array
438438

439439
// Don't store default values in the database, so future updates to default values in the SDK get propagated.
440440
foreach ($settings as $setting_name => $setting_value) {
441+
// Loose comparison to allow for boolean values to be set to 0 or 1 and integers to be strings, which is how
442+
// they are posted via HTTP.
441443
if ($setting_value == Plugin::getInstance()->settingsInstance()->getDefaultOption($setting_name)) {
442444
unset($settings[$setting_name]);
443445
}
@@ -579,13 +581,16 @@ private function fetchSettings(): void
579581
esc_attr(trim($options['server_side_access_token'])) :
580582
'',
581583
'client_side_access_token' => (!empty($options['client_side_access_token'])) ?
582-
trim($options['client_side_access_token']) :
584+
esc_attr(trim($options['client_side_access_token'])) :
583585
'',
584586
'included_errno' => (!empty($options['included_errno'])) ?
585587
esc_attr(trim($options['included_errno'])) :
586588
self::getDefaultOption('included_errno'),
587589
];
588590

591+
// Filter out options that are not in the list of options.
592+
$options = array_intersect_key($options, array_flip(self::listOptions()));
593+
589594
foreach (self::listOptions() as $option) {
590595
// 'access_token' and 'enabled' are different in WordPress plugin
591596
// look for 'server_side_access_token' and 'php_logging_enabled' above

0 commit comments

Comments
 (0)