├── assets └── screenshot-1.png ├── mu-plugin └── rollbar-mu-plugin.php ├── templates ├── html │ ├── flashMessages.php │ ├── reset.php │ └── input │ │ ├── booleanInput.php │ │ ├── numberInput.php │ │ ├── textInput.php │ │ ├── checkBoxInput.php │ │ └── selectInput.php └── admin │ ├── settings-section-header.php │ ├── settings.php │ └── settings-status.php ├── public ├── admin │ ├── rollbar.css │ └── rollbar.js └── js │ └── rollbar.snippet.js ├── src ├── bootstrap.php ├── Settings │ └── SettingType.php ├── Html │ ├── Input │ │ ├── TextInput.php │ │ ├── NumberInput.php │ │ ├── BooleanInput.php │ │ ├── SelectInput.php │ │ ├── InputInterface.php │ │ ├── CheckBoxInput.php │ │ └── AbstractInput.php │ └── Template.php ├── Lib │ └── AbstractSingleton.php ├── Admin │ ├── FlashMessages.php │ └── SettingsPage.php ├── Setting.php ├── API │ └── AdminAPI.php ├── Plugin.php ├── Telemetry │ └── Listener.php └── Settings.php ├── rollbar.php ├── composer.json ├── README.md ├── LICENSE └── readme.txt /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rollbar/rollbar-php-wordpress/HEAD/assets/screenshot-1.png -------------------------------------------------------------------------------- /mu-plugin/rollbar-mu-plugin.php: -------------------------------------------------------------------------------- 1 | 12 |
13 |

14 |
15 | -------------------------------------------------------------------------------- /public/admin/rollbar.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Rollbar for WordPress admin CSS. 3 | */ 4 | 5 | .rollbar-settings-section-header .section-heading { 6 | cursor: pointer; 7 | } 8 | 9 | .rollbar-settings-section-header .dashicons { 10 | transform: rotate(270deg); 11 | color: #2271b1; 12 | } 13 | 14 | .rollbar-settings-section-header.open .dashicons { 15 | transform: rotate(90deg); 16 | } 17 | 18 | .rollbar-settings-section-header + table.form-table { 19 | display: none; 20 | } 21 | 22 | .rollbar-settings-section-header.open + table.form-table { 23 | display: table; 24 | } -------------------------------------------------------------------------------- /src/bootstrap.php: -------------------------------------------------------------------------------- 1 | 14 |
15 |
16 |

17 | 18 | 19 |

20 | 21 |
22 | 23 |
24 | 25 |
26 | -------------------------------------------------------------------------------- /templates/html/reset.php: -------------------------------------------------------------------------------- 1 | getDefault(); 15 | if ($input::class === SettingType::Boolean->value) { 16 | $default = $default ? 'true' : 'false'; 17 | } 18 | if (is_array($default)) { 19 | $default = implode(',', $default); 20 | } 21 | ?> 22 | 31 | -------------------------------------------------------------------------------- /src/Settings/SettingType.php: -------------------------------------------------------------------------------- 1 | $this]); 29 | } 30 | 31 | /** 32 | * Returns the type of the input. 33 | * 34 | * @return string 35 | */ 36 | public function getType(): string 37 | { 38 | return 'text'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Html/Input/NumberInput.php: -------------------------------------------------------------------------------- 1 | $this]); 29 | } 30 | 31 | /** 32 | * Returns the type of the input. 33 | * 34 | * @return string 35 | */ 36 | public function getType(): string 37 | { 38 | return 'number'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rollbar.php: -------------------------------------------------------------------------------- 1 | 15 | getValue(), true, false) ?> 22 | isDisabled()) ?> 23 | getHelpText() ? 'aria-describedby="' . esc_attr($input->getId()) . '-help"' : '' ?> 24 | /> 25 | showReset() ? Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/reset.php', ['input' => $input], 26 | ) : '' ?> 27 | getLabel())) : ?> 28 | 29 | 30 | getHelpText())) : ?> 31 |

getHelpText() ?>

32 | 33 | -------------------------------------------------------------------------------- /templates/html/input/numberInput.php: -------------------------------------------------------------------------------- 1 | 14 | isDisabled()) ?> 21 | getHelpText() ? 'aria-describedby="' . esc_attr($input->getId()) . '-help"' : '' ?> 22 | serializeAttributes() ?> 23 | /> 24 | showReset() ? Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/reset.php', ['input' => $input]) : '' ?> 25 | getLabel())) : ?> 26 | 27 | 28 | getHelpText())) : ?> 29 |

getHelpText() ?>

30 | 31 | -------------------------------------------------------------------------------- /templates/html/input/textInput.php: -------------------------------------------------------------------------------- 1 | 14 | isDisabled()) ?> 21 | getHelpText() ? 'aria-describedby="' . esc_attr($input->getId()) . '-help"' : '' ?> 22 | serializeAttributes() ?> 23 | class="regular-text" 24 | /> 25 | showReset() ? Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/reset.php', ['input' => $input]) : '' ?> 26 | getLabel())) : ?> 27 | 28 | 29 | getHelpText())) : ?> 30 |

getHelpText() ?>

31 | 32 | -------------------------------------------------------------------------------- /src/Html/Input/BooleanInput.php: -------------------------------------------------------------------------------- 1 | $this]); 29 | } 30 | 31 | /** 32 | * Returns the value of the input. 33 | * 34 | * @return bool 35 | */ 36 | public function getValue(): bool 37 | { 38 | return boolval(parent::getValue()); 39 | } 40 | 41 | /** 42 | * Returns the type of the input. 43 | * 44 | * @return string 45 | */ 46 | public function getType(): string 47 | { 48 | return 'boolean'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /templates/html/input/checkBoxInput.php: -------------------------------------------------------------------------------- 1 | getOptions() as $value => $label): ?> 15 |
16 | getValue()), display: false) ?> 23 | isDisabled()) ?> 24 | isDisabled()) ?> 25 | /> 26 | 27 | 28 | 29 |
30 | 31 | showReset() ? Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/reset.php', ['input' => $input]) : '' ?> 32 | getHelpText())) : ?> 33 |

getHelpText() ?>

34 | 35 | -------------------------------------------------------------------------------- /templates/admin/settings.php: -------------------------------------------------------------------------------- 1 | 9 |
10 |

Rollbar Settings

11 |
12 | 13 | 18 |
19 |
20 |
21 | 22 | 23 | 30 |
31 |
32 | 39 |
40 | -------------------------------------------------------------------------------- /templates/html/input/selectInput.php: -------------------------------------------------------------------------------- 1 | 14 | 29 | showReset() ? Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/reset.php', ['input' => $input]) : '' ?> 30 | getLabel())) : ?> 31 | 32 | 33 | getHelpText())) : ?> 34 |

getHelpText() ?>

