0, 'paste_raw' => '', 'placement' => 'body', 'sidebar' => '', 'exclude_logged_in' => 1, 'badge' => 'floating_right', ]; } function statjolt_options($reset = FALSE) { static $o = NULL; if ($reset) $o = NULL; if ($o !== NULL) return $o; $o = array_merge(statjolt_defaults(), (array)get_option('statjolt', [])); if (is_multisite() && (int)$o['user_id'] < 1) { $net = (array)get_site_option('statjolt', []); if (!empty($net['user_id'])) $o['user_id'] = $net['user_id']; if (!empty($net['badge'])) $o['badge'] = $net['badge']; } $o['user_id'] = (int)$o['user_id']; return $o; } // For links pointing at statjolt.com. function statjolt_site_lang() { $lang = substr(get_user_locale(), 0, 2); return in_array($lang, explode(',', STATJOLT_SITE_LANGS), TRUE) ? $lang : 'en'; } // A floating badge is position:fixed, so placement has no effect on how it looks. function statjolt_badge_floats() { return !in_array(statjolt_options()['badge'], ['inline', 'row_centered'], TRUE); } function statjolt_should_track() { static $memo = NULL; if ($memo !== NULL) return $memo; $memo = FALSE; $opt = statjolt_options(); if ($opt['user_id'] < 1) return FALSE; if (is_admin() || is_feed() || is_embed() || is_robots() || is_preview()) return FALSE; // DOING_CRON rather than wp_doing_cron(), which arrived in WP 4.8 and would raise the // minimum for no gain. The function is only a filtered wrapper around this constant. if (wp_doing_ajax() || (defined('DOING_CRON') && DOING_CRON) || is_customize_preview()) return FALSE; if (defined('REST_REQUEST') && REST_REQUEST) return FALSE; if (defined('WP_CLI') && WP_CLI) return FALSE; // No wp_is_json_request() here: it needs WP 5.0 and it is redundant. REST and ajax are // already excluded above and the buffer only touches output that contains '."\n"; if (!$echo) return $html; echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above return ''; } add_shortcode('statjolt_badge', function () { return statjolt_block(FALSE); }); add_action('dynamic_sidebar_after', function ($index) { $opt = statjolt_options(); if ($opt['placement'] === 'sidebar' && $opt['sidebar'] !== '' && $index === $opt['sidebar'] && !statjolt_badge_floats()) statjolt_block(); }); // Tier 1. Nearly every theme calls wp_footer, so this is where the block normally lands. // The wrapper is required: do_action() passes an empty string when it has no arguments, // which would make $echo falsy and return the block instead of printing it. add_action('wp_footer', function () { statjolt_block(); }, PHP_INT_MAX); // Tiers 2 and 3, decided at flush time when we already know whether wp_footer fired. // No probe and no setting: if the theme never printed the block, put it before the last // and if there is not even one, put it at the very end. // Uses the core template output buffer (WP 6.9+). Core only starts it if this filter is // registered before the template is included, so it is added here. Older WP: tier 1 only. add_action('template_redirect', function () { if (!function_exists('wp_should_output_buffer_template_for_enhancement') || !statjolt_should_track()) return; add_filter('wp_template_enhancement_output_buffer', function ($html) { if (!empty($GLOBALS['statjolt_printed'])) return $html; // Without this guard tier 3 would append the block to JSON and XML responses. if (stripos($html, ''); if ($pos === FALSE) return $html.statjolt_block(FALSE); return substr($html, 0, $pos).statjolt_block(FALSE).substr($html, $pos); }); }, PHP_INT_MAX); // The bundled files are only a fallback. A language pack from translate.wordpress.org wins. add_action('init', function () { load_plugin_textdomain('statjolt-analytics', FALSE, dirname(plugin_basename(__FILE__)).'/languages'); }); register_activation_hook(__FILE__, function () { set_transient('statjolt_activated', 1, 60); }); if (is_admin()) require __DIR__.'/admin.php';