init();
}
/**
* Filters & Actions.
*
* @return void
*/
private function init() {
add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_install_module' ], 9, 2 );
add_filter( 'pre_update_option_plausible_analytics_settings', [ $this, 'maybe_enable_proxy' ], 10, 2 );
}
/**
* Decide whether we should install the module, or not.
*
* @param array $settings Current settings, already written to the DB.
*
* @return void
* @since 1.3.0
*
*/
public function maybe_install_module( $old_settings, $settings ) {
$settings_proxy = ( is_array( $settings ) && isset( $settings['proxy_enabled'] ) ) ? $settings['proxy_enabled'] : '';
$old_proxy = ( is_array( $old_settings ) && isset( $old_settings['proxy_enabled'] ) ) ? $old_settings['proxy_enabled'] : '';
if ( $settings_proxy === 'on' && $old_proxy !== 'on' ) {
$this->install();
} elseif ( $settings_proxy === '' && $old_proxy === 'on' ) {
$this->uninstall();
}
}
/**
* Takes care of installing the M(ust)U(se) plugin when the Proxy is enabled.
*
* @return void.
* @since 1.3.0
*/
public function install() {
if ( ! current_user_can( 'install_plugins' ) ) {
return; // @codeCoverageIgnore
}
if ( ! function_exists( 'WP_Filesystem' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' ); // @codeCoverageIgnore
}
WP_Filesystem();
if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
wp_mkdir_p( WPMU_PLUGIN_DIR );
add_option( 'plausible_analytics_created_mu_plugins_dir', true );
}
if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
$this->show_module_not_installed_error(); // @codeCoverageIgnore
}
$results = copy_dir( PLAUSIBLE_ANALYTICS_PLUGIN_DIR . 'mu-plugin', WPMU_PLUGIN_DIR );
if ( is_wp_error( $results ) ) {
$this->show_module_not_installed_error(); // @codeCoverageIgnore
}
add_option( 'plausible_analytics_proxy_speed_module_installed', true );
}
/**
* @return void
*
* @codeCoverageIgnore
* @since 1.3.0
*/
private function show_module_not_installed_error() {
$message = sprintf(
wp_kses(
// translators: %s: URL to manual proxy speed module installation instructions.
__(
'The proxy is enabled, but the proxy\'s speed module failed to install. Try installing it manually.',
'plausible-analytics'
),
'post'
),
'https://plausible.io/docs/troubleshoot-integration#proxy-script-is-slow'
);
Messages::set_error( $message );
}
/**
* Uninstall the Speed Module, generates JS files and all related settings when the proxy is disabled.
*
* @return void.
* @throws Exception
* @since 1.3.0
*/
public function uninstall() {
if ( ! current_user_can( 'install_plugins' ) ) {
return; // @codeCoverageIgnore
}
/**
* Clean up MU plugin.
*/
$file_path = WP_CONTENT_DIR . '/mu-plugins/plausible-proxy-speed-module.php';
if ( file_exists( $file_path ) ) {
wp_delete_file( $file_path );
}
if ( get_option( 'plausible_analytics_created_mu_plugins_dir' ) && $this->dir_is_empty( WPMU_PLUGIN_DIR ) ) {
rmdir( WPMU_PLUGIN_DIR );
}
/**
* Clean up generated JS files in /uploads dir.
*/
$cache_dir = Helpers::get_proxy_resource( 'cache_dir' );
$js_file = $this->get_filename();
if ( file_exists( $cache_dir . $js_file . '.js' ) ) {
wp_delete_file( $cache_dir . $js_file . '.js' ); // @codeCoverageIgnore
}
if ( $this->dir_is_empty( $cache_dir ) ) {
rmdir( $cache_dir );
}
/**
* Clean up related DB entries.
*/
delete_option( 'plausible_analytics_created_mu_plugins_dir' );
delete_option( 'plausible_analytics_proxy_speed_module_installed' );
delete_option( 'plausible_analytics_proxy_resources' );
}
/**
* Check if a directory is empty.
* This works because a new FilesystemIterator will initially point to the first file in the folder -
* if there are no files in the folder, valid() will return false.
*
* @see https://www.php.net/manual/en/directoryiterator.valid.php
* @since 1.3.0
*
* @param mixed $dir
*
* @return bool
*
* @codeCoverageIgnore Because we don't want to test the filesystem.
*/
protected function dir_is_empty( $dir ) {
$iterator = new \FilesystemIterator( $dir );
return ! $iterator->valid();
}
/**
* @throws Exception
*
* @codeCoverageIgnore because Helpers are tested elsewhere.
*/
protected function get_filename() {
return Helpers::get_filename();
}
/**
* Test the proxy before enabling the option.
*
* @param mixed $settings
*
* @return mixed
* @throws Exception
* @since 1.3.0
*
*/
public function maybe_enable_proxy( $settings, $old_settings ) {
/**
* No need to run this on each update run, or when the proxy is disabled.
*/
$new_proxy_setting = ( is_array( $settings ) && isset( $settings['proxy_enabled'] ) ) ? $settings['proxy_enabled'] : '';
$old_proxy_setting = ( is_array( $old_settings ) && isset( $old_settings['proxy_enabled'] ) ) ? $old_settings['proxy_enabled'] : '';
if ( empty( $new_proxy_setting ) || ( $new_proxy_setting === 'on' && $old_proxy_setting === 'on' ) ) {
return $settings;
}
$is_ssl = $this->is_ssl();
if ( ! $is_ssl ) {
Messages::set_notice(
sprintf(
// translators: %s: URL to list of potential proxy solutions.
__(
'Please check that your proxy is functioning correctly. If you encounter any issues with tracking, click here for a list of potential solutions',
'plausible-analytics'
),
'https://plausible.io/docs/troubleshoot-integration#proxy-not-recording-stats-after-setup'
)
);
}
$test_succeeded = $this->test_proxy( Helpers::proxy_enabled( $settings ) && wp_doing_ajax() );
if ( ! $test_succeeded ) {
// @codeCoverageIgnoreStart
Messages::set_error(
sprintf(
wp_kses(
// translators: 1: Proxy endpoint URL, 2: URL to Plausible support.
__(
'Plausible\'s proxy couldn\'t be enabled, because the WordPress API is inaccessible. This might be due to a conflicting setting in a (security) plugin or server firewall. Make sure you whitelist requests to the Proxy\'s endpoint: %1$s. Contact support if you need help locating the issue.',
'plausible-analytics'
),
'post'
),
Helpers::get_rest_endpoint( false ),
'https://plausible.io/contact'
)
);
// Disable the proxy.
return $old_settings;
// @codeCoverageIgnoreEnd
}
return $settings;
}
/**
* is_ssl() only checks the current scheme that is used, which fails in a Nginx Reverse Proxy configuration (where the scheme is HTTP behind the
* proxy), this function is a custom wrapper which also checks the WordPress configuration for the presence of "https" in the configured Home
* URL.
*
* @return bool
*/
private function is_ssl() {
return strpos( get_home_url(), 'https' ) !== false || is_ssl();
}
/**
* Runs a quick internal call to the WordPress API to make sure it's accessible.
*
* @return bool
* @throws Exception
* @since 1.3.0
*/
private function test_proxy( $run = true ) {
// Always succeed if this is a CI environment.
if ( defined( 'PLAUSIBLE_CI' ) ) {
return true;
}
// Should we run the test?
if ( ! apply_filters( 'plausible_analytics_module_run_test_proxy', $run ) ) {
return false; // @codeCoverageIgnore
}
$proxy = new Proxy( false );
$result = $proxy->do_request( 'pageview', 'plausible.test', 'https://plausible.test/test' );
return wp_remote_retrieve_response_code( $result ) === 202;
}
}