35 | 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollbar/rollbar-php-wordpress", 3 | "description": "WordPress plugin for Rollbar", 4 | "type": "wordpress-plugin", 5 | "require": { 6 | "php": "^8.1", 7 | "rollbar/rollbar": "^4.1.3" 8 | }, 9 | "require-dev": { 10 | "yoast/phpunit-polyfills": "^4.0", 11 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7", 12 | "squizlabs/php_codesniffer": "^3.5", 13 | "phpcompatibility/php-compatibility": "^9.3", 14 | "wp-coding-standards/wpcs": "^2.2", 15 | "sirbrillig/phpcs-variable-analysis": "^2.8" 16 | }, 17 | "license": "GPL-2.0-or-later", 18 | "authors": [ 19 | { 20 | "name": "Rollbar, Inc.", 21 | "email": "support@rollbar.com", 22 | "role": "Developer" 23 | } 24 | ], 25 | "support": { 26 | "email": "support@rollbar.com" 27 | }, 28 | "autoload": { 29 | "files": [ 30 | "src/bootstrap.php" 31 | ], 32 | "psr-4": { 33 | "Rollbar\\WordPress\\": "src/" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Rollbar\\WordPress\\Tests\\": "tests/" 39 | } 40 | }, 41 | "extra": { 42 | "scripts-description": { 43 | "test": "Run all tests. Wrapper around npm run test" 44 | }, 45 | "installer-name": "rollbar" 46 | }, 47 | "scripts": { 48 | "test": [ 49 | "@pre-test", 50 | "npm run test" 51 | ], 52 | "pre-test": [ 53 | "npm install" 54 | ] 55 | }, 56 | "config": { 57 | "allow-plugins": { 58 | "dealerdirect/phpcodesniffer-composer-installer": true 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Lib/AbstractSingleton.php: -------------------------------------------------------------------------------- 1 | $instances 25 | */ 26 | private static array $instances = []; 27 | 28 | /** 29 | * Returns the singleton instance of the class. 30 | * 31 | * @return static 32 | */ 33 | public static function getInstance(): static 34 | { 35 | if (!array_key_exists(static::class, self::$instances)) { 36 | self::$instances[static::class] = new static(); 37 | self::$instances[static::class]->postInit(); 38 | } 39 | 40 | return self::$instances[static::class]; 41 | } 42 | 43 | /** 44 | * Method called after the class is constructed. 45 | * 46 | * This can be useful for setting up other singleton classes or performing actions that don't fit in the 47 | * constructor. 48 | * 49 | * @return void 50 | */ 51 | protected function postInit(): void 52 | { 53 | } 54 | 55 | /** 56 | * Singletons should not be cloneable. 57 | * 58 | * @return void 59 | */ 60 | public function __clone(): void 61 | { 62 | } 63 | 64 | /** 65 | * Singletons should not be restored from strings. 66 | * 67 | * @return void 68 | * @throws Exception If attempting to unserialize a singleton. 69 | */ 70 | public function __wakeup(): void 71 | { 72 | throw new Exception('Cannot unserialize a singleton.'); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Html/Template.php: -------------------------------------------------------------------------------- 1 | $type, 37 | "message" => $message, 38 | ]; 39 | 40 | update_option(self::getKey(), $messages); 41 | } 42 | 43 | /** 44 | * Renders and flushes the messages for the current user. 45 | * 46 | * @return string 47 | */ 48 | public static function flushMessages(): string 49 | { 50 | if (is_admin() && session_id() && !wp_doing_cron()) { 51 | return ''; 52 | } 53 | $messages = get_option(self::getKey(), []); 54 | update_option(self::getKey(), []); 55 | 56 | if (empty($messages)) { 57 | return ''; 58 | } 59 | return Template::string( 60 | path: ROLLBAR_PLUGIN_DIR . '/templates/html/flashMessages.php', 61 | data: [ 62 | 'messages' => $messages, 63 | ], 64 | ); 65 | } 66 | 67 | /** 68 | * Returns the option key to use to store messages. 69 | * 70 | * @return string 71 | */ 72 | private static function getKey(): string 73 | { 74 | return self::KEY_PREFIX . get_current_user_id(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /templates/admin/settings-status.php: -------------------------------------------------------------------------------- 1 | 22 | 41 |
42 | 49 | 60 |

61 | You can find your access tokens under your project settings: Project Access Tokens. 62 |

-------------------------------------------------------------------------------- /src/Html/Input/SelectInput.php: -------------------------------------------------------------------------------- 1 | $attributes Additional attributes for the input. 33 | * @param array $options The value, label pairs of possible options. 34 | */ 35 | public function __construct( 36 | protected string $id, 37 | protected string|array $name, 38 | protected $value = null, 39 | protected $default = null, 40 | protected string $label = '', 41 | protected string $helpText = '', 42 | protected bool $disabled = false, 43 | protected bool $showReset = true, 44 | protected array $attributes = [], 45 | protected array $options = [], 46 | ) { 47 | parent::__construct($id, $name, $value, $default, $label, $helpText, $disabled, $showReset, $attributes); 48 | } 49 | 50 | /** 51 | * Renders the input as a string. 52 | * 53 | * @return string 54 | */ 55 | public function __toString(): string 56 | { 57 | return Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/input/selectInput.php', ['input' => $this]); 58 | } 59 | 60 | /** 61 | * Returns the value, label pairs of possible options. 62 | * 63 | * @return array 64 | */ 65 | public function getOptions(): array 66 | { 67 | return $this->options; 68 | } 69 | 70 | /** 71 | * Returns the type of the input. 72 | * 73 | * @return string 74 | */ 75 | public function getType(): string 76 | { 77 | return 'select'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Html/Input/InputInterface.php: -------------------------------------------------------------------------------- 1 | 81 | */ 82 | public function getAttributes(): array; 83 | 84 | /** 85 | * Returns true if the input is disabled, false otherwise. 86 | * 87 | * @return bool 88 | */ 89 | public function isDisabled(): bool; 90 | 91 | /** 92 | * Returns the serialized HTML attributes of the input. 93 | * 94 | * @return string 95 | */ 96 | public function serializeAttributes(): string; 97 | 98 | /** 99 | * Returns true if the reset button should be displayed, false otherwise. 100 | * 101 | * @return bool 102 | */ 103 | public function showReset(): bool; 104 | } 105 | -------------------------------------------------------------------------------- /src/Html/Input/CheckBoxInput.php: -------------------------------------------------------------------------------- 1 | $attributes Additional attributes for the input. 33 | * @param array $options The value, label pairs of possible options. 34 | * @param bool $sort True to sort the options by key. 35 | */ 36 | public function __construct( 37 | protected string $id, 38 | protected string|array $name, 39 | protected $value = null, 40 | protected $default = null, 41 | protected string $label = '', 42 | protected string $helpText = '', 43 | protected bool $disabled = false, 44 | protected bool $showReset = true, 45 | protected array $attributes = [], 46 | protected array $options = [], 47 | protected bool $sort = false, 48 | ) { 49 | parent::__construct($id, $name, $value, $default, $label, $helpText, $disabled, $showReset, $attributes); 50 | if ($this->sort) { 51 | ksort($this->options); 52 | } 53 | } 54 | 55 | /** 56 | * Renders the input as a string. 57 | * 58 | * @return string 59 | */ 60 | public function __toString(): string 61 | { 62 | return Template::string(ROLLBAR_PLUGIN_DIR . '/templates/html/input/checkBoxInput.php', ['input' => $this]); 63 | } 64 | 65 | /** 66 | * Returns the value, label pairs of possible options. 67 | * 68 | * @return array 69 | */ 70 | public function getOptions(): array 71 | { 72 | return $this->options; 73 | } 74 | 75 | /** 76 | * Returns the selected value(s) of the input. 77 | * 78 | * @return string[] 79 | */ 80 | public function getValue(): array 81 | { 82 | if (is_array($this->value)) { 83 | return $this->value; 84 | } 85 | return $this->value ? [$this->value] : []; 86 | } 87 | 88 | /** 89 | * Returns the type of the input. 90 | * 91 | * @return string 92 | */ 93 | public function getType(): string 94 | { 95 | return 'checkbox'; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Setting.php: -------------------------------------------------------------------------------- 1 | input = $this->createInput(); 51 | } 52 | 53 | /** 54 | * Returns the formatted label. 55 | * 56 | * @return string 57 | */ 58 | public function getTitle(): string 59 | { 60 | if (!empty($this->label)) { 61 | return $this->label; 62 | } 63 | return ucwords(str_replace('_', ' ', $this->id)); 64 | } 65 | 66 | /** 67 | * Returns the label HTML element for the setting input. 68 | * 69 | * @return string 70 | */ 71 | public function getLabelElement(): string 72 | { 73 | return ''; 74 | } 75 | 76 | /** 77 | * Outputs the HTML input element. 78 | * 79 | * @param array{value: mixed} $args The arguments passed to {@see add_settings_field()}. 80 | * @return void 81 | */ 82 | public function render(array $args): void 83 | { 84 | $this->input->setValue($args['value'] ?? Plugin::getInstance()->getSetting($this->id)); 85 | echo $this->input; 86 | } 87 | 88 | /** 89 | * Coerces the value of the setting to the appropriate type. 90 | * 91 | * @param mixed $value The value to be coerced. 92 | * @return mixed The coerced value. 93 | */ 94 | public function coerceValue(mixed $value): mixed 95 | { 96 | return match ($this->type) { 97 | SettingType::Boolean => Settings::toBoolean($value), 98 | SettingType::CheckBox => Settings::toStringArray($value), 99 | SettingType::Integer => Settings::toInteger($value), 100 | SettingType::Select, SettingType::Text => Settings::toString($value), 101 | SettingType::Skip => null, 102 | }; 103 | } 104 | 105 | /** 106 | * Creates and returns the input object otherwise null if it should be skipped. 107 | * 108 | * @return InputInterface|null 109 | */ 110 | private function createInput(): null|InputInterface 111 | { 112 | if ($this->type === SettingType::Skip) { 113 | return null; 114 | } 115 | $args = [ 116 | 'id' => 'rollbar_wp_' . $this->id, 117 | 'name' => ['rollbar_wp', $this->id], 118 | 'default' => $this->default, 119 | 'label' => '', 120 | 'helpText' => $this->helpText, 121 | ]; 122 | foreach ($this->inputArgs as $key => $value) { 123 | if (!property_exists($this->type->value, $key) || array_key_exists($key, $args)) { 124 | continue; 125 | } 126 | $args[$key] = $value; 127 | } 128 | if (in_array($this->type, [SettingType::Select, SettingType::CheckBox])) { 129 | $args['options'] = $this->options; 130 | } 131 | return new $this->type->value(...$args); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Html/Input/AbstractInput.php: -------------------------------------------------------------------------------- 1 | $attributes Additional attributes for the input. 29 | */ 30 | public function __construct( 31 | protected string $id, 32 | protected string|array $name, 33 | protected $value = null, 34 | protected $default = null, 35 | protected string $label = '', 36 | protected string $helpText = '', 37 | protected bool $disabled = false, 38 | protected bool $showReset = true, 39 | protected array $attributes = [], 40 | ) { 41 | } 42 | 43 | /** 44 | * Returns the input ID. 45 | * 46 | * @return string 47 | */ 48 | public function getId(): string 49 | { 50 | return $this->id; 51 | } 52 | 53 | /** 54 | * Returns the value of the HTML name attribute. 55 | * 56 | * @return string 57 | */ 58 | public function getName(): string 59 | { 60 | if (!is_array($this->name)) { 61 | return $this->name; 62 | } 63 | $name = $this->name[0]; 64 | if (1 < count($this->name)) { 65 | $name .= '[' . implode('][', array_slice($this->name, 1)) . ']'; 66 | } 67 | return $name; 68 | } 69 | 70 | /** 71 | * Returns the value of the input. 72 | * 73 | * @return mixed 74 | */ 75 | public function getValue() 76 | { 77 | return $this->value; 78 | } 79 | 80 | /** 81 | * Sets the value of the input. 82 | * 83 | * @param mixed $value 84 | */ 85 | public function setValue($value): void 86 | { 87 | $this->value = $value; 88 | } 89 | 90 | /** 91 | * Returns the default value of the input. 92 | * 93 | * @return mixed 94 | */ 95 | public function getDefault() 96 | { 97 | return $this->default; 98 | } 99 | 100 | /** 101 | * Returns the label of the input. 102 | * 103 | * @return string 104 | */ 105 | public function getLabel(): string 106 | { 107 | return $this->label; 108 | } 109 | 110 | /** 111 | * Returns the help text to be displayed with the input. 112 | * 113 | * @return string 114 | */ 115 | public function getHelpText(): string 116 | { 117 | return $this->helpText; 118 | } 119 | 120 | /** 121 | * Returns true if the input is disabled, false otherwise. 122 | * 123 | * @return bool 124 | */ 125 | public function isDisabled(): bool 126 | { 127 | return $this->disabled; 128 | } 129 | 130 | /** 131 | * Returns a list of HTML attributes to be applied to the input. 132 | * 133 | * @return array 134 | */ 135 | public function getAttributes(): array 136 | { 137 | return $this->attributes; 138 | } 139 | 140 | /** 141 | * Returns the serialized HTML attributes of the input. 142 | * 143 | * @return string 144 | */ 145 | public function serializeAttributes(): string 146 | { 147 | if (empty($this->attributes)) { 148 | return ''; 149 | } 150 | $result = ''; 151 | foreach ($this->attributes as $key => $value) { 152 | if (is_bool($value) || is_null($value)) { 153 | $result .= ' ' . $key; 154 | continue; 155 | } 156 | $result .= ' ' . $key . '="' . esc_attr($value) . '"'; 157 | } 158 | return trim($result); 159 | } 160 | 161 | /** 162 | * Returns true if the reset button should be displayed, false otherwise. 163 | * 164 | * @return bool 165 | */ 166 | public function showReset(): bool 167 | { 168 | return $this->showReset; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/API/AdminAPI.php: -------------------------------------------------------------------------------- 1 | registerRoutes(...)); 36 | } 37 | 38 | /** 39 | * Registers the REST API routes for the Rollbar WordPress plugin. 40 | * 41 | * @return void 42 | */ 43 | protected function registerRoutes(): void 44 | { 45 | register_rest_route( 46 | route_namespace: self::API_NAMESPACE, 47 | route: '/test-php-logging', 48 | args: [ 49 | 'methods' => WP_REST_Server::CREATABLE, 50 | 'callback' => $this->handleTestPhpLogging(...), 51 | 'permission_callback' => function (WP_REST_Request $request): bool { 52 | /** 53 | * Filter to allow or deny access to a Rollbar route in the WordPress REST API used in the WordPress 54 | * Admin. 55 | * 56 | * @param bool $value The initial value. Defaults is `true` for admin users, `false` for non-admin 57 | * users. 58 | * @param string $route The route being accessed. 59 | * @param WP_REST_Request $request The REST request object. 60 | * @since 3.0.0 61 | * 62 | */ 63 | return apply_filters( 64 | hook_name: 'rollbar_api_admin_permission', 65 | value: is_user_logged_in() && current_user_can('manage_options'), 66 | route: 'test-php-logging', 67 | request: $request, 68 | ); 69 | }, 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 | ], 84 | ], 85 | ); 86 | } 87 | 88 | /** 89 | * Handles a test request for PHP logging functionality within the plugin. 90 | * 91 | * Endpoint: rollbar/v1/test-php-logging 92 | * 93 | * @param WP_REST_Request $request The REST request object containing the request data. 94 | * @return WP_REST_Response The REST response object containing the result of the logging test. 95 | */ 96 | public function handleTestPhpLogging(WP_REST_Request $request): WP_REST_Response 97 | { 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 | 107 | $plugin = Plugin::getInstance(); 108 | foreach ($settings as $key => $value) { 109 | $plugin->setSetting($key, $value); 110 | } 111 | 112 | try { 113 | $plugin->initPhpLogging(ignoreEnabledSetting: true); 114 | $rollbarResponse = Rollbar::report( 115 | Level::INFO, 116 | 'Test message from Rollbar WordPress plugin using PHP: ' . 117 | 'integration with WordPress successful', 118 | ); 119 | } catch (Throwable $exception) { 120 | return new WP_REST_Response( 121 | [ 122 | 'code' => 500, 123 | 'success' => false, 124 | // Send the exception message to the client to the admin user so they can debug the issue. 125 | 'message' => $exception->getMessage(), 126 | ], 127 | 500, 128 | ); 129 | } 130 | 131 | $response = [ 132 | 'code' => $rollbarResponse->getStatus(), 133 | 'success' => $rollbarResponse->getStatus() === 200, 134 | 'message' => '', 135 | ]; 136 | 137 | $info = $rollbarResponse->getInfo(); 138 | if (is_string($info)) { 139 | $response['message'] = $info; 140 | } 141 | 142 | return new WP_REST_Response($response, 200); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /public/js/rollbar.snippet.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var r={349:r=>{r.exports={captureUncaughtExceptions:function(r,o,e){if(r){var n;if("function"==typeof o._rollbarOldOnError)n=o._rollbarOldOnError;else if(r.onerror){for(n=r.onerror;n._rollbarOldOnError;)n=n._rollbarOldOnError;o._rollbarOldOnError=n}o.handleAnonymousErrors();var t=function(){var e=Array.prototype.slice.call(arguments,0);!function(r,o,e,n){r._rollbarWrappedError&&(n[4]||(n[4]=r._rollbarWrappedError),n[5]||(n[5]=r._rollbarWrappedError._rollbarContext),r._rollbarWrappedError=null);var t=o.handleUncaughtException.apply(o,n);e&&e.apply(r,n),"anonymous"===t&&(o.anonymousErrorsPending+=1)}(r,o,n,e)};e&&(t._rollbarOldOnError=n),r.onerror=t}},captureUnhandledRejections:function(r,o,e){if(r){"function"==typeof r._rollbarURH&&r._rollbarURH.belongsToShim&&r.removeEventListener("unhandledrejection",r._rollbarURH);var n=function(r){var e,n,t;try{e=r.reason}catch(r){e=void 0}try{n=r.promise}catch(r){n="[unhandledrejection] error getting `promise` from event"}try{t=r.detail,!e&&t&&(e=t.reason,n=t.promise)}catch(r){}e||(e="[unhandledrejection] error getting `reason` from event"),o&&o.handleUnhandledRejection&&o.handleUnhandledRejection(e,n)};n.belongsToShim=e,r._rollbarURH=n,r.addEventListener("unhandledrejection",n)}}}},202:r=>{function o(r,e){this.impl=r(e,this),this.options=e,function(r){for(var o=function(r){return function(){var o=Array.prototype.slice.call(arguments,0);if(this.impl[r])return this.impl[r].apply(this.impl,o)}},e="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleAnonymousErrors,handleUnhandledRejection,_createItem,wrap,loadFull,shimId,captureEvent,captureDomContentLoaded,captureLoad".split(","),n=0;n{var n=e(349),t=e(965);function a(r){return function(){try{return r.apply(this,arguments)}catch(r){try{console.error("[Rollbar]: Internal error",r)}catch(r){}}}}var l=0;function i(r,o){this.options=r,this._rollbarOldOnError=null;var e=l++;this.shimId=function(){return e},"undefined"!=typeof window&&window._rollbarShims&&(window._rollbarShims[e]={handler:o,messages:[]})}var s=e(202),d=function(r,o){return new i(r,o)},p=function(r){return new s(d,r)};function c(r){return a((function(){var o=Array.prototype.slice.call(arguments,0),e={shim:this,method:r,args:o,ts:new Date};window._rollbarShims[this.shimId()].messages.push(e)}))}i.prototype.loadFull=function(r,o,e,n,t){var l=!1,i=o.createElement("script"),s=o.getElementsByTagName("script")[0],d=s.parentNode;i.crossOrigin="",i.src=n.rollbarJsUrl,e||(i.async=!0),i.onload=i.onreadystatechange=a((function(){if(!(l||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){i.onload=i.onreadystatechange=null;try{d.removeChild(i)}catch(r){}l=!0,function(){var o;if(void 0===r._rollbarDidLoad){o=new Error("rollbar.js did not load");for(var e,n,a,l,i=0;e=r._rollbarShims[i++];)for(e=e.messages||[];n=e.shift();)for(a=n.args||[],i=0;i{r.exports=function(r){return function(o){if(!o&&!window._rollbarInitialized){for(var e,n,t=(r=r||{}).globalAlias||"Rollbar",a=window.rollbar,l=function(r){return new a(r)},i=0;e=window._rollbarShims[i++];)n||(n=e.handler),e.handler._swapAndProcessMessages(l,e.messages);window[t]=n,window._rollbarInitialized=!0}}}},965:r=>{function o(r,o,e){if(o.hasOwnProperty&&o.hasOwnProperty("addEventListener")){for(var n=o.addEventListener;n._rollbarOldAdd&&n.belongsToShim;)n=n._rollbarOldAdd;var t=function(o,e,t){n.call(this,o,r.wrap(e),t)};t._rollbarOldAdd=n,t.belongsToShim=e,o.addEventListener=t;for(var a=o.removeEventListener;a._rollbarOldRemove&&a.belongsToShim;)a=a._rollbarOldRemove;var l=function(r,o,e){a.call(this,r,o&&o._rollbar_wrapped||o,e)};l._rollbarOldRemove=a,l.belongsToShim=e,o.removeEventListener=l}}r.exports=function(r,e,n){if(r){var t,a,l="EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split(",");for(t=0;t{var r=e(758),o=e(157);_rollbarConfig=_rollbarConfig||{},_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||"https://cdn.rollbar.com/rollbarjs/refs/tags/v2.26.4/rollbar.min.js",_rollbarConfig.async=void 0===_rollbarConfig.async||_rollbarConfig.async;var n=r.setupShim(window,_rollbarConfig),t=o(_rollbarConfig);window.rollbar=r.Rollbar,n.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,t)})()})(); -------------------------------------------------------------------------------- /src/Admin/SettingsPage.php: -------------------------------------------------------------------------------- 1 | hooks(); 46 | } 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | protected function postInit(): void 52 | { 53 | $this->plugin = Plugin::getInstance(); 54 | } 55 | 56 | /** 57 | * Register the event handlers for the settings page. 58 | * 59 | * @return void 60 | */ 61 | private function hooks(): void 62 | { 63 | add_action('admin_menu', $this->addAdminMenu(...)); 64 | add_filter('plugin_action_links_' . plugin_basename(ROLLBAR_PLUGIN_FILE), $this->addAdminMenuLink(...)); 65 | add_action('admin_init', $this->addSettings(...)); 66 | add_action('admin_enqueue_scripts', $this->enqueueAdminScripts(...)); 67 | add_action('admin_post_rollbar_wp_restore_defaults', $this->restoreDefaultsAction(...)); 68 | } 69 | 70 | /** 71 | * Adds the settings page to the admin menu. 72 | * 73 | * @return void 74 | * @hook admin_menu 75 | */ 76 | public function addAdminMenu(): void 77 | { 78 | add_submenu_page( 79 | parent_slug: 'options-general.php', 80 | page_title: 'Rollbar', 81 | menu_title: 'Rollbar', 82 | capability: 'manage_options', 83 | menu_slug: 'rollbar_wp', 84 | callback: $this->optionsPage(...), 85 | ); 86 | } 87 | 88 | /** 89 | * Adds the settings link to the plugins list view. 90 | * 91 | * @param string[] $links 92 | * @return array 93 | * @hook plugin_action_links_{plugin} 94 | */ 95 | public function addAdminMenuLink(array $links): array 96 | { 97 | $url = admin_url('options-general.php?' . http_build_query(['page' => 'rollbar_wp'])); 98 | 99 | $links[] = 'Settings'; 100 | 101 | return $links; 102 | } 103 | 104 | /** 105 | * Registers the plugin settings to be displayed on the settings page. 106 | * 107 | * @return void 108 | */ 109 | public function addSettings(): void 110 | { 111 | $advancedOptions = Settings::settings(); 112 | 113 | register_setting( 114 | option_group: 'rollbar_wp', 115 | option_name: 'rollbar_wp', 116 | ); 117 | 118 | // SECTION: General 119 | add_settings_section( 120 | id: 'rollbar_wp_general', 121 | title: false, 122 | callback: false, 123 | page: 'rollbar_wp', 124 | ); 125 | 126 | // On/Off and Tokens 127 | add_settings_field( 128 | id: 'rollbar_wp_status', 129 | title: 'Status', 130 | callback: function () { 131 | Template::print( 132 | ROLLBAR_PLUGIN_DIR . '/templates/admin/settings-status.php', 133 | [ 134 | 'php_logging_enabled' => (!empty($this->plugin->getSetting('php_logging_enabled'))) ? 1 : 0, 135 | 'server_side_access_token' => $this->plugin->getSetting('server_side_access_token'), 136 | 'js_logging_enabled' => (!empty($this->plugin->getSetting('js_logging_enabled'))) ? 1 : 0, 137 | 'client_side_access_token' => $this->plugin->getSetting('client_side_access_token'), 138 | ], 139 | ); 140 | }, 141 | page: 'rollbar_wp', 142 | section: 'rollbar_wp_general', 143 | ); 144 | 145 | $this->addSettingsField($advancedOptions['environment']); 146 | $this->addSettingsField($advancedOptions['included_errno']); 147 | 148 | // SECTION: Advanced 149 | add_settings_section( 150 | id: 'rollbar_wp_advanced', 151 | title: false, // The title is created by the callback. 152 | callback: $this->advancedSectionHeader(...), 153 | page: 'rollbar_wp', 154 | ); 155 | 156 | foreach ($advancedOptions as $option) { 157 | if (null === $option || $option->section !== 'rollbar_wp_advanced') { 158 | continue; 159 | } 160 | 161 | $this->addSettingsField($option); 162 | } 163 | 164 | // SECTION: Advanced 165 | add_settings_section( 166 | id: 'rollbar_wp_telemetry', 167 | title: false, // The title is created by the callback. 168 | callback: $this->telemetrySectionHeader(...), 169 | page: 'rollbar_wp', 170 | ); 171 | 172 | foreach ($advancedOptions as $option) { 173 | if (null === $option || $option->section !== 'rollbar_wp_telemetry') { 174 | continue; 175 | } 176 | 177 | $this->addSettingsField($option); 178 | } 179 | } 180 | 181 | /** 182 | * Registers the static files for the admin page. 183 | * 184 | * @param string $hook 185 | * @return void 186 | */ 187 | public function enqueueAdminScripts(string $hook): void 188 | { 189 | if ($hook != 'settings_page_rollbar_wp') { 190 | return; 191 | } 192 | 193 | wp_enqueue_style( 194 | 'rollbar-admin-css', 195 | plugin_dir_url(ROLLBAR_PLUGIN_FILE) . '/public/admin/rollbar.css', 196 | false, 197 | Plugin::VERSION, 198 | ); 199 | 200 | wp_enqueue_script( 201 | 'rollbar-admin-js', 202 | Plugin::getAssetUrl('/public/admin/rollbar.js'), 203 | [], 204 | Plugin::VERSION, 205 | ); 206 | 207 | wp_localize_script( 208 | handle: 'rollbar-admin-js', 209 | object_name: 'rollbarSettings', 210 | l10n: [ 211 | 'nonce' => wp_create_nonce('rollbar_wp_api_test_logging'), 212 | 'rest_nonce' => wp_create_nonce('wp_rest'), 213 | 'rest_root' => esc_url_raw(rest_url()), 214 | 'plugin_url' => plugin_dir_url(ROLLBAR_PLUGIN_FILE), 215 | ], 216 | ); 217 | } 218 | 219 | /** 220 | * Adds a setting field to the admin setting page. 221 | * 222 | * @param Setting $setting 223 | * @param array{value: mixed} $args The arguments to pass to the callback. If the 'value' key is set it will be used 224 | * as the value of the input field. 225 | * @return void 226 | */ 227 | private function addSettingsField(Setting $setting, array $args = []): void 228 | { 229 | add_settings_field( 230 | id: 'rollbar_wp_' . $setting->id, 231 | title: $setting->getLabelElement(), 232 | callback: $setting->render(...), 233 | page: 'rollbar_wp', 234 | section: $setting->section, 235 | args: $args, 236 | ); 237 | } 238 | 239 | /** 240 | * Renders the header for the "Advanced" section of the settings page. 241 | * 242 | * @return void 243 | */ 244 | public function advancedSectionHeader(): void 245 | { 246 | Template::print(ROLLBAR_PLUGIN_DIR . '/templates/admin/settings-section-header.php', [ 247 | 'id' => 'rollbar_settings_advanced_header', 248 | 'title' => 'Advanced Settings', 249 | 'description' => '

See the configuration documentation for more details on each of these settings.

', 251 | ]); 252 | } 253 | 254 | /** 255 | * Renders the header for the "Advanced" section of the settings page. 256 | * 257 | * @return void 258 | */ 259 | public function telemetrySectionHeader(): void 260 | { 261 | Template::print(ROLLBAR_PLUGIN_DIR . '/templates/admin/settings-section-header.php', [ 262 | 'id' => 'rollbar_settings_telemetry_header', 263 | 'title' => 'Telemetry Settings', 264 | 'description' => '

See the Rollbar ' 265 | . 'telemetry documentation and WordPress action reference for more details on these settings.

', 267 | ]); 268 | } 269 | 270 | /** 271 | * Renders the settings page. 272 | * 273 | * @return void 274 | */ 275 | public function optionsPage(): void 276 | { 277 | Template::print(ROLLBAR_PLUGIN_DIR . '/templates/admin/settings.php'); 278 | } 279 | 280 | /** 281 | * Function called when the "Restore All Defaults" button is clicked. 282 | * 283 | * @return void 284 | */ 285 | public static function restoreDefaultsAction(): void 286 | { 287 | if (!check_admin_referer('rollbar_wp_restore_defaults')) { 288 | return; 289 | } 290 | Settings::getInstance()->restoreDefaults(); 291 | 292 | FlashMessages::addMessage( 293 | message: 'Default Rollbar settings restored.', 294 | ); 295 | 296 | wp_redirect(admin_url('/options-general.php?page=rollbar_wp')); 297 | exit(); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /public/admin/rollbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rollbar for WordPress admin JS. 3 | * 4 | * @var {{ 5 | * nonce: string, 6 | * rest_nonce: string, 7 | * rest_root: string, 8 | * plugin_url: string, 9 | * }} rollbarSettings Declared in SettingsPage::enqueueAdminScripts(). 10 | */ 11 | 12 | // import Rollbar from 'rollbar'; 13 | 14 | document.addEventListener('DOMContentLoaded', () => { 15 | // Collapse Settings Sections Toggles 16 | const sectionHeaders = document.querySelectorAll('.rollbar-settings-section-header'); 17 | sectionHeaders.forEach(header => { 18 | const toggle = header.querySelector('.section-heading'); 19 | toggle.addEventListener('click', () => { 20 | header.classList.toggle('open'); 21 | }); 22 | }); 23 | 24 | const enablePhpInput = document.getElementById('rollbar_wp_php_logging_enabled'); 25 | const enableJsInput = document.getElementById('rollbar_wp_js_logging_enabled'); 26 | 27 | // Toggle PHP/JS logging 28 | Object.entries({ 29 | "rollbar_wp_server_side_access_token_container": enablePhpInput, 30 | "rollbar_wp_client_side_access_token_container": enableJsInput, 31 | }).forEach(([container, el]) => { 32 | // Include here so they start open. 33 | if (el.checked) { 34 | document.getElementById(container).classList.remove('hidden'); 35 | } 36 | el.addEventListener('input', () => { 37 | if (el.checked) { 38 | document.getElementById(container).classList.remove('hidden'); 39 | return; 40 | } 41 | document.getElementById(container).classList.add('hidden'); 42 | }); 43 | }); 44 | 45 | // Reset Default 46 | document.querySelectorAll('.rollbar_wp_restore_default').forEach(el => { 47 | el.addEventListener('click', (event) => { 48 | event.preventDefault(); 49 | const setting = el.getAttribute('data-setting'); 50 | let defaultValue = el.getAttribute('data-default'); 51 | const type = el.getAttribute('data-setting-input-type'); 52 | const input = document.querySelectorAll(`[data-setting="${setting}"]`); 53 | if (null === input || input.length === 0) { 54 | return; 55 | } 56 | switch (type) { 57 | case 'boolean': 58 | input[0].checked = defaultValue === 'true'; 59 | break; 60 | case 'checkbox': 61 | defaultValue = defaultValue.split(','); 62 | for (const checkbox of input) { 63 | checkbox.checked = defaultValue.includes(checkbox.value); 64 | } 65 | break; 66 | default: 67 | input[0].value = defaultValue; 68 | } 69 | }) 70 | }); 71 | 72 | const testMessageContainer = document.getElementById('rollbar_test_message_container'); 73 | 74 | /** 75 | * Escapes HTML characters in the string. 76 | * 77 | * @param {string} str The string to escape. 78 | * @returns {string} 79 | */ 80 | const escapeHtml = (str) => { 81 | return new Option(str).innerHTML; 82 | }; 83 | 84 | /** 85 | * Escapes HTML characters in the string for use in an attribute. 86 | * 87 | * @param {string} str The string to escape. 88 | * @returns {string} 89 | */ 90 | const escapeHtmlAttr = (str) => { 91 | return ('' + str) 92 | .replace(/&/g, '&') 93 | .replace(/"/g, '"') 94 | .replace(/'/g, ''') 95 | .replace(//g, '>'); 97 | }; 98 | 99 | /** 100 | * Returns a formatted notice message HTML Node. 101 | * 102 | * @param {string} type The notice type class. 103 | * @param {string} message The notice message. The message should be escaped before being passed in. 104 | * @returns {ChildNode} 105 | */ 106 | const makeNotice = (type, message) => { 107 | const el = document.createElement('div'); 108 | el.innerHTML = `
${message}
`; 109 | return el.firstChild; 110 | }; 111 | 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 | 144 | /** 145 | * Request a test of the Rollbar config in PHP via the REST API. 146 | */ 147 | const testPhpLogging = () => { 148 | if (enablePhpInput.checked === false) { 149 | testMessageContainer.appendChild(makeNotice( 150 | 'notice-warning', 151 | `

PHP Test: Skipped testing logging since it is disabled.

`, 152 | )); 153 | return; 154 | } 155 | fetch(`${rollbarSettings.rest_root}rollbar/v1/test-php-logging`, { 156 | method: 'POST', 157 | headers: { 158 | 'X-WP-Nonce': rollbarSettings.rest_nonce, 159 | 'Content-Type': 'application/json', 160 | }, 161 | body: JSON.stringify(collectSettings()), 162 | }).then(response => { 163 | response.json().then(data => { 164 | if (data.success) { 165 | testMessageContainer.appendChild(makeNotice( 166 | 'notice-success', 167 | `

PHP Test: Test message sent to Rollbar using PHP. Please check 168 | your Rollbar dashboard to see if you received it. Save your changes and you're 169 | ready to go.

`, 170 | )); 171 | return; 172 | } 173 | testMessageContainer.appendChild(makeNotice( 174 | 'notice-error', 175 | `

PHP Test: There was a problem accessing Rollbar service.

176 |
    177 |
  • Code:${escapeHtml(data.code)}
  • 178 |
  • Message:${escapeHtml(data.message)}
  • 179 |
`, 180 | )); 181 | }) 182 | }).finally(() => { 183 | testButton.disabled = false; 184 | }); 185 | }; 186 | 187 | /** 188 | * Send the test log message to the Rollbar service. 189 | * 190 | * @param {string} token The client side access token. 191 | * @param {string} env The environment. 192 | */ 193 | const sendTestJsMessage = (token, env) => { 194 | Rollbar.configure({ 195 | accessToken: token, 196 | captureUncaught: true, 197 | captureUnhandledRejections: true, 198 | payload: { 199 | environment: env 200 | } 201 | }); 202 | Rollbar.info( 203 | "Test message from Rollbar WordPress plugin using JS: integration with WordPress successful", 204 | function(error, data) { 205 | if (undefined !== data) { 206 | testMessageContainer.appendChild(makeNotice( 207 | 'notice-success', 208 | `

JS Test: Test message sent to Rollbar using JS. Please check 209 | your Rollbar dashboard to see if you received it. Save your changes and you're 210 | ready to go.

`, 211 | )); 212 | return; 213 | } 214 | testMessageContainer.appendChild(makeNotice( 215 | 'notice-error', 216 | `

JS Test: There was a problem accessing Rollbar service using provided credentials for JS logging. Check your client side token.

`, 217 | )); 218 | } 219 | ); 220 | }; 221 | 222 | /** 223 | * Test the Rollbar JS configuration to ensure it is working. 224 | */ 225 | const testJsLogging = () => { 226 | const clientSideAccessToken = document.getElementById('rollbar_wp_client_side_access_token').value; 227 | const environment = document.getElementById('rollbar_wp_environment').value; 228 | if (enableJsInput.checked === false) { 229 | testMessageContainer.appendChild(makeNotice( 230 | 'notice-warning', 231 | `

JS Test: Skipped testing logging since it is disabled.

`, 232 | )); 233 | return; 234 | } 235 | 236 | // Depending on the plugin status prior to loading the page, the Rollbar object may not exist. So we fetch it. 237 | if (window.Rollbar !== undefined) { 238 | sendTestJsMessage(clientSideAccessToken, environment); 239 | return; 240 | } 241 | // If the Rollbar object doesn't exist, we need to load and configure it. 242 | window._rollbarConfig = { 243 | accessToken: clientSideAccessToken, 244 | captureUncaught: true, 245 | captureUnhandledRejections: true, 246 | payload: { 247 | environment: environment 248 | } 249 | } 250 | // Load the Rollbar JS library. 251 | const script = document.createElement('script'); 252 | script.src = rollbarSettings.plugin_url + "public/js/rollbar.snippet.js"; 253 | let mainRollbarScript = document.querySelector('script[src$="rollbar.min.js"]'); 254 | 255 | // Wait for both Rollbar JS libraries to load before sending the test message. 256 | script.addEventListener('load', () => { 257 | if (window.Rollbar !== undefined) { 258 | sendTestJsMessage(clientSideAccessToken, environment); 259 | return; 260 | } 261 | document.querySelector('script[src$="rollbar.min.js"]')?.addEventListener('load', () => { 262 | sendTestJsMessage(clientSideAccessToken, environment); 263 | }); 264 | }); 265 | 266 | // Handle error loading either the snippet or main Rollbar JS library. 267 | window.addEventListener('error', (e) => { 268 | if (e.target === script || e.target === mainRollbarScript) { 269 | testMessageContainer.appendChild(makeNotice( 270 | 'notice-error', 271 | `

JS Test: There was an error loading the Rollbar JS library.

`, 272 | )); 273 | } 274 | }, true); 275 | 276 | document.body.appendChild(script); 277 | }; 278 | 279 | // Send Test Message 280 | const testButton = document.getElementById('rollbar_wp_test_logging'); 281 | testButton?.addEventListener('click', () => { 282 | testButton.disabled = true; 283 | testMessageContainer.innerHTML = ''; 284 | testPhpLogging(); 285 | testJsLogging(); 286 | }); 287 | }); 288 | -------------------------------------------------------------------------------- /src/Plugin.php: -------------------------------------------------------------------------------- 1 | settings = Settings::getInstance(); 58 | $this->config = []; 59 | $this->hooks(); 60 | $this->initPhpLogging(); 61 | } 62 | 63 | /** 64 | * Post-initialization tasks 65 | * 66 | * Sets up admin views and API after the main initialization is complete. 67 | * 68 | * @return void 69 | */ 70 | protected function postInit(): void 71 | { 72 | // Set up the telemetry listener. 73 | if ($this->getSetting('enable_telemetry_listener')) { 74 | $this->listener = Listener::getInstance(); 75 | } 76 | } 77 | 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 | 90 | /** 91 | * Updates the plugin configuration 92 | * 93 | * Merges the provided configuration with the existing one and applies 94 | * it to the Rollbar logger if available. 95 | * 96 | * @param array $config Configuration options to merge with existing config 97 | * @return void 98 | */ 99 | public function configure(array $config): void 100 | { 101 | $this->config = array_merge($this->config, $config); 102 | 103 | if ($logger = Rollbar::logger()) { 104 | $logger->configure($this->config); 105 | } 106 | } 107 | 108 | /** 109 | * Returns true if the admin area should be disabled. 110 | * 111 | * Disable the admin settings page if the `ROLLBAR_DISABLE_ADMIN` constant is defined and set to `true`. 112 | * This allows for the plugin to be used without the admin settings page, for example, if the plugin is managed 113 | * via the `wp-config.php` file. 114 | * 115 | * @return bool 116 | * @since 3.0.0 117 | */ 118 | public static function disabledAdmin(): bool 119 | { 120 | return defined('ROLLBAR_DISABLE_ADMIN') && constant('ROLLBAR_DISABLE_ADMIN'); 121 | } 122 | 123 | /** 124 | * Returns true if the admin area should be disabled. 125 | * 126 | * @return bool 127 | * @since 3.0.0 128 | */ 129 | public static function userCanViewAdmin(): bool 130 | { 131 | if (self::disabledAdmin()) { 132 | return false; 133 | } 134 | 135 | /** 136 | * Filter to enable / disable the admin settings page of the plugin for the current user. 137 | * 138 | * This filter cannot override the `ROLLBAR_DISABLE_ADMIN` constant. 139 | * 140 | * @param bool $allow `true` to enable the admin settings page, `false` to disable it. 141 | * @since 3.0.0 142 | */ 143 | return apply_filters('rollbar_user_can_view_admin', current_user_can('manage_options')) === true; 144 | } 145 | 146 | /** 147 | * @param string $path 148 | * @return string 149 | * 150 | * @since 3.0.0 151 | */ 152 | public static function getAssetUrl(string $path): string 153 | { 154 | if (str_starts_with($path, '/')) { 155 | $path = substr($path, 1); 156 | } 157 | return plugin_dir_url(ROLLBAR_PLUGIN_FILE) . $path; 158 | } 159 | 160 | /** 161 | * Retrieve the value of a specific setting. 162 | * 163 | * @param string $setting The name of the setting to retrieve. 164 | * @return mixed The value of the setting, or null if the setting does not exist. 165 | * 166 | * @since 3.0.0 167 | */ 168 | public function getSetting(string $setting): mixed 169 | { 170 | return $this->settings->get($setting) ?? null; 171 | } 172 | 173 | /** 174 | * Sets a specific setting with the provided value. 175 | * 176 | * @param string $setting The name of the setting to be updated. 177 | * @param mixed $value The value to be assigned to the specified setting. 178 | * @return void 179 | * 180 | * @since 3.0.0 181 | */ 182 | public function setSetting(string $setting, mixed $value): void 183 | { 184 | $this->settings->set($setting, $value); 185 | } 186 | 187 | /** 188 | * Get the Settings instance used by this plugin 189 | * 190 | * @return Settings The settings instance 191 | */ 192 | public function settingsInstance(): Settings 193 | { 194 | return $this->settings; 195 | } 196 | 197 | /** 198 | * Register WordPress hooks and actions 199 | * 200 | * Sets up action hooks for JavaScript logging in both frontend and admin areas. 201 | * 202 | * @return void 203 | */ 204 | private function hooks(): void 205 | { 206 | add_action('init', $this->onInit(...)); 207 | add_action('wp_head', $this->initJsLogging(...)); 208 | add_action('admin_head', $this->initJsLogging(...)); 209 | } 210 | 211 | /** 212 | * Build the error reporting level bitmask 213 | * 214 | * Creates a bitmask of PHP error reporting levels up to and including the specified cutoff level. 215 | * 216 | * @param int $cutoff The maximum error level to include 217 | * @return int The combined bitmask of all error levels up to the cutoff 218 | */ 219 | public static function buildIncludedErrno(int $cutoff): int 220 | { 221 | $levels = [ 222 | E_ERROR, 223 | E_WARNING, 224 | E_PARSE, 225 | E_NOTICE, 226 | E_CORE_ERROR, 227 | E_CORE_WARNING, 228 | E_COMPILE_ERROR, 229 | E_COMPILE_WARNING, 230 | E_USER_ERROR, 231 | E_USER_WARNING, 232 | E_USER_NOTICE, 233 | E_STRICT, 234 | E_RECOVERABLE_ERROR, 235 | E_DEPRECATED, 236 | E_USER_DEPRECATED, 237 | E_ALL, 238 | ]; 239 | 240 | $included_errno = 0; 241 | 242 | foreach ($levels as $level) { 243 | if ($level <= $cutoff) { 244 | $included_errno |= $level; 245 | } 246 | } 247 | 248 | return $included_errno; 249 | } 250 | 251 | /** 252 | * Initialize PHP error logging with Rollbar 253 | * 254 | * Sets up the Rollbar PHP error handler if PHP logging is enabled. 255 | * Handles configuration errors by displaying appropriate error messages. 256 | * 257 | * @param bool $ignoreEnabledSetting If true, the plugin will not check the 'php_logging_enabled' setting first. 258 | * 259 | * @return void 260 | */ 261 | public function initPhpLogging(bool $ignoreEnabledSetting = false): void 262 | { 263 | // Return if logging is not enabled 264 | if (!$ignoreEnabledSetting && false === $this->getSetting('php_logging_enabled')) { 265 | return; 266 | } 267 | 268 | // installs global error and exception handlers 269 | try { 270 | Rollbar::init($this->buildPHPConfig()); 271 | } catch (Exception $exception) { 272 | FlashMessages::addMessage( 273 | message: 'Rollbar is misconfigured. Please, fix your configuration here: Rollbar Settings.', 275 | type: 'error', 276 | ); 277 | } 278 | } 279 | 280 | /** 281 | * Build the PHP configuration for Rollbar 282 | * 283 | * Generates the configuration array for the Rollbar PHP SDK from the plugin settings. 284 | * Processes boolean values and special settings like error reporting levels and person function. 285 | * 286 | * @return array The complete Rollbar PHP configuration 287 | */ 288 | public function buildPHPConfig(): array 289 | { 290 | $config = $this->settings->getAll(); 291 | 292 | $config['access_token'] = $this->getSetting('server_side_access_token'); 293 | $config['included_errno'] = self::buildIncludedErrno($this->getSetting('included_errno')); 294 | $config['timeout'] = intval($this->getSetting('timeout')); 295 | 296 | // Set up telemetry 297 | $config['telemetry'] = false; 298 | if ($this->getSetting('enable_telemetry_listener')) { 299 | $config['telemetry'] = [ 300 | 'includeItemsInTelemetry' => $this->getSetting('include_items_in_telemetry'), 301 | 'includeIgnoredItemsInTelemetry' => $this->getSetting('include_ignored_items_in_telemetry'), 302 | ]; 303 | } 304 | 305 | foreach ($config as $setting => $value) { 306 | if (SettingType::Boolean !== Settings::getSettingType($setting)) { 307 | continue; 308 | } 309 | $config[$setting] = Settings::toBoolean($value); 310 | } 311 | 312 | if ($config['enable_person_reporting'] && empty($config['person_fn']) && empty($config['person'])) { 313 | $config['person_fn'] = Settings::getPersonFunction(...); 314 | } 315 | 316 | $config['framework'] = 'wordpress ' . get_bloginfo('version'); 317 | 318 | /** 319 | * Filters the Rollbar Core SDK PHP configuration. 320 | * 321 | * @param array $config The Rollbar PHP configuration array. 322 | * @since 3.0.0 323 | */ 324 | return apply_filters('rollbar_php_config', $config); 325 | } 326 | 327 | /** 328 | * Initialize JavaScript error logging with Rollbar 329 | * 330 | * Sets up the Rollbar JavaScript error handler if JS logging is enabled. 331 | * Checks for valid configuration and outputs the necessary JavaScript code. 332 | * 333 | * @return void 334 | */ 335 | public function initJsLogging(): void 336 | { 337 | // Return if logging is not enabled 338 | if (false === $this->getSetting('js_logging_enabled')) { 339 | return; 340 | } 341 | 342 | // Return if access token is not set 343 | if (empty($this->getSetting('client_side_access_token'))) { 344 | FlashMessages::addMessage( 345 | message: 'Rollbar is misconfigured. Please, fix your configuration here: Rollbar Settings.', 347 | type: 'error', 348 | ); 349 | return; 350 | } 351 | 352 | /** 353 | * Filter that can be used to set the nonce attribute value of the frontend JS script. 354 | * 355 | * @param string|null $nonce The nonce value to be used in the script tag. If `null` the attribute is excluded. 356 | * Default is `null`. 357 | * @since 3.1.0 358 | */ 359 | $nonce = apply_filters('rollbar_js_nonce', null); 360 | 361 | $rollbarJs = RollbarJsHelper::buildJs($this->buildJsConfig(), nonce: $nonce); 362 | 363 | echo $rollbarJs; 364 | } 365 | 366 | /** 367 | * Build the JavaScript configuration for Rollbar 368 | * 369 | * Generates the configuration array for the Rollbar JavaScript SDK from the plugin settings. 370 | * Applies filters to allow customization of the JavaScript configuration. 371 | * 372 | * @return array The complete Rollbar JavaScript configuration 373 | */ 374 | public function buildJsConfig(): array 375 | { 376 | $config = [ 377 | 'accessToken' => $this->getSetting('client_side_access_token'), 378 | 'captureUncaught' => true, 379 | 'payload' => [ 380 | 'environment' => $this->getSetting('environment'), 381 | ], 382 | ]; 383 | 384 | /** 385 | * Filters the Rollbar JavaScript configuration. 386 | * 387 | * @param array $config The Rollbar JavaScript configuration array. 388 | * @since 3.0.0 389 | * 390 | */ 391 | return apply_filters('rollbar_js_config', $config); 392 | } 393 | 394 | /** 395 | * Check if the Must-Use plugin is enabled 396 | * 397 | * Determines if the Rollbar Must-Use plugin is installed and active. 398 | * 399 | * @return bool True if the Must-Use plugin exists, false otherwise 400 | */ 401 | public static function mustUsePluginEnabled(): bool 402 | { 403 | return file_exists(plugin_dir_path(__DIR__) . '../../mu-plugins/rollbar-mu-plugin.php'); 404 | } 405 | 406 | /** 407 | * Enable the Must-Use plugin 408 | * 409 | * Copies the Rollbar Must-Use plugin into the WordPress mu-plugins directory, 410 | * creating the directory if it doesn't exist. 411 | * 412 | * @return void 413 | * @throws Exception If the directory cannot be created or the plugin cannot be copied 414 | */ 415 | public function enableMustUsePlugin(): void 416 | { 417 | $muPluginsDir = plugin_dir_path(__DIR__) . '../../mu-plugins/'; 418 | 419 | $muPluginFilepath = plugin_dir_path(__DIR__) . 'mu-plugin/rollbar-mu-plugin.php'; 420 | 421 | if (!file_exists($muPluginsDir) && !mkdir($muPluginsDir, 0700)) { 422 | throw new Exception('Can\'t create the mu-plugins directory: ' . $muPluginsDir); 423 | } 424 | 425 | if (!copy($muPluginFilepath, $muPluginsDir . 'rollbar-mu-plugin.php')) { 426 | throw new Exception('Can\'t copy mu-plugin from ' . $muPluginFilepath . ' to ' . $muPluginsDir); 427 | } 428 | } 429 | 430 | /** 431 | * Disable the Must-Use plugin 432 | * 433 | * Removes the Rollbar Must-Use plugin from the WordPress mu-plugins directory if it exists. 434 | * 435 | * @return void 436 | * @throws Exception If the plugin file cannot be deleted 437 | */ 438 | public function disableMustUsePlugin(): void 439 | { 440 | $file = plugin_dir_path(__DIR__) . '../../mu-plugins/rollbar-mu-plugin.php'; 441 | if (file_exists($file) && !unlink($file)) { 442 | throw new Exception('Can\'t delete the mu-plugin'); 443 | } 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rollbar for WordPress 2 | 3 | [![Plugin Version](https://img.shields.io/wordpress/plugin/v/rollbar.svg)](https://wordpress.org/plugins/rollbar/) [![WordPress Version Compatibility](https://img.shields.io/wordpress/v/rollbar.svg)](https://wordpress.org/plugins/rollbar/) [![Downloads](https://img.shields.io/wordpress/plugin/dt/rollbar.svg)](https://wordpress.org/plugins/rollbar/) [![Rating](https://img.shields.io/wordpress/plugin/r/rollbar.svg)](https://wordpress.org/plugins/rollbar/) 4 | 5 | Rollbar full-stack error tracking for WordPress. 6 | 7 | Rollbar collects errors that happen in your application, notifies you, and analyzes them so you can debug and fix them. 8 | This plugin integrates Rollbar into your WordPress installation. 9 | 10 | #### Helpful Links 11 | 12 | * [Documentation](https://docs.rollbar.com/docs/wordpress) 13 | * [Official WordPress.org Plugin](https://wordpress.org/plugins/rollbar/) 14 | * [How Rollbar Works](https://rollbar.com/discover/) 15 | * [Rollbar Pricing](https://rollbar.com/pricing/) 16 | 17 | [Official WordPress.org Plugin](https://wordpress.org/plugins/rollbar/) 18 | #### Table of Contents 19 | 20 | 1. [Installation](#installation) 21 | 1. [Through WordPress Plugin directory](#through-wordpress-plugin-directory) 22 | 2. [Through Packagist](#through-packagist-recommended-new) 23 | 3. [Through WPackagist](#through-wpackagist) 24 | 2. [Configuration](#configuration) 25 | 1. [WordPress Admin](#wordpress-admin) 26 | 2. [Programmatic Configuration](#programmatic-configuration) 27 | 3. [Advanced Use](#advanced-use) 28 | 1. [Constants](#constants) 29 | 2. [Environment Variables](#environment-variables) 30 | 3. [Filters](#filters) 31 | 4. [Telemetry](#telemetry) 32 | 4. [Help / Support](#help--support) 33 | 5. [Special thanks](#special-thanks) 34 | 6. [Contributing](#contributing) 35 | 7. [Testing](#testing) 36 | 37 | ## Installation 38 | 39 | ### Through [WordPress Plugin directory](https://wordpress.org/plugins/rollbar/) 40 | 41 | The easiest way to install the plugin is from the WordPress Plugin directory. If you have an existing WordPress 42 | installation, and you want to add Rollbar: 43 | 44 | 1. In your WordPress administration panel go to `Plugins` → `Add New`. 45 | 2. Search for "Rollbar" and find `Rollbar` by Rollbar in the search results. 46 | 3. Click `Install Now` next to the `Rollbar` plugin. 47 | 4. In `Plugins` → `Installed plugins` find `Rollbar` and click `Activate` underneath. 48 | 49 | **Warning**: This installation method might not be suitable for complex WordPress projects. The plugin installed this 50 | way will be self-contained and include all of the required dependencies for itself and the `rollbar/rollbar-php` 51 | library. In complex projects, this might lead to version conflicts between dependencies and other plugins/packages. If 52 | this is an issue in your project, we recommend the "Packagist" installation method. 53 | 54 | ### Through [Packagist](https://packagist.org/packages/rollbar/rollbar-php-wordpress) *recommended new* 55 | 56 | *Note: this only works if your WordPress project is managed with Composer. 57 | Read [Using Composer with WordPress](https://roots.io/using-composer-with-wordpress/) for more details.* 58 | 59 | This is a recommended way to install the Rollbar plugin for advanced projects. This way ensures the plugin and all of 60 | its dependencies are managed by Composer. 61 | 62 | You can install the plugin by running the following command in the root directory of your WordPress project: 63 | 64 | ```txt 65 | composer require rollbar/rollbar-php-wordpress:^3.0 66 | ``` 67 | 68 | ### Through [WPackagist](https://wpackagist.org/) 69 | 70 | *Note: if your WordPress project is managed with Composer, we strongly recommend installing the plugin through 71 | Packagist instead.* 72 | 73 | *Installing the plugin from wpackagist.org instead of packagist.org will result in the plugin being managed by 74 | Composer. However, the downside is that it's dependencies will not be managed by composer. Instead they will be packaged 75 | in the plugin's `vendor` directory not in your project's `vendor` directory. This has the potential to cause name 76 | collisions if other plugins or your project use different versions of the same dependencies.* 77 | 78 | To install the plugin from wpackagist.org run the following steps command in the root directory of your WordPress 79 | project: 80 | 81 | ```txt 82 | composer require wpackagist-plugin/rollbar 83 | ``` 84 | 85 | ## Configuration 86 | 87 | The plugin can be configured in the WordPress Admin or programmatically. 88 | 89 | ### WordPress Admin 90 | 91 | The plugin provides a settings page in the WordPress Admin that allows you to configure the plugin. This settings page 92 | can be disabled by setting the `ROLLBAR_DISABLE_ADMIN` constant to `true` in your `wp-config.php` file. 93 | 94 | 1. In `Plugins` → `Installed plugins` find `Rollbar` and click `Activate` underneath. 95 | 2. Log into your [Rollbar account dashboard](https://rollbar.com/login/): 96 | 1. Go to `Settings` → `Project Access Tokens`. 97 | 2. Copy the token value under `post_client_item` and `post_server_item`. 98 | 3. In WordPress, navigate to `Settings` → `Rollbar`: 99 | 1. Enable `PHP error logging` and/or `Javascript error logging` depending on your needs. 100 | 2. Paste the tokens you copied in step 7 in `Access Token` section. 101 | 3. Provide the name of your environment in `Environment`. By default, the environment will be taken from `WP_ENV` 102 | environment variable if it's set otherwise it's blank. We recommend to fill this out either with `development` or 103 | `production`. 104 | 4. Pick a minimum logging level. Only errors at that or higher level will be reported. For 105 | reference: [PHP Manual: Predefined Error Constants](http://php.net/manual/en/errorfunc.constants.php). 106 | 5. Click `Save Changes`. 107 | 108 | ### Programmatic Configuration 109 | 110 | The plugin can also be configured programmatically. This is useful if you want to configure the plugin in a more 111 | advanced way or if you want to disable the admin settings page. 112 | 113 | ```php 114 | // wp-config.php 115 | 116 | // Configure the plugin. 117 | define( 'ROLLBAR_SETTINGS', [ 118 | 'php_logging_enabled' => true, 119 | 'server_side_access_token' => '', 120 | 'js_logging_enabled' => true, 121 | 'client_side_access_token' => '', 122 | 'environment' => 'development', 123 | 'included_errno' => E_ERROR, 124 | 'enable_person_reporting' => true, 125 | ] ); 126 | 127 | // Optional: disable the admin settings page so the plugin is configured only programmatically. 128 | define( 'ROLLBAR_DISABLE_ADMIN', true ); 129 | ``` 130 | 131 | ## Advanced Use 132 | 133 | ### Constants 134 | 135 | The plugin provides a number of constants that can be used to configure the plugin. These should typically be defined in 136 | the `wp-config.php` file. 137 | 138 | Note: If you define these constants, they will override the settings defined in the WordPress Admin. 139 | 140 | * `ROLLBAR_DISABLE_ADMIN` - (optional) Removes the Rollbar Admin menu from the WordPress Admin if set to `true`. This 141 | allows for the plugin to be used without the admin settings page, for example, if the plugin is managed via the 142 | `wp-config.php` file. 143 | * `ROLLBAR_SETTINGS` - (optional) An associative array of settings to override the settings values from the WordPress 144 | Admin. Note: if you disable the admin settings page with `ROLLBAR_DISABLE_ADMIN` constant, this constant must be used 145 | to configure the plugin. 146 | * `ROLLBAR_ACCESS_TOKEN` - The Rollbar PHP server access token. 147 | * `ROLLBAR_CLIENT_ACCESS_TOKEN` - The Rollbar JS client access token. 148 | 149 | #### WordPress Constants 150 | 151 | The Rollbar plugin respects the following WordPress constants: 152 | 153 | * `WP_ENV` - (optional) The environment name. This is used to determine the environment name for the Rollbar project. 154 | * `WP_PROXY_HOST` - (optional) The proxy host. If both `WP_PROXY_HOST` and `WP_PROXY_PORT` are set, the plugin will use 155 | the respect the HTTP proxy when making requests to Rollbar. 156 | * `WP_PROXY_PORT` - (optional) The proxy port. 157 | * `WP_PROXY_USERNAME` - (optional) The proxy username. 158 | * `WP_PROXY_PASSWORD` - (optional) The proxy password. 159 | * `WP_PROXY_BYPASS_HOSTS` - (optional) The proxy bypass hosts. This is a comma-separated list of hosts that should not 160 | be 161 | proxied. If proxying is enabled, but you don't want to proxy requests to Rollbar, you can add `api.rollbar.com` to 162 | this list. 163 | 164 | ### Environment Variables 165 | 166 | The plugin looks for the following environment variables to configure itself. Note: these are overridden by the 167 | constants defined above. 168 | 169 | * `ROLLBAR_ACCESS_TOKEN` - The Rollbar PHP server access token. 170 | * `ROLLBAR_CLIENT_ACCESS_TOKEN` - The Rollbar JS client access token. 171 | * `WP_ENV` - (optional) The environment name. This is used to determine the environment name for the Rollbar project. 172 | 173 | ### Filters 174 | 175 | The plugin provides a number of filters that allow you to customize the behavior of the plugin. 176 | 177 | #### `apply_filters('rollbar_api_admin_permission', bool $value, string $route, WP_REST_Request $request)` 178 | 179 | Filter to allow or deny access to a Rollbar route in the WordPress REST API used in the WordPress Admin. Generally, 180 | this should be the same as the `rollbar_user_can_view_admin` filter. 181 | 182 | **Parameters** 183 | 184 | * `bool $value` - The initial value. Defaults is `true` for admin users, `false` for non-admin users. 185 | * `string $route` - The route being accessed. 186 | * `WP_REST_Request $request` - The REST request object. 187 | 188 | #### `apply_filters('rollbar_js_config', array $config)` 189 | 190 | Filters the Rollbar JavaScript configuration. 191 | 192 | **Parameters** 193 | 194 | * `array $config` - The Rollbar JavaScript configuration array. 195 | 196 | #### `apply_filters('rollbar_js_nonce', string|null $nonce)` 197 | 198 | Filter that can be used to set the nonce attribute value of the frontend JS script. 199 | 200 | **Since: 3.1.0** 201 | 202 | **Parameters** 203 | 204 | * `string|null $nonce` - The nonce value to be used in the script tag. If `null` the attribute is excluded. Default 205 | is `null`. 206 | 207 | #### `apply_filters('rollbar_plugin_settings', array $settings)` 208 | 209 | Filters the Rollbar plugin settings. 210 | 211 | **Parameters** 212 | 213 | * `array $settings` - The Rollbar plugin settings array. 214 | 215 | #### `apply_filters('rollbar_php_config', array $config)` 216 | 217 | Filters the Rollbar Core SDK PHP configuration. 218 | 219 | **Parameters** 220 | 221 | * `array $config` - The Rollbar PHP configuration array. 222 | 223 | #### `apply_filters('rollbar_telemetry_actions', array $actions)` 224 | 225 | Filter the list of actions to instrument with Telemetry. 226 | 227 | **Parameters** 228 | 229 | * `array $actions` - An associative array where the keys are action names and the values are the number of 230 | arguments accepted by the action. 231 | 232 | #### `apply_filters('rollbar_telemetry_custom_handlers', array $handlers)` 233 | 234 | Filter the list of custom action event handlers for Telemetry. 235 | 236 | Note: The custom action handler will only be called if the action is instrumented with Telemetry. This means you must 237 | select the action on the settings page, or add it to the list of actions using the `rollbar_telemetry_actions` filter. 238 | 239 | **Parameters** 240 | 241 | * `array $handlers` - An associative array where the keys are action names 242 | and the values are the custom event handler. 243 | 244 | #### `apply_filters('rollbar_user_can_view_admin', bool $disable)` 245 | 246 | Filter to enable / disable the admin settings page of the plugin for the current user. 247 | 248 | This filter cannot override the `ROLLBAR_DISABLE_ADMIN` constant. 249 | 250 | **Parameters** 251 | 252 | * `bool $allow` - `true` to enable the admin settings page, `false` to disable it. 253 | 254 | ### Telemetry 255 | 256 | Starting in version 3.0.0 of the Rollbar plugin, Telemetry is enabled by default. Telemetry is a feature that allows 257 | the plugin to track events that occur in your WordPress installation prior to an exception or message being sent to 258 | Rollbar. The Telemetry data is sent to Rollbar along with the exception or message, and can be used to provide 259 | additional context and help debug the issue. 260 | 261 | You can modify the list of actions you want to instrument with Telemetry by selecting them on the settings page, or 262 | using the `rollbar_telemetry_actions` filter. To use a custom handler for a specific action, use the 263 | `rollbar_telemetry_custom_handlers` filter. This can also be used to change the handler on any of the default actions. 264 | 265 | #### Registering custom actions 266 | 267 | You can also instrument custom actions like this: 268 | 269 | ```php 270 | use Rollbar\WordPress\Telemetry\Listener; 271 | 272 | // Register a custom action with a custom handler function. 273 | Listener::getInstance()->instrumentAction( 274 | action: 'my_custom_action', 275 | priority: 10, 276 | acceptedArgs: 2, 277 | argsHandler: function ($action, ...$args) { 278 | $foo = $action; 279 | return 'custom_listener_test_action: ' . implode(', ', $args); 280 | }, 281 | ); 282 | 283 | // Use the default handler for the action. 284 | Listener::getInstance()->instrumentAction( 285 | action: 'my_other_custom_action', 286 | priority: 10, 287 | acceptedArgs: 1, 288 | argsHandler: Listener::concatExtraArgs(...), 289 | ); 290 | ``` 291 | 292 | Of course, you can also call `Rollbar::captureTelemetryEvent()` directly to send custom events. See the 293 | [Telemetry documentation](https://docs.rollbar.com/docs/php-telemetry) for more information. 294 | 295 | ## Help / Support 296 | 297 | If you run into any issues, please email us at [support@rollbar.com](mailto:support@rollbar.com) 298 | 299 | For bug reports, please [open an issue on GitHub](https://github.com/rollbar/rollbar-php-wordpress/issues/new). 300 | 301 | ## Contributing 302 | 303 | 1. Fork it 304 | 2. Create your feature branch (`git checkout -b my-new-feature`) 305 | 3. Commit your changes (`git commit -am 'Added some feature'`) 306 | 4. Make sure tests pass (see below). 307 | 5. Push to the branch (`git push origin my-new-feature`) 308 | 6. Create a new Pull Request 309 | 310 | ## Testing 311 | 312 | To run the tests, you will need to install the dependencies for 313 | [@wordpress/env](https://www.npmjs.com/package/@wordpress/env) including Node.js, git, and Docker. 314 | 315 | 1. `npm install` - This will install the dependencies to set up the local environment. 316 | 2. `npm run start` - This will start a local WordPress installation with the Rollbar plugin installed. 317 | 3. `npm run test` - This will run the tests. 318 | 319 | You can set the `WP_ENV_PHP_VERSION` environment variable to test with different versions of PHP. If you are changing 320 | the version, you can do so by running `WP_ENV_PHP_VERSION="8.2" npm run wp-env start -- --update` and setting the 321 | environment variable based on the version you want to test with. 322 | 323 | ## Special thanks 324 | 325 | The original author of this package is [@flowdee](https://twitter.com/flowdee/). This is a fork and continuation of his 326 | efforts. 327 | 328 | ## Tagging 329 | 330 | This is only for contributors with committer access: 331 | 332 | 1. Bump the plugin version. 333 | 1. Bump the plugin version in `readme.txt` under `Stable tag`. 334 | 2. Add record in the `Changelog` section of the `readme.txt`. 335 | 3. Add record in the `Upgrade Notice` section of the `readme.txt`. 336 | 4. Bump the plugin version in `rollbar-php-wordpress.php` in the `Version:` comment. 337 | 5. Bump the plugin version in `src/Plugin.php` in the `\Rollbar\WordPress\Plugin::VERSION` constant. 338 | 6. Add and commit the changes you made to bump the plugin version: 339 | `git add readme.txt rollbar-php-wordpress.php src/Plugin.php && git commit -m"Bump version to v[version number]"` 340 | 7. Bump versions of the JS and CSS files versions in Settings.php class to force refresh of those assets on users' 341 | installations. 342 | 8. `git push origin master` 343 | 2. Tag the new version from the `master` branch and push upstream with `git tag v[version number] && git push --tags`. 344 | 3. Publish a new release on [GitHub](https://github.com/rollbar/rollbar-php-wordpress/releases). 345 | 4. Update the WordPress Plugin Directory Subversion Repository. 346 | 1. Fetch the latest contents of Subversion repo with `svn update`. 347 | 2. Remove the contents of `trunk/` with `rm -Rf trunk`. 348 | 3. Update the contents of `trunk/` with a clone of the tag you created in step 2. 349 | 1. `git clone https://github.com/rollbar/rollbar-php-wordpress.git trunk` 350 | 2. `cd trunk && git checkout tags/v[version number] && cd ..` 351 | 3. `rm -Rf trunk/.git` 352 | 4. `svn add trunk --force` 353 | 5. `svn commit -m"Sync with GitHub repo"` 354 | 4. Create the Subversion tag: 355 | `svn copy https://plugins.svn.wordpress.org/rollbar/trunk https://plugins.svn.wordpress.org/rollbar/tags/[version number] -m"Tag [version number]"`. 356 | Notice the version number in Subversion doesn't include the "v" prefix. 357 | 358 | ## Disclaimer 359 | 360 | This plugin is a community-driven contribution. All rights reserved to Rollbar. 361 | -------------------------------------------------------------------------------- /src/Telemetry/Listener.php: -------------------------------------------------------------------------------- 1 | 0, 29 | 'plugins_loaded' => 0, 30 | 'setup_theme' => 0, 31 | 'after_setup_theme' => 0, 32 | 'init' => 0, 33 | 'wp_loaded' => 0, 34 | 'pre_get_posts' => 1, 35 | 'admin_init' => 0, 36 | 'send_headers' => 1, 37 | 'wp_head' => 0, 38 | 'wp_footer' => 0, 39 | 'shutdown' => 0, 40 | ]; 41 | 42 | /** 43 | * All available actions in WordPress. 44 | * 45 | * This does not include filters or any dynamically named actions. This list is an associative array where the keys 46 | * are action names and the values are the number of arguments accepted by the action. 47 | * 48 | * @link https://developer.wordpress.org/apis/hooks/action-reference/ 49 | */ 50 | public const ALL_ACTIONS = [ 51 | // Lifecycle actions 52 | 'muplugins_loaded' => 0, 53 | 'registered_taxonomy' => 3, 54 | 'registered_post_type' => 2, 55 | 'plugins_loaded' => 0, 56 | 'sanitize_comment_cookies' => 0, 57 | 'setup_theme' => 0, 58 | 'load_textdomain' => 2, 59 | 'after_setup_theme' => 0, 60 | 'auth_cookie_malformed' => 2, 61 | 'auth_cookie_valid' => 2, 62 | 'set_current_user' => 0, 63 | 'init' => 0, 64 | 'widgets_init' => 0, 65 | 'register_sidebar' => 1, 66 | 'wp_register_sidebar_widget' => 1, 67 | 'wp_default_scripts' => 1, 68 | 'wp_default_styles' => 1, 69 | 'admin_bar_init' => 0, 70 | 'add_admin_bar_menus' => 1, 71 | 'wp_loaded' => 0, 72 | 'parse_request' => 1, 73 | 'send_headers' => 1, 74 | 'parse_query' => 1, 75 | 'pre_get_posts' => 1, 76 | 'posts_selection' => 1, 77 | 'wp' => 1, 78 | 'template_redirect' => 0, 79 | 'get_header' => 2, 80 | 'wp_enqueue_scripts' => 0, 81 | 'wp_head' => 0, 82 | 'wp_print_styles' => 0, 83 | 'wp_print_scripts' => 0, 84 | 'get_search_form' => 2, 85 | 'loop_start' => 1, 86 | 'the_post' => 2, 87 | 'loop_end' => 1, 88 | 'get_sidebar' => 2, 89 | 'dynamic_sidebar' => 1, 90 | 'pre_get_comments' => 1, 91 | 'wp_meta' => 0, 92 | 'get_footer' => 2, 93 | 'wp_footer' => 0, 94 | 'wp_print_footer_scripts' => 0, 95 | 'admin_bar_menu' => 1, 96 | 'wp_before_admin_bar_render' => 0, 97 | 'wp_after_admin_bar_render' => 0, 98 | 'shutdown' => 0, 99 | // Admin actions 100 | 'auth_redirect' => 1, 101 | 'admin_menu' => 1, 102 | 'user_admin_menu' => 1, 103 | 'network_admin_menu' => 1, 104 | 'admin_init' => 0, 105 | 'current_screen' => 1, 106 | 'admin_xml_ns' => 0, 107 | 'admin_enqueue_scripts' => 1, 108 | 'admin_print_styles' => 0, 109 | 'admin_print_scripts' => 0, 110 | 'admin_head' => 0, 111 | 'in_admin_header' => 0, 112 | 'admin_notices' => 0, 113 | 'all_admin_notices' => 0, 114 | 'restrict_manage_posts' => 2, 115 | 'pre_user_query' => 1, 116 | 'in_admin_footer' => 0, 117 | 'admin_footer' => 1, 118 | 'admin_print_footer_scripts' => 0, 119 | 'wp_dashboard_setup' => 0, 120 | // Post, Taxonomy, and Attachment actions 121 | 'post_submitbox_misc_actions' => 1, 122 | 'add_attachment' => 1, 123 | 'clean_post_cache' => 2, 124 | 'delete_attachment' => 2, 125 | 'wp_trash_post' => 2, 126 | 'trashed_post' => 2, 127 | 'untrash_post' => 2, 128 | 'untrashed_post' => 2, 129 | 'before_delete_post' => 2, 130 | 'delete_post' => 2, 131 | 'deleted_post' => 2, 132 | 'edit_attachment' => 1, 133 | 'edit_post' => 2, 134 | 'pre_post_update' => 2, 135 | 'post_updated' => 3, 136 | 'transition_post_status' => 3, 137 | 'publish_phone' => 1, 138 | 'save_post' => 3, 139 | 'updated_postmeta' => 4, 140 | 'wp_insert_post' => 3, 141 | 'xmlrpc_publish_post' => 1, 142 | // Taxonomy and Term actions 143 | 'create_term' => 4, 144 | 'created_term' => 4, 145 | 'add_term_relationship' => 3, 146 | 'added_term_relationship' => 3, 147 | 'set_object_terms' => 6, 148 | 'edit_terms' => 3, 149 | 'edited_terms' => 3, 150 | 'edit_term_taxonomy' => 3, 151 | 'edited_term_taxonomy' => 3, 152 | 'edit_term_taxonomies' => 1, 153 | 'edited_term_taxonomies' => 1, 154 | 'pre_delete_term' => 2, 155 | 'delete_term_taxonomy' => 1, 156 | 'deleted_term_taxonomy' => 1, 157 | 'delete_term' => 5, 158 | 'delete_term_relationships' => 3, 159 | 'deleted_term_relationships' => 3, 160 | 'clean_object_term_cache' => 2, 161 | 'clean_term_cache' => 3, 162 | 'split_shared_term' => 4, 163 | // Comment, Ping, and Trackback actions 164 | 'comment_closed' => 1, 165 | 'comment_id_not_found' => 1, 166 | 'comment_flood_trigger' => 2, 167 | 'comment_on_draft' => 1, 168 | 'comment_post' => 3, 169 | 'edit_comment' => 2, 170 | 'delete_comment' => 2, 171 | 'deleted_comment' => 2, 172 | 'trash_comment' => 2, 173 | 'trashed_comment' => 2, 174 | 'untrash_comment' => 2, 175 | 'untrashed_comment' => 2, 176 | 'spam_comment' => 2, 177 | 'spammed_comment' => 2, 178 | 'unspam_comment' => 2, 179 | 'unspammed_comment' => 2, 180 | 'pingback_post' => 1, 181 | 'pre_ping' => 3, 182 | 'trackback_post' => 1, 183 | 'wp_check_comment_disallowed_list' => 6, 184 | 'wp_insert_comment' => 2, 185 | 'wp_set_comment_status' => 2, 186 | // RSS, Atom, and RDF actions 187 | 'add_link' => 1, 188 | 'delete_link' => 1, 189 | 'edit_link' => 1, 190 | 'atom_entry' => 0, 191 | 'atom_head' => 0, 192 | 'atom_ns' => 0, 193 | 'commentrss2_item' => 2, 194 | 'rdf_header' => 0, 195 | 'rdf_item' => 0, 196 | 'rdf_ns' => 0, 197 | 'rss_head' => 0, 198 | 'rss_item' => 0, 199 | 'rss2_head' => 0, 200 | 'rss2_item' => 0, 201 | 'rss2_ns' => 0, 202 | // Template actions 203 | 'comment_form' => 1, 204 | 'comment_form_after' => 0, 205 | 'do_robots' => 0, 206 | 'do_robotstxt' => 0, 207 | 'switch_theme' => 3, 208 | 'after_switch_theme' => 2, 209 | // Management actions 210 | 'activity_box_end' => 0, 211 | 'add_option' => 2, 212 | 'added_option' => 2, 213 | 'dbx_post_sidebar' => 1, 214 | 'delete_option' => 1, 215 | 'deleted_option' => 1, 216 | 'delete_user' => 3, 217 | 'edit_form_top' => 1, 218 | 'edit_form_after_title' => 1, 219 | 'edit_form_after_editor' => 1, 220 | 'edit_form_advanced' => 1, 221 | 'edit_page_form' => 1, 222 | 'edit_user_profile' => 1, 223 | 'login_form' => 0, 224 | 'login_head' => 0, 225 | 'lost_password' => 1, 226 | 'lostpassword_form' => 0, 227 | 'lostpassword_post' => 2, 228 | 'manage_link_custom_column' => 2, 229 | 'manage_posts_custom_column' => 2, 230 | 'manage_posts_columns' => 2, 231 | 'manage_pages_custom_column' => 2, 232 | 'manage_pages_columns' => 1, 233 | 'manage_media_custom_column' => 2, 234 | 'manage_media_columns' => 2, 235 | // Make sure not to set 'password_reset' higher than 1. The password is the second argument. We don't want to 236 | // log it in the Telemetry data. 237 | 'password_reset' => 1, 238 | 'personal_options_update' => 1, 239 | 'profile_personal_options' => 1, 240 | 'profile_update' => 3, 241 | 'quick_edit_custom_box' => 3, 242 | 'register_form' => 0, 243 | 'register_post' => 3, 244 | 'retrieve_password' => 1, 245 | 'show_user_profile' => 1, 246 | 'sidebar_admin_page' => 0, 247 | 'sidebar_admin_setup' => 0, 248 | 'update_option' => 3, 249 | 'updated_option' => 3, 250 | 'user_new_form' => 1, 251 | 'user_profile_update_errors' => 3, 252 | 'wpmu_new_user' => 1, 253 | 'user_register' => 2, 254 | 'welcome_panel' => 0, 255 | // Make sure not to set 'wp_authenticate' higher than 1. The password is the second argument. We don't want to 256 | // log it in the Telemetry data. 257 | 'wp_authenticate' => 1, 258 | 'wp_login' => 2, 259 | 'wp_logout' => 1, 260 | // Advanced actions 261 | 'activated_plugin' => 2, 262 | 'add_meta_boxes' => 2, 263 | 'network_admin_notices' => 0, 264 | 'user_admin_notices' => 0, 265 | 'blog_privacy_selector' => 0, 266 | 'check_admin_referer' => 2, 267 | 'check_ajax_referer' => 2, 268 | 'customize_controls_enqueue_scripts' => 0, 269 | 'customize_register' => 1, 270 | 'customize_preview_init' => 0, 271 | 'deactivated_plugin' => 2, 272 | 'generate_rewrite_rules' => 1, 273 | 'upgrader_process_complete' => 2, 274 | // Login actions 275 | 'login_init' => 0, 276 | 'login_enqueue_scripts' => 0, 277 | 'login_header' => 0, 278 | 'admin_email_confirm' => 1, 279 | 'admin_email_confirm_form' => 0, 280 | 'validate_password_reset' => 2, 281 | 'resetpass_form' => 1, 282 | 'user_request_action_confirmed' => 1, 283 | 'login_footer' => 0, 284 | ]; 285 | 286 | protected function __construct() 287 | { 288 | } 289 | 290 | /** 291 | * @inheritdoc 292 | */ 293 | protected function postInit(): void 294 | { 295 | $enable = Plugin::getInstance()->settingsInstance()->get('enable_telemetry_listener'); 296 | if (!$enable) { 297 | return; 298 | } 299 | $actions = array_flip(Plugin::getInstance()->settingsInstance()->get('telemetry_hooks')); 300 | if (empty($actions)) { 301 | $actions = self::DEFAULT_ACTIONS; 302 | } 303 | 304 | $actions = array_intersect_key(self::ALL_ACTIONS, $actions); 305 | $customHandlers = $this->getCustomHandlers(); 306 | 307 | /** 308 | * Filter the list of actions to instrument with Telemetry. 309 | * 310 | * @param array $actions An associative array where the keys are action names and the values are 311 | * the number of arguments accepted by the action. 312 | * @since 3.0.0 313 | */ 314 | $actions = apply_filters('rollbar_telemetry_actions', $actions); 315 | 316 | foreach ($actions as $action => $acceptedArgs) { 317 | $handler = $customHandlers[$action] ?? self::concatExtraArgs(...); 318 | $this->instrumentAction( 319 | action: $action, 320 | acceptedArgs: $acceptedArgs, 321 | argsHandler: $handler, 322 | ); 323 | } 324 | } 325 | 326 | /** 327 | * Returns a list of custom action event handlers for Telemetry. 328 | * 329 | * @return array 330 | */ 331 | protected function getCustomHandlers(): array 332 | { 333 | /** 334 | * Filter the list of custom action event handlers for Telemetry. 335 | * 336 | * @param array $handlers An associative array where the keys are 337 | * action names and the values are the corresponding event handler functions. 338 | * @since 3.0.0 339 | */ 340 | return apply_filters('rollbar_telemetry_custom_handlers', [ 341 | 'after_setup_theme' => self::handlerAfterSetupTheme(...), 342 | 'plugins_loaded' => self::handlerPluginsLoaded(...), 343 | ]); 344 | } 345 | 346 | /** 347 | * Captures a Telemetry event for a WordPress action. 348 | * 349 | * @param string $action The action to log. 350 | * @param int $priority The priority at which the action should be executed. Default is 10. 351 | * @param int $acceptedArgs The number of arguments the action accepts. Default is 1. If this is greater than 1, the 352 | * `$argsHandler` argument is required. 353 | * @param null|callable(string, mixed...):string $argsHandler This is a function that will be used to serialize the 354 | * arguments passed to the action into a string. If null, the default behavior is to log the action name only. 355 | * @return void 356 | * 357 | * @throws InvalidArgumentException If `acceptedArgs` is greater than 1 and `argsHandler` is not provided. 358 | */ 359 | public function instrumentAction( 360 | string $action, 361 | int $priority = 10, 362 | int $acceptedArgs = 1, 363 | callable $argsHandler = null, 364 | ): void { 365 | if (null === $argsHandler && $acceptedArgs > 1) { 366 | throw new InvalidArgumentException( 367 | 'If acceptedArgs is greater than 1, argsHandler must be provided.', 368 | ); 369 | } 370 | add_action( 371 | hook_name: $action, 372 | callback: function (...$args) use ($action, $argsHandler) { 373 | $message = $action; 374 | if (null !== $argsHandler) { 375 | $message = $argsHandler($action, ...$args); 376 | } 377 | $this->log('Action triggered: ' . $message); 378 | }, 379 | priority: $priority, 380 | accepted_args: $acceptedArgs, 381 | ); 382 | } 383 | 384 | /** 385 | * Concatenates the action name with additional arguments for logging. 386 | * 387 | * @param string $action The action name to log. 388 | * @param mixed ...$args Additional arguments to concatenate with the action name. 389 | * @return string The resulting log message string. 390 | */ 391 | public static function concatExtraArgs(string $action, mixed ...$args): string 392 | { 393 | $args = array_map(function ($arg) { 394 | return match (gettype($arg)) { 395 | 'object' => self::objectRepresentation($arg), 396 | 'array' => 'Array(' . count($arg) . ')', 397 | 'boolean' => $arg ? 'true' : 'false', 398 | 'resource' => 'resource(' . get_resource_type($arg) . ': ' . get_resource_id($arg) . ')', 399 | 'NULL' => 'null', 400 | default => (string)$arg, // All other types can be cast to a string. 401 | }; 402 | }, $args); 403 | return $action . ': ' . implode(', ', $args); 404 | } 405 | 406 | /** 407 | * Captures a Telemetry event for a WordPress action hook. 408 | * 409 | * @param string $message The message to log. 410 | * @param EventLevel $level The level of the event. 411 | * @return void 412 | */ 413 | public function log(string $message, EventLevel $level = EventLevel::Info): void 414 | { 415 | $telemeter = Rollbar::getTelemeter(); 416 | if (null === $telemeter) { 417 | return; 418 | } 419 | $telemeter->captureLog($message, $level); 420 | } 421 | 422 | /** 423 | * Custom handler for the `after_setup_theme` action. 424 | * 425 | * Adds the active theme and its ancestors (if it is a child theme) to the log message. 426 | * 427 | * @param string $action 428 | * @param mixed ...$args 429 | * @return string 430 | */ 431 | public static function handlerAfterSetupTheme(string $action, mixed ...$args): string 432 | { 433 | $theme = wp_get_theme(); 434 | $themes = [$theme->get_stylesheet_directory()]; 435 | while ($theme->parent()) { 436 | $theme = $theme->parent(); 437 | $themes[] = $theme->get_stylesheet_directory(); 438 | } 439 | return $action . ': Active Theme: ' . implode(' > ', $themes); 440 | } 441 | 442 | /** 443 | * Custom handler for the `plugins_loaded` action. 444 | * 445 | * Adds the list of active plugins to the log message. 446 | * 447 | * @param string $action 448 | * @param mixed ...$args 449 | * @return string 450 | */ 451 | public static function handlerPluginsLoaded(string $action, mixed ...$args): string 452 | { 453 | return $action . ': Active Plugins: ' . implode(', ', get_option('active_plugins')); 454 | } 455 | 456 | /** 457 | * Serializes an object into a string representation. 458 | * 459 | * @param object $object The object to serialize. 460 | * @return string 461 | */ 462 | protected static function objectRepresentation(object $object): string 463 | { 464 | if ($object instanceof \UnitEnum) { 465 | return 'enum(' . $object::class . '::' . $object->name . ')'; 466 | } 467 | if ($object instanceof \Closure) { 468 | return 'closure'; 469 | } 470 | $class = get_class($object); 471 | if (!property_exists($object, 'id')) { 472 | return 'object(' . $class . ')'; 473 | } 474 | $vars = get_object_vars($object); 475 | $id = $vars['id'] ?? 'unknown'; 476 | 477 | return 'object(' . $class . ') {id: ' . $id . '}'; 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Rollbar === 2 | Contributors: arturmoczulski, jorbin, danielmorell 3 | Tags: rollbar, full stack, error, tracking, error tracking, error reporting, reporting, debug 4 | Requires at least: 6.5.0 5 | Tested up to: 6.8 6 | Requires PHP: 8.1 7 | Stable tag: 3.0.0 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | Official Rollbar full-stack error tracking for WordPress supported by Rollbar, Inc. 12 | 13 | == Description == 14 | 15 | Rollbar collects errors that happen in your application, notifies you, and analyzes them so you can debug and fix them. 16 | This plugin integrates Rollbar into your WordPress installation. 17 | 18 | = Features = 19 | 20 | * PHP & JavaScript error logging. 21 | * Define an environment for each single WordPress installation or Multisite blog. 22 | * Specify your desired logging level. 23 | * Regular updates and improvements! 24 | 25 | **Please note:** 26 | In order to use this plugin, a [Rollbar account](https://rollbar.com/) is required. 27 | 28 | = Support = 29 | 30 | * Browse [issue tracker](https://github.com/rollbar/rollbar-php-wordpress/issues) on GitHub and report new issues. 31 | * If you run into any issues, please email us at [support@rollbar.com](mailto:support@rollbar.com). 32 | * For bug reports, please [open an issue on GitHub](https://github.com/rollbar/rollbar-php-wordpress/issues/new). 33 | 34 | = You like it? = 35 | 36 | You can at least support the plugin development and rate it up! 37 | 38 | = Disclaimer = 39 | 40 | This plugin is a community-driven contribution. All rights reserved to [Rollbar](https://rollbar.com/). 41 | 42 | == Installation == 43 | 44 | The installation and configuration of the plugin are as simple as it can be. 45 | 46 | = Through WordPress Plugin directory = 47 | 48 | The easiest way to install the plugin is from the WordPress Plugin directory. If you have an existing WordPress 49 | installation, and you want to add Rollbar: 50 | 51 | 1. In your WordPress administration panel go to `Plugins` → `Add New`. 52 | 2. Search for "Rollbar" and find `Rollbar` by Rollbar in the search results. 53 | 3. Click `Install Now` next to the `Rollbar` plugin. 54 | 4. In `Plugins` → `Installed plugins` find `Rollbar` and click `Activate` underneath. 55 | 56 | **Warning**: This installation method might not be suitable for complex WordPress projects. The plugin installed this 57 | way will be self-contained and include all of the required dependencies for itself and the `rollbar/rollbar-php` 58 | library. In complex projects, this might lead to version conflicts between dependencies and other plugins/packages. If 59 | this is an issue in your project, we recommend the "Packagist" installation method. 60 | 61 | = Through Packagist = 62 | 63 | *Note: this only works if your WordPress project is managed with Composer. 64 | Read [Using Composer with WordPress](https://roots.io/using-composer-with-wordpress/) for more details.* 65 | 66 | This is a recommended way to install the Rollbar plugin for advanced projects. This way ensures the plugin and all of 67 | its dependencies are managed by Composer. 68 | 69 | You can install the plugin by running the following command in the root directory of your WordPress project: 70 | 71 | ``` 72 | composer require rollbar/rollbar-php-wordpress:^3.0 73 | ``` 74 | 75 | = Through WPackagist = 76 | 77 | *Note: if your WordPress project is managed with Composer, we strongly recommend installing the plugin through 78 | Packagist instead.* 79 | 80 | *Installing the plugin from wpackagist.org instead of packagist.org will result in the plugin being managed by 81 | Composer. However, the downside is that it's dependencies will not be managed by composer. Instead they will be packaged 82 | in the plugin's `vendor` directory not in your project's `vendor` directory. This has the potential to cause name 83 | collisions if other plugins or your project use different versions of the same dependencies.* 84 | 85 | To install the plugin from wpackagist.org run the following steps command in the root directory of your WordPress 86 | project: 87 | 88 | ``` 89 | composer require wpackagist-plugin/rollbar 90 | ``` 91 | 92 | = Configuration = 93 | 94 | The plugin can be configured in the WordPress Admin or programmatically. 95 | 96 | **WordPress Admin** 97 | 98 | The plugin provides a settings page in the WordPress Admin that allows you to configure the plugin. This settings page 99 | can be disabled by setting the `ROLLBAR_DISABLE_ADMIN` constant to `true` in your `wp-config.php` file. 100 | 101 | 1. In `Plugins` → `Installed plugins` find `Rollbar` and click `Activate` underneath. 102 | 2. Log into your [Rollbar account dashboard](https://rollbar.com/login/): 103 | 1. Go to `Settings` → `Project Access Tokens`. 104 | 2. Copy the token value under `post_client_item` and `post_server_item`. 105 | 3. In WordPress, navigate to `Settings` → `Rollbar`: 106 | 1. Enable `PHP error logging` and/or `Javascript error logging` depending on your needs. 107 | 2. Paste the tokens you copied in step 7 in `Access Token` section. 108 | 3. Provide the name of your environment in `Environment`. By default, the environment will be taken from `WP_ENV` 109 | environment variable if it's set otherwise it's blank. We recommend to fill this out either with `development` or 110 | `production`. 111 | 4. Pick a minimum logging level. Only errors at that or higher level will be reported. For 112 | reference: [PHP Manual: Predefined Error Constants](http://php.net/manual/en/errorfunc.constants.php). 113 | 5. Click `Save Changes`. 114 | 115 | **Programmatic Configuration** 116 | 117 | The plugin can also be configured programmatically. This is useful if you want to configure the plugin in a more 118 | advanced way or if you want to disable the admin settings page. 119 | 120 | ```php 121 | // wp-config.php 122 | 123 | // Configure the plugin. 124 | define( 'ROLLBAR_SETTINGS', [ 125 | 'php_logging_enabled' => true, 126 | 'server_side_access_token' => '', 127 | 'js_logging_enabled' => true, 128 | 'client_side_access_token' => '', 129 | 'environment' => 'development', 130 | 'included_errno' => E_ERROR, 131 | 'enable_person_reporting' => true, 132 | ] ); 133 | 134 | // Optional: disable the admin settings page so the plugin is configured only programmatically. 135 | define( 'ROLLBAR_DISABLE_ADMIN', true ); 136 | ``` 137 | 138 | == Frequently Asked Questions == 139 | 140 | = Multisite supported? = 141 | Yes, of course. Additionally, you can assign different environments to each of your blogs. 142 | 143 | = I have a complex WordPress project and use composer for managing dependencies. Is your plugin composer friendly? = 144 | Yes. It's actually the recommended method of installation. You can install the `rollbar/rollbar-php-wordpress` package 145 | using composer. 146 | 147 | == Screenshots == 148 | 149 | 1. Settings page. 150 | 151 | == Changelog == 152 | 153 | = Version 3.1.0 (?) = 154 | * Fixed settings values not being saved correctly when they match the default. 155 | 156 | = Version 3.0.0 (October 17 2025) = 157 | * Fixed CSRF vulnerability. 158 | * Removed support for PHP 8.0 and below. 159 | * Updated and improved the settings page. 160 | * Updated the Rollbar core PHP SDK to v4.1. 161 | * Updated the Rollbar core JavaScript SDK to v2.26. 162 | * Added support for telemetry and added auto-instrumentation. 163 | * Added support for `ROLLBAR_DISABLE_ADMIN` to remove the plugin settings page from the admin. 164 | * Added support for `ROLLBAR_SETTINGS` to configure the plugin without the admin page. 165 | * Added support for `ROLLBAR_CLIENT_ACCESS_TOKEN` constant or environment variable to set the client access token. 166 | * Added support for `WP_PROXY_BYPASS_HOSTS`, `WP_PROXY_USERNAME`, and `WP_PROXY_PASSWORD` for better proxy management. 167 | * Added `rollbar_api_admin_permission` filter to allow custom authorization of the admin API. 168 | * Added `rollbar_user_can_view_admin` filter to allow custom disabling of the admin page. 169 | * Added `rollbar_php_config` filter to allow more exact control over Rollbar PHP configurations. 170 | * Added `rollbar_telemetry_actions` filter to allow control of which actions are logged via telemetry. 171 | * Added `rollbar_telemetry_custom_handlers` filter to allow custom control over what is logged in telemetry messages. 172 | * Added 'framework' details with the WordPress version to the item payload. 173 | 174 | = Version 2.7.1 (September 13 2023) = 175 | * Fix issue that could lead to fatal error with some settings (https://github.com/rollbar/rollbar-php-wordpress/pull/120) 176 | 177 | = Version 2.7.0 (September 11 2023) = 178 | * Updated PHP Dependencies including loading seperate dependencies for PHP7 and PHP8. (https://github.com/rollbar/rollbar-php-wordpress/pull/114) 179 | * Updated node development dependencies (https://github.com/rollbar/rollbar-php-wordpress/pull/115) 180 | 181 | = Version 2.6.4 (June 13th 2022) = 182 | * Updated admin test results to show a skipped test as a success (https://github.com/rollbar/rollbar-php-wordpress/pull/110) 183 | * Fixed new session being created on every request (https://github.com/rollbar/rollbar-php-wordpress/pull/111) 184 | * Added search for WP_ENV as a constant or the environment (https://github.com/rollbar/rollbar-php-wordpress/pull/108) 185 | * Added a link to settings from the plugins page (https://github.com/rollbar/rollbar-php-wordpress/pull/109) 186 | 187 | = Version 2.6.3 (April 18th 2022) = 188 | * Update the tested WP versions 189 | 190 | = Version 2.6.2 (March 4th 2020) = 191 | * don’t start a session for wp-cron (https://github.com/rollbar/rollbar-php-wordpress/pull/88) 192 | 193 | = Version 2.6.1 (December 27th 2019) = 194 | * fix(initPhpLogging): Moving fetch settings to before settings check. (https://github.com/rollbar/rollbar-php-wordpress/pull/84) 195 | 196 | = Version 2.5.1 (February 20th 2019) = 197 | * Fixed a call to Rollbar\WordPress\Defaults for enableMustUsePlugin (https://github.com/rollbar/rollbar-php-wordpress/pull/75) 198 | 199 | = Version 2.5.0 (February 19th 2019) = 200 | * Moved Rollbar initialization from `plugins_loaded` hook to the invocation of the main plugin file (https://github.com/rollbar/rollbar-php-wordpress/issues/73) 201 | * Added support for running the plugin as a Must-Use plugin (https://github.com/rollbar/rollbar-php-wordpress/issues/73) 202 | * Added `Enable as a Must-Use plugin` settings (https://github.com/rollbar/rollbar-php-wordpress/issues/73) 203 | * UI improvements 204 | 205 | = Version 2.4.10 (February 5th 2019) = 206 | * Added support for ROLLBAR_ACCESS_TOKEN constant and respecting the ROLLBAR_ACCESS_TOKEN environment variable (https://github.com/rollbar/rollbar-php-wordpress/issues/72) 207 | * Fixed tests 208 | * Updated dependencies 209 | 210 | = Version 2.4.9 (January 24th 2019) = 211 | * Fix for issue #69 (https://github.com/rollbar/rollbar-php-wordpress/issues/69) 212 | 213 | = Version 2.4.8 (January 17th 2019) = 214 | * Update rollbar-php to v1.7.4 215 | 216 | = Version 2.4.7 (August 14th 2018) = 217 | * Update rollbar-php to v1.6.2 218 | 219 | = Version 2.4.6 (August 13th 2018) = 220 | * Configuration option custom_data_method doesn’t exist in Rollbar (https://github.com/rollbar/rollbar-php-wordpress/issues/66) 221 | 222 | = Version 2.4.5 (August 7th 2018) = 223 | * Update rollbar-php to v1.6.1 224 | * Remove mentions of IRC channel from README.md and readme.txt 225 | 226 | = Version 2.4.4 (June 18th 2018) = 227 | * Update rollbar-php to v1.5.3 228 | 229 | = Version 2.4.3 (June 11th 2018) = 230 | * Update rollbar-php to v1.5.2 231 | * Use rollbar-php:v1.5.2 new defaults methods to handle restoring default settings. 232 | 233 | = Version 2.4.2 (25th May 2018) = 234 | * Fixed the plugin not always respecting the boolean true settings (https://github.com/rollbar/rollbar-php-wordpress/issues/58) 235 | 236 | = Version 2.4.1 (19th May 2018) = 237 | * Updated rollbar-php dependency to v1.5.1 238 | 239 | = Version 2.4.0 (17th May 2018) = 240 | * Added capture_ip, capture_email, and capture_username to the config options. 241 | * Fixed populating config options from the database to the plugin for boolean values. 242 | * Updated rollbar-php dependency to v1.5.0 243 | 244 | = Version 2.3.1 (10th April 2018) = 245 | * Fixed a bug in strict PHP setups (https://github.com/rollbar/rollbar-php-wordpress/issues/44) 246 | 247 | = Version 2.3.0 (5th April 2018) = 248 | * Added `rollbar_plugin_settings` filter 249 | * Added majority of Rollbar PHP config options to the User Interface. 250 | * Moved the settings from Tools -> Rollbar to Settings -> Rollbar 251 | 252 | = Version 2.2.0 (4th December 2017) = 253 | * Fixed the logging level to correctly inlude errors from specified level and up. 254 | * Changed the default logging level setting. 255 | * Added instructions on tagging the repo to the README.md file. 256 | * Added tests for logging level. 257 | * Set up a PHPUnit test suite. 258 | * Add rollbar_js_config filter for JS config data customization. 259 | * Updated dependencies. 260 | 261 | = Version 2.1.2 (11th October 2017) = 262 | * Use the default rest route instead of permalink /wp-json 263 | * Dynamically build the Rollbar JS snippet URL 264 | 265 | = Version 2.1.1 (11th October 2017) = 266 | * Fixed location of the Rollbar JS snippet 267 | 268 | = Version 2.1.0 (11th October 2017) = 269 | * Added "Send test message to Rollbar" button 270 | * Fixed the plugin's name inconsistency between WordPress plugin directory and composer. 271 | 272 | = Version 2.0.1 (6th October 2017) = 273 | * Fixed RollbarJsHelper class loading bug in src/Plugin.php (https://github.com/rollbar/rollbar-php-wordpress/issues/23) 274 | 275 | = Version 2.0.0 (9th September 2017) = 276 | * Added support for the WP_ENV environment variable 277 | * Organized the code into namespaces 278 | * Moved helper functions into static methods 279 | * Updated Rollbar PHP library 280 | * Included dependencies to make the plugin self-contained when installing through WP plugin directory 281 | * Rewrote readme files 282 | 283 | = Version 1.0.3 (12th August 2016) = 284 | * Updated rollbar php lib to latest v0.18.2 285 | * Added .pot translation file 286 | * Removed WP.org assets from plugin folder 287 | 288 | = Version 1.0.2 (28th March 2016) = 289 | * Updated rollbar js lib 290 | * Added escaping for setting values 291 | 292 | = Version 1.0.0 (4th November 2015) = 293 | * Initial release! 294 | 295 | == Upgrade Notice == 296 | 297 | = Version 2.7.1 (September 13 2023) 298 | Fix issue that could lead to fatal error with some settings 299 | 300 | = Version 2.7.0 (September 11 2023) 301 | Add compatability for modern PHP versions 302 | 303 | = Version 2.6.4 (June 13th 2022) = 304 | Updated admin test results to show a skipped test as a success. Fixed new session being created on every request. Added search for WP_ENV as a constant or the environment. Added a link to settings from the plugins page. 305 | 306 | = Version 2.6.3 (April 18th 2022) = 307 | * Update the tested WP versions 308 | 309 | = Version 2.6.2 (March 4th 2020) = 310 | * don’t start a session for wp-cron (https://github.com/rollbar/rollbar-php-wordpress/pull/88) 311 | 312 | = Version 2.6.1 (December 27th 2019) = 313 | * fix(initPhpLogging): Moving fetch settings to before settings check. (https://github.com/rollbar/rollbar-php-wordpress/pull/84) 314 | 315 | = Version 2.5.1 (February 20th 2019) = 316 | * Fixed a call to Rollbar\WordPress\Defaults for enableMustUsePlugin (https://github.com/rollbar/rollbar-php-wordpress/pull/75) 317 | 318 | = Version 2.5.0 (February 19th 2019) = 319 | * Moved Rollbar initialization from `plugins_loaded` hook to the invocation of the main plugin file (https://github.com/rollbar/rollbar-php-wordpress/issues/73) 320 | * Added support for running the plugin as a Must-Use plugin (https://github.com/rollbar/rollbar-php-wordpress/issues/73) 321 | * Added `Enable as a Must-Use plugin` settings (https://github.com/rollbar/rollbar-php-wordpress/issues/73) 322 | * UI improvements 323 | 324 | = Version 2.4.10 (February 5th 2019) = 325 | * Added support for ROLLBAR_ACCESS_TOKEN constant and respecting the ROLLBAR_ACCESS_TOKEN environment variable (https://github.com/rollbar/rollbar-php-wordpress/issues/72) 326 | * Fixed tests 327 | * Updated dependencies 328 | 329 | = Version 2.4.9 (January 24th 2019) = 330 | * Fix for issue #69 (https://github.com/rollbar/rollbar-php-wordpress/issues/69) 331 | 332 | = Version 2.4.8 (January 17th 2019) = 333 | * Update rollbar-php to v1.7.4 334 | 335 | = Version 2.4.7 (August 14th 2018) = 336 | * Update rollbar-php to v1.6.2 337 | 338 | = Version 2.4.6 (August 13th 2018) = 339 | * Configuration option custom_data_method doesn’t exist in Rollbar (https://github.com/rollbar/rollbar-php-wordpress/issues/66) 340 | 341 | = Version 2.4.5 (August 7th 2018) = 342 | * Update rollbar-php to v1.6.1 343 | * Remove mentions of IRC channel from README.md and readme.txt 344 | 345 | = Version 2.4.4 (June 18th 2018) = 346 | * Update rollbar-php to v1.5.3 347 | 348 | = Version 2.4.3 (June 11th 2018) = 349 | * Update rollbar-php to v1.5.2 350 | * Use rollbar-php:v1.5.2 new defaults methods to handle restoring default settings. 351 | 352 | = Version 2.4.2 (25th May 2018) = 353 | * Fixed the plugin not always respecting the boolean true settings (https://github.com/rollbar/rollbar-php-wordpress/issues/58) 354 | 355 | = Version 2.4.1 (19th May 2018) = 356 | * Updated rollbar-php dependency to v1.5.1 357 | 358 | = Version 2.4.0 (5th April 2018) = 359 | * Added capture_ip, capture_email, and capture_username to the config options. 360 | * Fixed populating config options from the database to the plugin for boolean values. 361 | * Updated rollbar-php dependency to v1.5.0 362 | 363 | = Version 2.3.1 (10th April 2018) = 364 | * Fixed a bug in strict PHP setups (https://github.com/rollbar/rollbar-php-wordpress/issues/44) 365 | 366 | = Version 2.3.0 (5th April 2018) = 367 | * Added `rollbar_plugin_settings` filter 368 | * Added majority of Rollbar PHP config options to the User Interface. 369 | * Moved the settings from Tools -> Rollbar to Settings -> Rollbar 370 | 371 | = Version 2.2.0 (4th December 2017) = 372 | * Fixed the logging level to correctly inlude errors from specified level and up. 373 | * Changed the default logging level setting. 374 | * Added instructions on tagging the repo to the README.md file. 375 | * Added tests for logging level. 376 | * Set up a PHPUnit test suite. 377 | * Add rollbar_js_config filter for JS config data customization. 378 | * Updated dependencies. 379 | 380 | = Version 2.1.2 (11th October 2017) = 381 | * Use the default rest route instead of permalink /wp-json 382 | * Dynamically build the Rollbar JS snippet URL 383 | 384 | = Version 2.1.1 (11th October 2017) = 385 | * Fixed location of the Rollbar JS snippet 386 | 387 | = Version 2.1.0 (11th October 2017) = 388 | * Added "Send test message to Rollbar" button 389 | * Fixed the plugin's name inconsistency between WordPress plugin directory and composer. 390 | 391 | = Version 2.0.1 (6th October 2017) = 392 | * Fixed RollbarJsHelper class loading bug in src/Plugin.php (https://github.com/rollbar/rollbar-php-wordpress/issues/23) 393 | 394 | = Version 2.0.0 (9th September 2017) = 395 | * Added support for the WP_ENV environment variable 396 | * Organized the code into namespaces 397 | * Moved helper functions into static methods 398 | * Updated Rollbar PHP library 399 | * Included dependencies to make the plugin self-contained when installing through WP plugin directory 400 | * Rewrote readme files 401 | * Made the package composer friendly with composer.json 402 | 403 | = Version 1.0.3 (12th August 2016) = 404 | * Updated rollbar php lib to latest v0.18.2 405 | * Added .pot translation file 406 | * Removed WP.org assets from plugin folder 407 | 408 | = Version 1.0.2 (28th March 2016) = 409 | * Updated rollbar js lib 410 | * Added escaping for setting values 411 | 412 | = Version 1.0.0 (4th November 2015) = 413 | * Initial release! 414 | -------------------------------------------------------------------------------- /src/Settings.php: -------------------------------------------------------------------------------- 1 | fetchSettings(); 49 | $this->hooks(); 50 | } 51 | 52 | /** 53 | * Register the event handlers for the settings. 54 | * 55 | * @return void 56 | */ 57 | private function hooks(): void 58 | { 59 | add_filter('pre_update_option_rollbar_wp', $this->preUpdate(...)); 60 | } 61 | 62 | /** 63 | * Returns an array of {@see Setting} that can be used in the plugin. 64 | * 65 | * @return array 66 | */ 67 | public static function settings(): array 68 | { 69 | static $settings; 70 | if (isset($settings)) { 71 | return $settings; 72 | } 73 | $env = Settings::getEnvironment(null); 74 | 75 | $defaults = Defaults::get(); 76 | 77 | $settings = [ 78 | 'php_logging_enabled' => new Setting( 79 | id: 'php_logging_enabled', 80 | type: SettingType::Boolean, 81 | default: false, 82 | section: 'rollbar_wp_general', 83 | alwaysSave: true, 84 | ), 85 | 'server_side_access_token' => new Setting( 86 | id: 'server_side_access_token', 87 | type: SettingType::Text, 88 | default: '', 89 | section: 'rollbar_wp_general', 90 | alwaysSave: true, 91 | ), 92 | 'js_logging_enabled' => new Setting( 93 | id: 'js_logging_enabled', 94 | type: SettingType::Boolean, 95 | default: false, 96 | section: 'rollbar_wp_general', 97 | alwaysSave: true, 98 | ), 99 | 'client_side_access_token' => new Setting( 100 | id: 'client_side_access_token', 101 | type: SettingType::Text, 102 | default: '', 103 | section: 'rollbar_wp_general', 104 | alwaysSave: true, 105 | ), 106 | 'environment' => new Setting( 107 | id: 'environment', 108 | type: SettingType::Text, 109 | helpText: '

WP_ENV environment variable: ' . $env . '

' . 110 | '

Rollbar for WordPress honors the WP_ENV environment variable. ' . 111 | 'If the environment setting is not specified here, it will take ' . 112 | 'precendence over the default value.

', 113 | default: 'production', 114 | section: 'rollbar_wp_general', 115 | alwaysSave: true, 116 | ), 117 | 'included_errno' => new Setting( 118 | id: 'included_errno', 119 | type: SettingType::Select, 120 | label: 'Logging Level', 121 | default: E_ERROR, 122 | options: [ 123 | E_ERROR => 'Fatal run-time errors (E_ERROR) only', 124 | E_WARNING => 'Run-time warnings (E_WARNING) and above', 125 | E_PARSE => 'Compile-time parse errors (E_PARSE) and above', 126 | E_NOTICE => 'Run-time notices (E_NOTICE) and above', 127 | E_USER_ERROR => 'User-generated error messages (E_USER_ERROR) and above', 128 | E_USER_WARNING => 'User-generated warning messages (E_USER_WARNING) and above', 129 | E_USER_NOTICE => 'User-generated notice messages (E_USER_NOTICE) and above', 130 | E_STRICT => 'Suggest code changes to ensure forward compatibility (E_STRICT) and above', 131 | E_DEPRECATED => 'Warnings about code that won\'t work in future versions (E_DEPRECATED) and above', 132 | E_ALL => 'Absolutely everything (E_ALL)', 133 | ], 134 | section: 'rollbar_wp_general', 135 | alwaysSave: true, 136 | ), 137 | 'agent_log_location' => new Setting( 138 | id: 'agent_log_location', 139 | type: SettingType::Text, 140 | default: $defaults->agentLogLocation(), 141 | ), 142 | 'allow_exec' => new Setting( 143 | id: 'allow_exec', 144 | type: SettingType::Boolean, 145 | default: $defaults->allowExec(), 146 | ), 147 | 'autodetect_branch' => new Setting( 148 | id: 'autodetect_branch', 149 | type: SettingType::Boolean, 150 | default: $defaults->autodetectBranch(), 151 | ), 152 | 'branch' => new Setting( 153 | id: 'branch', 154 | type: SettingType::Text, 155 | default: $defaults->branch(), 156 | ), 157 | 'capture_error_stacktraces' => new Setting( 158 | id: 'capture_error_stacktraces', 159 | type: SettingType::Boolean, 160 | default: $defaults->captureErrorStacktraces(), 161 | ), 162 | 'code_version' => new Setting( 163 | id: 'code_version', 164 | type: SettingType::Text, 165 | default: $defaults->codeVersion(), 166 | ), 167 | 'endpoint' => new Setting( 168 | id: 'endpoint', 169 | type: SettingType::Text, 170 | default: $defaults->endpoint(), 171 | ), 172 | 'fluent_host' => new Setting( 173 | id: 'fluent_host', 174 | type: SettingType::Text, 175 | default: $defaults->fluentHost(), 176 | ), 177 | 'fluent_port' => new Setting( 178 | id: 'fluent_port', 179 | type: SettingType::Text, 180 | default: $defaults->fluentPort(), 181 | ), 182 | 'fluent_tag' => new Setting( 183 | id: 'fluent_tag', 184 | type: SettingType::Text, 185 | default: $defaults->fluentTag(), 186 | ), 187 | 'handler' => new Setting( 188 | id: 'handler', 189 | type: SettingType::Select, 190 | default: $defaults->handler(), 191 | options: [ 192 | 'blocking' => 'blocking', 193 | 'agent' => 'agent', 194 | 'fluent' => 'fluent', 195 | ], 196 | ), 197 | 'host' => new Setting( 198 | id: 'host', 199 | type: SettingType::Text, 200 | default: $defaults->host(), 201 | ), 202 | 'include_error_code_context' => new Setting( 203 | id: 'include_error_code_context', 204 | type: SettingType::Boolean, 205 | default: $defaults->includeErrorCodeContext(), 206 | ), 207 | 'include_exception_code_context' => new Setting( 208 | id: 'include_exception_code_context', 209 | type: SettingType::Boolean, 210 | default: $defaults->includeExceptionCodeContext(), 211 | ), 212 | 'include_raw_request_body' => new Setting( 213 | id: 'include_raw_request_body', 214 | type: SettingType::Boolean, 215 | default: $defaults->rawRequestBody(), 216 | ), 217 | 'local_vars_dump' => new Setting( 218 | id: 'local_vars_dump', 219 | type: SettingType::Boolean, 220 | default: $defaults->localVarsDump(), 221 | ), 222 | 'log_payload' => new Setting( 223 | id: 'log_payload', 224 | type: SettingType::Boolean, 225 | default: $defaults->logPayload(), 226 | ), 227 | 'max_items' => new Setting( 228 | id: 'max_items', 229 | type: SettingType::Integer, 230 | default: $defaults->maxItems(), 231 | ), 232 | 'max_nesting_depth' => new Setting( 233 | id: 'max_nesting_depth', 234 | type: SettingType::Integer, 235 | default: $defaults->maxNestingDepth(), 236 | ), 237 | 'minimum_level' => new Setting( 238 | id: 'minimum_level', 239 | type: SettingType::Select, 240 | default: $defaults->minimumLevel(), 241 | options: [ 242 | 100000 => Level::EMERGENCY . ', ' . Level::ALERT . ', ' . Level::CRITICAL, 243 | 10000 => Level::ERROR . ', ' . Level::WARNING, 244 | 1000 => Level::WARNING, 245 | 100 => Level::NOTICE . ', ' . Level::INFO, 246 | 10 => Level::DEBUG, 247 | ], 248 | ), 249 | 'enable_person_reporting' => new Setting( 250 | id: 'enable_person_reporting', 251 | type: SettingType::Boolean, 252 | helpText: 'Adds the current user to the payload as a person. This will allow Rollbar to display the ' 253 | . 'user\'s details in the Rollbar UI.', 254 | default: false, 255 | ), 256 | 'capture_ip' => new Setting( 257 | id: 'capture_ip', 258 | type: SettingType::Boolean, 259 | label: 'Capture IP Address', 260 | default: $defaults->captureIp(), 261 | ), 262 | 'capture_email' => new Setting( 263 | id: 'capture_email', 264 | type: SettingType::Boolean, 265 | default: $defaults->captureEmail(), 266 | ), 267 | 'capture_username' => new Setting( 268 | id: 'capture_username', 269 | type: SettingType::Boolean, 270 | default: $defaults->captureUsername(), 271 | ), 272 | 'proxy' => new Setting( 273 | id: 'proxy', 274 | type: SettingType::Text, 275 | default: '', 276 | ), 277 | 'raise_on_error' => new Setting( 278 | id: 'raise_on_error', 279 | type: SettingType::Boolean, 280 | default: $defaults->raiseOnError(), 281 | ), 282 | 'report_suppressed' => new Setting( 283 | id: 'report_suppressed', 284 | type: SettingType::Boolean, 285 | default: $defaults->reportSuppressed(), 286 | ), 287 | 'root' => new Setting( 288 | id: 'root', 289 | type: SettingType::Text, 290 | default: ABSPATH, 291 | ), 292 | 'send_message_trace' => new Setting( 293 | id: 'send_message_trace', 294 | type: SettingType::Boolean, 295 | default: $defaults->sendMessageTrace(), 296 | ), 297 | 'timeout' => new Setting( 298 | id: 'timeout', 299 | type: SettingType::Integer, 300 | default: $defaults->timeout(), 301 | ), 302 | 'transmit' => new Setting( 303 | id: 'transmit', 304 | type: SettingType::Boolean, 305 | default: $defaults->transmit(), 306 | ), 307 | 'use_error_reporting' => new Setting( 308 | id: 'use_error_reporting', 309 | type: SettingType::Boolean, 310 | default: $defaults->useErrorReporting(), 311 | ), 312 | 'verbose' => new Setting( 313 | id: 'verbose', 314 | type: SettingType::Select, 315 | default: $defaults->verbose(), 316 | options: [ 317 | LogLevel::EMERGENCY => LogLevel::EMERGENCY, 318 | LogLevel::ALERT => LogLevel::ALERT, 319 | LogLevel::CRITICAL => LogLevel::CRITICAL, 320 | LogLevel::ERROR => LogLevel::ERROR, 321 | LogLevel::WARNING => LogLevel::WARNING, 322 | LogLevel::NOTICE => LogLevel::NOTICE, 323 | LogLevel::INFO => LogLevel::INFO, 324 | LogLevel::DEBUG => LogLevel::DEBUG, 325 | ], 326 | ), 327 | 'enable_must_use_plugin' => new Setting( 328 | id: 'enable_must_use_plugin', 329 | type: SettingType::Boolean, 330 | helpText: 'Allows Rollbar plugin to be loaded as early as possible as a Must-Use plugin. Activating / ' 331 | . 'deactivating the plugin in the plugins admin panel won\'t have an effect as long as this option ' 332 | . 'in enabled.', 333 | default: false, 334 | ), 335 | 'enable_telemetry_listener' => new Setting( 336 | id: 'enable_telemetry_listener', 337 | type: SettingType::Boolean, 338 | helpText: 'Enables the collection of telemetry data to help debug issues.', 339 | default: true, 340 | section: 'rollbar_wp_telemetry', 341 | ), 342 | 'include_items_in_telemetry' => new Setting( 343 | id: 'include_items_in_telemetry', 344 | type: SettingType::Boolean, 345 | helpText: 'Include Rollbar captured exceptions and messages in the telemetry data of future exceptions ' 346 | . 'and messages.', 347 | default: true, 348 | section: 'rollbar_wp_telemetry', 349 | ), 350 | 'include_ignored_items_in_telemetry' => new Setting( 351 | id: 'include_ignored_items_in_telemetry', 352 | type: SettingType::Boolean, 353 | helpText: 'Include Rollbar captured exceptions and messages in the telemetry data of future exceptions ' 354 | . 'and messages even if they they are ignored because they have a lower level.', 355 | default: false, 356 | section: 'rollbar_wp_telemetry', 357 | ), 358 | 'telemetry_hooks' => new Setting( 359 | id: 'telemetry_hooks', 360 | type: SettingType::CheckBox, 361 | label: 'Enable Telemetry Actions', 362 | helpText: 'Adds a telemetry event for each action that is hooked enabled in this list.', 363 | default: array_keys(Listener::DEFAULT_ACTIONS), 364 | options: array_combine(array_keys(Listener::ALL_ACTIONS), array_keys(Listener::ALL_ACTIONS)), 365 | section: 'rollbar_wp_telemetry', 366 | inputArgs: [ 367 | 'sort' => true, 368 | ], 369 | ), 370 | // The following fields are intentionally left out of the Admin UI. They will be set later in the plugin or 371 | // can be programmed by an end user in their WP site. 372 | 'access_token' => null, 373 | 'ca_cert_path' => null, 374 | 'check_ignore' => null, 375 | 'custom' => null, 376 | 'custom_data_method' => null, 377 | 'custom_truncation' => null, 378 | 'base_api_url' => null, 379 | 'enabled' => null, 380 | 'error_sample_rates' => null, 381 | 'exception_sample_rates' => null, 382 | 'log_payload_logger' => null, 383 | 'person' => null, 384 | 'person_fn' => null, 385 | 'scrubber' => null, 386 | 'scrub_fields' => null, 387 | 'scrub_safelist' => null, 388 | 'transformer' => null, 389 | 'telemetry' => null, 390 | 'verbose_logger' => null, 391 | ]; 392 | return $settings; 393 | } 394 | 395 | /** 396 | * Returns the type of the setting, otherwise null if the key is not found, or it is skipped. 397 | * 398 | * @param string $key The setting key. 399 | * @return SettingType|null 400 | */ 401 | public static function getSettingType(string $key): null|SettingType 402 | { 403 | return self::settings()[$key]?->type ?? null; 404 | } 405 | 406 | /** 407 | * Returns the default value for a setting. 408 | * 409 | * @param string $setting 410 | * @return mixed 411 | */ 412 | public static function getDefaultOption(string $setting): mixed 413 | { 414 | return self::settings()[$setting]?->default ?? null; 415 | } 416 | 417 | /** 418 | * The filter applied before the `rollbar_wp` settings are saved to the DB. 419 | * 420 | * @param array $settings The array of settings. 421 | * @return array 422 | */ 423 | public static function preUpdate(array $settings): array 424 | { 425 | // Empty checkboxes don't get sent in the $_POST. Fill missing boolean settings with default values. 426 | foreach (Settings::settings() as $setting) { 427 | if (null == $setting || isset($settings[$setting->id]) || SettingType::Boolean !== $setting->type) { 428 | continue; 429 | } 430 | $settings[$setting->id] = false; 431 | } 432 | 433 | $settings['enabled'] = $settings['php_logging_enabled'] ?? false; 434 | 435 | if (isset($settings['enable_must_use_plugin']) && $settings['enable_must_use_plugin']) { 436 | try { 437 | Plugin::getInstance()->enableMustUsePlugin(); 438 | } catch (\Exception $exception) { 439 | add_action('admin_notices', function () { 440 | echo '

Error:' 441 | . 'failed to enable the Rollbar Must-Use plugin.

'; 442 | }); 443 | $settings['enable_must_use_plugin'] = false; 444 | } 445 | } else { 446 | try { 447 | Plugin::getInstance()->disableMustUsePlugin(); 448 | } catch (\Exception $exception) { 449 | add_action('admin_notices', function () { 450 | echo '

Error:' 451 | . 'failed to disable the Rollbar Must-Use plugin.

'; 452 | }); 453 | $settings['enable_must_use_plugin'] = true; 454 | } 455 | } 456 | 457 | // Don't store default values in the database, so future updates to default values in the SDK get propagated. 458 | foreach ($settings as $setting_name => $setting_value) { 459 | // Always save settings that are marked as alwaysSave. 460 | $settingConfig = self::settings()[$setting_name] ?? null; 461 | if (null !== $settingConfig && $settingConfig->alwaysSave) { 462 | continue; 463 | } 464 | // Loose comparison to allow for boolean values to be set to 0 or 1 and integers to be strings, which is how 465 | // they are posted via HTTP. 466 | if ($setting_value == Plugin::getInstance()->settingsInstance()->getDefaultOption($setting_name)) { 467 | unset($settings[$setting_name]); 468 | } 469 | } 470 | 471 | return $settings; 472 | } 473 | 474 | /** 475 | * Returns all the settings as an array. 476 | * 477 | * @return array 478 | */ 479 | public function getAll(): array 480 | { 481 | return $this->settings; 482 | } 483 | 484 | /** 485 | * Returns a single setting, otherwise null if the $name is invalid. 486 | * 487 | * @param string $name The setting name. 488 | * @return mixed 489 | */ 490 | public function get(string $name): mixed 491 | { 492 | return $this->settings[$name] ?? null; 493 | } 494 | 495 | /** 496 | * Sets a value to the setting of the given name. 497 | * 498 | * @param string $name The name of the setting. 499 | * @param mixed $value The value of the setting. 500 | * @return void 501 | */ 502 | public function set(string $name, mixed $value): void 503 | { 504 | $this->settings[$name] = $value; 505 | } 506 | 507 | /** 508 | * Save settings to the DB. 509 | * 510 | * @param array $settings 511 | * @return void 512 | */ 513 | public function saveSettings(array $settings): void 514 | { 515 | $option = get_option(self::OPTIONS_KEY); 516 | 517 | $option = array_merge($option, $settings); 518 | 519 | foreach ($settings as $setting => $value) { 520 | $this->settings[$setting] = $value; 521 | } 522 | 523 | update_option(self::OPTIONS_KEY, $option); 524 | } 525 | 526 | /** 527 | * Sets all the settings back to the default value. 528 | * 529 | * @return void 530 | */ 531 | public function restoreDefaults(): void 532 | { 533 | $settings = []; 534 | 535 | foreach (Settings::listOptions() as $option) { 536 | $settings[$option] = $this->getDefaultOption($option); 537 | } 538 | 539 | $this->saveSettings($settings); 540 | } 541 | 542 | /** 543 | * Returns the array of option names for the Rollbar configs. 544 | * 545 | * @return string[] 546 | */ 547 | public static function listOptions(): array 548 | { 549 | return array_merge( 550 | Config::listOptions(), 551 | array_keys(self::settings()), 552 | ); 553 | } 554 | 555 | /** 556 | * Returns a reasonable boolean from a given value. 557 | * 558 | * @param mixed $value 559 | * @return bool 560 | */ 561 | public static function toBoolean(mixed $value): bool 562 | { 563 | if (is_bool($value)) { 564 | return $value; 565 | } 566 | if (is_string($value)) { 567 | return in_array(strtolower($value), ['true', '1', 'yes', 'on']); 568 | } 569 | if (is_int($value)) { 570 | return $value !== 0; 571 | } 572 | return boolval($value); 573 | } 574 | 575 | /** 576 | * Returns a reasonable integer from a given value. 577 | * 578 | * @param mixed $value The value to convert to an integer. 579 | * @return int 580 | */ 581 | public static function toInteger(mixed $value): int 582 | { 583 | if (is_int($value)) { 584 | return $value; 585 | } 586 | if (is_string($value)) { 587 | return intval($value); 588 | } 589 | if (is_bool($value)) { 590 | return $value ? 1 : 0; 591 | } 592 | if (is_numeric($value)) { 593 | return intval($value); 594 | } 595 | return 0; 596 | } 597 | 598 | /** 599 | * Returns a reasonable string from a given value. 600 | * 601 | * @param mixed $value The value to convert to a string. 602 | * @return string 603 | */ 604 | public static function toString(mixed $value): string 605 | { 606 | if (is_string($value)) { 607 | return $value; 608 | } 609 | if (is_bool($value)) { 610 | return $value ? 'true' : 'false'; 611 | } 612 | if (is_int($value)) { 613 | return (string) $value; 614 | } 615 | if (is_numeric($value)) { 616 | return (string) $value; 617 | } 618 | return ''; 619 | } 620 | 621 | /** 622 | * Returns an array of strings from a given value. 623 | * 624 | * @param mixed $value The value to convert to an array of strings. 625 | * @return string[] 626 | */ 627 | public static function toStringArray(mixed $value): array 628 | { 629 | if (is_array($value)) { 630 | return array_map(fn($v) => self::toString($v), $value); 631 | } 632 | if (is_string($value)) { 633 | return [$value]; 634 | } 635 | return []; 636 | } 637 | 638 | /** 639 | * Fetch settings provided in Admin -> Tools -> Rollbar 640 | * 641 | * @returns void 642 | */ 643 | private function fetchSettings(): void 644 | { 645 | $options = $this->settings; 646 | 647 | if (!Plugin::disabledAdmin() && $saved_options = get_option(self::OPTIONS_KEY)) { 648 | $options = array_merge($options, $saved_options); 649 | } 650 | if (defined('ROLLBAR_SETTINGS') && is_array(constant('ROLLBAR_SETTINGS'))) { 651 | $options = array_merge($options, constant('ROLLBAR_SETTINGS')); 652 | } 653 | 654 | $options['environment'] = self::getEnvironment($options['environment'] ?? null); 655 | $options['proxy'] = self::getProxySettings($options['proxy'] ?? null); 656 | $options['server_side_access_token'] = self::getServerAccessToken( 657 | $options['server_side_access_token'] ?? null, 658 | ); 659 | $options['client_side_access_token'] = self::getClientAccessToken( 660 | $options['client_side_access_token'] ?? null, 661 | ); 662 | 663 | $this->settings = self::normalizeSettings($options); 664 | } 665 | 666 | /** 667 | * Normalizes the settings array to ensure all required fields are set and properly formatted. 668 | * 669 | * @param array $options 670 | * @return array 671 | */ 672 | public static function normalizeSettings(array $options): array 673 | { 674 | $settings = [ 675 | 'php_logging_enabled' => (!empty($options['php_logging_enabled'])) ? 1 : 0, 676 | 'js_logging_enabled' => (!empty($options['js_logging_enabled'])) ? 1 : 0, 677 | 'server_side_access_token' => (!empty($options['server_side_access_token'])) ? 678 | esc_attr(trim($options['server_side_access_token'])) : 679 | '', 680 | 'client_side_access_token' => (!empty($options['client_side_access_token'])) ? 681 | esc_attr(trim($options['client_side_access_token'])) : 682 | '', 683 | 'included_errno' => (!empty($options['included_errno'])) ? 684 | esc_attr(trim($options['included_errno'])) : 685 | self::getDefaultOption('included_errno'), 686 | ]; 687 | 688 | // Filter out options that are not in the list of options. 689 | $options = array_intersect_key($options, array_flip(self::listOptions())); 690 | 691 | foreach (self::settings() as $key => $setting) { 692 | // 'access_token' and 'enabled' are different in the WordPress plugin look for 'server_side_access_token' 693 | // and 'php_logging_enabled' above. 694 | if (in_array($key, ['access_token', 'enabled'])) { 695 | continue; 696 | } 697 | 698 | $value = $options[$key] ?? ($setting?->default ?? null); 699 | if ($setting !== null) { 700 | $value = $setting->coerceValue($value); 701 | } elseif (!isset($options[$key])) { 702 | continue; 703 | } 704 | $settings[$key] = $value; 705 | } 706 | 707 | /** 708 | * Filter the Rollbar plugin settings. 709 | * 710 | * @param array $settings The Rollbar plugin settings array. 711 | * @since 3.0.0 712 | * 713 | */ 714 | return apply_filters('rollbar_plugin_settings', $settings); 715 | } 716 | 717 | /** 718 | * Returns the provided environment if set, otherwise retrieves it from the system config or environment. 719 | * 720 | * @param string|null $environment The current environment value. 721 | * @return string|null 722 | */ 723 | public static function getEnvironment(string|null $environment): string|null 724 | { 725 | if (null !== $environment) { 726 | $environment = trim($environment); 727 | } 728 | if (empty($environment)) { 729 | $environment = null; 730 | } 731 | $environment = trim(self::getSystemSetting('WP_ENV', $environment) ?? ''); 732 | if (!empty($environment)) { 733 | return $environment; 734 | } 735 | return trim(self::getSystemSetting('WP_ENVIRONMENT_TYPE', $environment) ?? ''); 736 | } 737 | 738 | /** 739 | * The default `person_fn` method. 740 | * 741 | * @return array|null 742 | */ 743 | public static function getPersonFunction(): null|array 744 | { 745 | $user = wp_get_current_user(); 746 | if (!$user) { 747 | return null; 748 | } 749 | return [ 750 | 'id' => $user->ID, 751 | 'username' => $user->user_login, 752 | 'email' => $user->user_email, 753 | ]; 754 | } 755 | 756 | /** 757 | * Returns the provided client access token if set, otherwise retrieves it from the system config or environment. 758 | * 759 | * @param string|null $token The current access token value. 760 | * @return string|null The access token to use for the frontend. 761 | */ 762 | private static function getClientAccessToken(string|null $token): string|null 763 | { 764 | if (!empty($token)) { 765 | $token = trim($token); 766 | } 767 | if (empty($token)) { 768 | $token = null; 769 | } 770 | return trim(self::getSystemSetting('ROLLBAR_CLIENT_ACCESS_TOKEN', $token) ?? ''); 771 | } 772 | 773 | /** 774 | * Returns the provided server access token if set, otherwise retrieves it from the system config or environment. 775 | * 776 | * @param string|null $token The current access token value. 777 | * @return string|null The access token to use server-side. 778 | */ 779 | private static function getServerAccessToken(string|null $token): string|null 780 | { 781 | if (!empty($token)) { 782 | $token = trim($token); 783 | } 784 | if (empty($token)) { 785 | $token = null; 786 | } 787 | return trim(self::getSystemSetting('ROLLBAR_ACCESS_TOKEN', $token) ?? ''); 788 | } 789 | 790 | /** 791 | * Retrieves the specified setting from either a constant or environment variable. 792 | * 793 | * If the default is provided, it will be used if the constant does not exist. If the default is `null`, the 794 | * environment variable will be used if it exists. Otherwise, `null` will be returned. 795 | * 796 | * @param string $key The name of the constant or environment variable. 797 | * @param mixed $default The default value to use if the setting is not found. 798 | * @return mixed The value of the constant, environment variable, the default, or null. 799 | */ 800 | private static function getSystemSetting(string $key, mixed $default = null): mixed 801 | { 802 | if (defined($key)) { 803 | return constant($key); 804 | } 805 | if (null !== $default) { 806 | return $default; 807 | } 808 | if ($value = getenv($key)) { 809 | return $value; 810 | } 811 | return $default; 812 | } 813 | 814 | /** 815 | * Retrieves the proxy settings based on provided input or system config. 816 | * 817 | * @param string|array|null $proxy The proxy settings as string, array, or null. If null or empty, attempts to fetch 818 | * system-defined proxy constants. 819 | * 820 | * @return string|array|null The proxy settings in string, array, or null if no proxy is defined or enabled. 821 | */ 822 | private function getProxySettings(string|array|null $proxy): string|array|null 823 | { 824 | if (!empty($proxy)) { 825 | return $proxy; 826 | } 827 | $proxy = new WP_HTTP_Proxy(); 828 | if (!$proxy->is_enabled() || !$proxy->send_through_proxy('https://api.rollbar.com/api')) { 829 | return null; 830 | } 831 | $proxySettings = [ 832 | 'address' => $proxy->host() . ':' . $proxy->port(), 833 | ]; 834 | if ($proxy->username() && $proxy->password()) { 835 | $proxySettings['username'] = $proxy->username(); 836 | $proxySettings['password'] = $proxy->password(); 837 | } 838 | return $proxySettings; 839 | } 840 | } 841 | --------------------------------------------------------------------------------