Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/coding-standards.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Coding Standards

# When to run.
on:
pull_request:
types:
- opened
- synchronize
push:
branches:
- master

jobs:
coding-standards:
name: Coding Standards / PHP ${{ matrix.php-versions }}
runs-on: ubuntu-latest

strategy:
matrix:
php-versions: [ '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]

steps:
- name: Checkout Plugin
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: none
tools: cs2pr

- name: Install Composer dependencies
run: composer update --no-interaction --no-progress

- name: Run PHP Coding Standards
run: vendor/bin/phpcs -q --standard=phpcs.xml --report=checkstyle ./ | cs2pr
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

.svn
**/.DS_Store
composer.lock
/vendor/
22 changes: 22 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "usefathom/fathom-analytics",
"description": "Fathom Analytics for WordPress.",
"type": "wordpress-plugin",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.4"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"wp-coding-standards/wpcs": "^3.0.0"
},
"scripts": {
"phpcs": "vendor/bin/phpcs ./ -s -v",
"phpcbf": "vendor/bin/phpcbf ./ -s -v"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
108 changes: 70 additions & 38 deletions fathom-analytics.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
/*
/**
Plugin Name: Fathom Analytics for WP
Description: Fathom analytics is a simple, GDPR-compliant alternative to Google Analytics.
Author: Conva Ventures Inc
Expand All @@ -23,6 +23,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

const FATHOM_PLUGIN_VERSION = '3.3.1';
const FATHOM_SITE_ID_OPTION_NAME = 'fathom_site_id';
const FATHOM_EXCLUDE_ROLES_OPTION_NAME = 'fathom_exclude_roles';
Expand Down Expand Up @@ -75,10 +80,36 @@ function fathom_get_excluded_roles()
return $excluded_roles;
}

/**
* Sanitize a checkbox option to 1 or 0.
*
* @since 3.3.2
*/
function fathom_sanitize_checkbox( $value )
{
return empty( $value ) ? 0 : 1;
}

/**
* Sanitize the excluded roles option to an array of valid role slugs.
*
* @since 3.3.2
*/
function fathom_sanitize_excluded_roles( $roles )
{
if ( ! is_array( $roles ) ) {
return array();
}

$valid_roles = array_keys( wp_roles()->get_names() );

return array_values( array_intersect( $valid_roles, array_map( 'sanitize_key', $roles ) ) );
}

/**
* Determine if the current user has any of the excluded roles.
*
* @return void
* @return bool
*/
function fathom_is_excluded_from_tracking() {
if ( ! is_user_logged_in() ) {
Expand All @@ -87,7 +118,7 @@ function fathom_is_excluded_from_tracking() {

$user = wp_get_current_user();

return array_intersect( fathom_get_excluded_roles(), $user->roles );
return (bool) array_intersect( fathom_get_excluded_roles(), $user->roles );
}

/**
Expand All @@ -99,7 +130,7 @@ function fathom_enqueue_js_snippet()
return;
}

wp_enqueue_script( 'fathom-snippet', 'https://cdn.usefathom.com/script.js', array(), null, array( 'strategy' => 'defer' ) );
wp_enqueue_script( 'fathom-snippet', 'https://cdn.usefathom.com/script.js', array(), null, array( 'strategy' => 'defer' ) ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- External CDN script, version intentionally omitted.
}

/**
Expand All @@ -116,7 +147,7 @@ function fathom_enqueue_js_snippet()
function fathom_add_data_attributes_to_js_script( $tag, $handle, $src )
{
if ( 'fathom-snippet' === $handle ) {
$attributes = ' data-site="' . fathom_get_site_id() . '" ' . exclude_fathom_script_from_cookiebot();
$attributes = ' data-site="' . esc_attr( fathom_get_site_id() ) . '" ' . exclude_fathom_script_from_cookiebot();

if ( get_option( FATHOM_IGNORE_CANONICAL ) ) {
$attributes .= ' data-canonical="false"';
Expand All @@ -139,7 +170,7 @@ function fathom_add_data_attributes_to_js_script( $tag, $handle, $src )
function fathom_get_menu_icon() {
$icon = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 32 32"><path fill="currentColor" d="M31.076 5.305h-1.127a.927.927 0 0 0-.874.621l-7.027 20.159a.927.927 0 0 0 .874 1.23h1.127a.926.926 0 0 0 .873-.62l7.026-20.16a.926.926 0 0 0-.873-1.23m-25.398 6.41v-.919a.927.927 0 0 0-.926-.926H2.98v-.852c-.003-.995.221-1.591.423-1.808l.01-.012a1.04 1.04 0 0 1 .5-.273 12.087 12.087 0 0 1 1.98-.153h.34a.927.927 0 0 0 .926-.927v-.83a.924.924 0 0 0-.748-.91 9.82 9.82 0 0 0-.377-.064C5.859 4.014 4.941 4 4.761 4H4.75c-1.492 0-2.763.348-3.613 1.261l-.002.002C.304 6.178.004 7.473 0 9.047V26.39a.927.927 0 0 0 .927.927h1.126a.927.927 0 0 0 .927-.927V12.64h1.772a.927.927 0 0 0 .926-.926m11.742-1.286a6.993 6.993 0 0 0-3.598-.855 8.192 8.192 0 0 0-3.59.73 5.763 5.763 0 0 0-2.445 2.186l-.004.006a.799.799 0 0 0-.12.422.845.845 0 0 0 .567.795l.91.323a1.297 1.297 0 0 0 1.44-.407c.085-.105.177-.206.274-.3.595-.578 1.478-.918 2.851-.923a4.958 4.958 0 0 1 1.739.27c.4.145.754.394 1.025.721.515.632.84 1.608.869 3.007-1.324.02-2.498.076-3.512.17a16.505 16.505 0 0 0-3.747.78l-.004.002-.011.003h-.002a5.958 5.958 0 0 0-2.877 1.781 4.826 4.826 0 0 0-1.045 3.17v.03a5.117 5.117 0 0 0 .787 2.836 4.904 4.904 0 0 0 2.2 1.826 7.872 7.872 0 0 0 3.157.58 7.195 7.195 0 0 0 2.433-.454 7.733 7.733 0 0 0 2.39-1.411c.14-.12.271-.246.398-.376.083.54.266 1.06.54 1.533a.92.92 0 0 0 .79.441h.002l.542-.006a.926.926 0 0 0 .915-.927v-9.923a8.683 8.683 0 0 0-.672-3.548 5.346 5.346 0 0 0-2.198-2.482m-.753 11.746a5.459 5.459 0 0 1-1.83 1.907 4.492 4.492 0 0 1-2.468.7c-1.165-.001-1.948-.269-2.476-.705a2.059 2.059 0 0 1-.58-.75 2.672 2.672 0 0 1-.197-1.058c-.007-.425.1-.845.309-1.216a2.477 2.477 0 0 1 1.137-.912 10.438 10.438 0 0 1 2.875-.749 32.23 32.23 0 0 1 3.906-.274v.573a4.846 4.846 0 0 1-.676 2.485"/></svg>';

return sprintf( 'data:image/svg+xml;base64,%s', base64_encode( $icon ) );
return sprintf( 'data:image/svg+xml;base64,%s', base64_encode( $icon ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Encoding an inline SVG for the menu icon.
}

/**
Expand All @@ -163,10 +194,11 @@ function fathom_stats_page() {
function fathom_print_stats_page()
{
if (!empty(get_option(FATHOM_SITE_ID_OPTION_NAME))) {
wp_enqueue_script('fathom-iframresize', plugins_url('iframeResizer.min.js', __FILE__));
wp_enqueue_script('fathom-stats-iframe', plugins_url('fathom-stats-iframe.js', __FILE__));
wp_enqueue_script( 'fathom-iframe-resizer', plugins_url( 'iframeResizer.min.js', __FILE__ ), array(), FATHOM_PLUGIN_VERSION, false );
wp_enqueue_script( 'fathom-stats-iframe', plugins_url( 'fathom-stats-iframe.js', __FILE__ ), array( 'fathom-iframe-resizer' ), FATHOM_PLUGIN_VERSION, false );
echo '<div class="wrap">';
echo '<iframe id="fathom-stats-iframe" src="https://app.usefathom.com/share/' . esc_attr(get_option(FATHOM_SITE_ID_OPTION_NAME)) . '/wordpress?password=' . hash('sha256', get_option(FATHOM_PRIVATE_SHARE_PASSWORD)) . '" style="width: 1px;min-width: 100%; height:1000px; max-width:1100px" frameborder="0" onload=fathomResizeIframe();></iframe>';
$stats_src = 'https://app.usefathom.com/share/' . get_option(FATHOM_SITE_ID_OPTION_NAME) . '/wordpress?password=' . hash('sha256', get_option(FATHOM_PRIVATE_SHARE_PASSWORD));
echo '<iframe id="fathom-stats-iframe" src="' . esc_url($stats_src) . '" style="width: 1px;min-width: 100%; height:1000px; max-width:1100px" frameborder="0" onload="fathomResizeIframe();"></iframe>';
echo '</div>';
} else {
echo '<div class="wrap">You have not configured Fathom. Go to Settings -> Fathom Analytics to configure this page.</div>';
Expand All @@ -183,11 +215,11 @@ function fathom_register_settings()
add_settings_section( 'default', "Fathom Analytics", 'fathom_settings_intro', 'fathom-analytics' );

// register options
register_setting( 'fathom', FATHOM_SITE_ID_OPTION_NAME, array( 'type' => 'string' ) );
register_setting( 'fathom', FATHOM_PRIVATE_SHARE_PASSWORD, array( 'type' => 'string' ) );
register_setting( 'fathom', FATHOM_EXCLUDE_ROLES_OPTION_NAME, array( 'type' => 'multi_checkbox' ) );
register_setting( 'fathom', FATHOM_SHOW_ANALYTICS_MENU_ITEM, array( 'type' => 'boolean', 'default' => 1 ) );
register_setting( 'fathom', FATHOM_IGNORE_CANONICAL, array( 'type' => 'boolean', 'default' => 0 ) );
register_setting( 'fathom', FATHOM_SITE_ID_OPTION_NAME, array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ) );
register_setting( 'fathom', FATHOM_PRIVATE_SHARE_PASSWORD, array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ) );
register_setting( 'fathom', FATHOM_EXCLUDE_ROLES_OPTION_NAME, array( 'type' => 'array', 'sanitize_callback' => 'fathom_sanitize_excluded_roles', 'default' => array() ) );
register_setting( 'fathom', FATHOM_SHOW_ANALYTICS_MENU_ITEM, array( 'type' => 'boolean', 'sanitize_callback' => 'fathom_sanitize_checkbox', 'default' => 1 ) );
register_setting( 'fathom', FATHOM_IGNORE_CANONICAL, array( 'type' => 'boolean', 'sanitize_callback' => 'fathom_sanitize_checkbox', 'default' => 0 ) );

// register settings fields
add_settings_field( FATHOM_SITE_ID_OPTION_NAME, __( 'Site ID', 'fathom-analytics' ), 'fathom_print_site_id_setting_field', 'fathom-analytics', 'default');
Expand All @@ -202,33 +234,33 @@ function fathom_register_settings()
*
* @param array $args Display arguments.
*
* @return string
* @return void
*
* @since 3.1.0
*/
function fathom_settings_intro( $args ) {
$intro = sprintf(
'<div class="notice notice-info"><p>%s</p></div>',
__( 'If you are enjoying the Fathom plugin for Wordpress, <a href="https://wordpress.org/support/plugin/fathom-analytics/reviews/#new-post" target="_blank">please leave us a ⭐️⭐️⭐️⭐️⭐️ rating</a>. <strong>Huge</strong> thanks in advance :)', 'fathom-analytics' )
__( 'If you are enjoying the Fathom plugin for WordPress, <a href="https://wordpress.org/support/plugin/fathom-analytics/reviews/#new-post" target="_blank">please leave us a ⭐️⭐️⭐️⭐️⭐️ rating</a>. <strong>Huge</strong> thanks in advance :)', 'fathom-analytics' )
);

if ( get_option( FATHOM_CUSTOM_DOMAIN_OPTION_NAME ) ) {
if ( isset( $_GET['action'] ) && 'remove_custom_domain' === $_GET['action'] ) {
if ( isset( $_GET['action'], $_GET['_wpnonce'] ) && 'remove_custom_domain' === sanitize_text_field( wp_unslash( $_GET['action'] ) ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'fathom_remove_custom_domain' ) ) {
delete_option( FATHOM_CUSTOM_DOMAIN_OPTION_NAME );
} else {
$intro .= sprintf(
'<div class="notice notice-warning"><p>%s</p><p>%s</p></div>',
__( 'As of May 9, 2023, we can no longer support custom domains - you can read more <a target="_blank" href="https://usefathom.com/docs/script/custom-domains">here</a>.', 'fathom-analytics' ),
sprintf(
'<a href="%s" class="button button-secondary" style="flex-shrink:0;">%s</a>',
esc_url( add_query_arg( 'action', 'remove_custom_domain' ) ),
esc_url( wp_nonce_url( add_query_arg( 'action', 'remove_custom_domain' ), 'fathom_remove_custom_domain' ) ),
__( 'Got it!', 'fathom-analytics' )
)
);
}
}

echo $intro; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wp_kses_post( $intro );
}

/**
Expand All @@ -237,7 +269,7 @@ function fathom_settings_intro( $args ) {
function fathom_print_settings_page()
{
echo '<div class="wrap">';
echo sprintf('<form method="POST" action="%s">', esc_attr(admin_url('options.php')));
printf('<form method="POST" action="%s">', esc_url(admin_url('options.php')));
settings_fields('fathom');
do_settings_sections('fathom-analytics');
submit_button();
Expand All @@ -251,8 +283,8 @@ function fathom_print_settings_page()
function fathom_print_display_analytics_menu_setting_field($args = array())
{
$value = get_option(FATHOM_SHOW_ANALYTICS_MENU_ITEM);
echo sprintf('<input type="checkbox" name="%s" id="%s" class="regular-text" ' . (esc_attr($value) ? 'checked' : '') .' />', FATHOM_SHOW_ANALYTICS_MENU_ITEM, FATHOM_SHOW_ANALYTICS_MENU_ITEM);
echo '<p class="description">' . __('Pro: Display the Fathom Tab in the sidebar (This is only available if you have enabled site sharing)', 'fathom-analytics') . '</p>';
printf('<input type="checkbox" name="%s" id="%s" class="regular-text" ' . (esc_attr($value) ? 'checked' : '') .' />', esc_attr( FATHOM_SHOW_ANALYTICS_MENU_ITEM ), esc_attr( FATHOM_SHOW_ANALYTICS_MENU_ITEM ));
echo '<p class="description">' . esc_html__('Pro: Display the Fathom Tab in the sidebar (This is only available if you have enabled site sharing)', 'fathom-analytics') . '</p>';
}

/**
Expand All @@ -267,8 +299,8 @@ function fathom_print_display_analytics_menu_setting_field($args = array())
function fathom_print_ignore_canonical_setting_field($args = array())
{
$value = get_option(FATHOM_IGNORE_CANONICAL);
echo sprintf('<input type="checkbox" name="%s" id="%s" class="regular-text" ' . (esc_attr($value) ? 'checked' : '') .' />', FATHOM_IGNORE_CANONICAL, FATHOM_IGNORE_CANONICAL);
echo '<p class="description">' . __('If there\'s a canonical link in place, we use it instead of the current URL. Check this to use the current URL instead', 'fathom-analytics') . '</p>';
printf('<input type="checkbox" name="%s" id="%s" class="regular-text" ' . (esc_attr($value) ? 'checked' : '') .' />', esc_attr( FATHOM_IGNORE_CANONICAL ), esc_attr( FATHOM_IGNORE_CANONICAL ));
echo '<p class="description">' . esc_html__('If there\'s a canonical link in place, we use it instead of the current URL. Check this to use the current URL instead', 'fathom-analytics') . '</p>';
}

/**
Expand All @@ -278,14 +310,14 @@ function fathom_print_share_password_setting_field($args = array())
{
$value = get_option(FATHOM_PRIVATE_SHARE_PASSWORD);
$placeholder = '';
echo sprintf('<input type="text" name="%s" id="%s" class="regular-text" value="%s" placeholder="%s" />', FATHOM_PRIVATE_SHARE_PASSWORD, FATHOM_PRIVATE_SHARE_PASSWORD, esc_attr($value), esc_attr($placeholder));
echo '<p class="description">' . __('Required if you have shared your dashboard <a href="https://usefathom.com/docs/features/shared-dashboards" target="_blank">privately</a>. Publicly shared dashboards do not need a password.', 'fathom-analytics') . '</p>';
printf('<input type="text" name="%s" id="%s" class="regular-text" value="%s" placeholder="%s" />', esc_attr( FATHOM_PRIVATE_SHARE_PASSWORD ), esc_attr( FATHOM_PRIVATE_SHARE_PASSWORD ), esc_attr($value), esc_attr($placeholder));
echo '<p class="description">' . wp_kses_post( __('Required if you have shared your dashboard <a href="https://usefathom.com/docs/features/shared-dashboards" target="_blank">privately</a>. Publicly shared dashboards do not need a password.', 'fathom-analytics') ) . '</p>';
}

/**
* Exclude Roles Field
*
* @param array $args
* @param array $args Display arguments.
*
* @return void
*/
Expand All @@ -300,14 +332,14 @@ function fathom_print_exclude_roles_setting_field( $args = array() ) {
$excluded_roles = fathom_get_excluded_roles();

echo '<fieldset>';
foreach ( $roles as $role => $label ) {
$checked = in_array( $role, $excluded_roles, true ) ? 'checked' : '';
echo sprintf( '<input type="checkbox" name="%s[]" id="%s" value="%s" %s />', FATHOM_EXCLUDE_ROLES_OPTION_NAME, $role, $role, $checked );
echo sprintf( '<label for="%s">%s</label><br />', $role, $label );
}
foreach ( $roles as $role => $label ) {
$checked = in_array( $role, $excluded_roles, true ) ? 'checked' : '';
printf( '<input type="checkbox" name="%s[]" id="%s" value="%s" %s />', esc_attr( FATHOM_EXCLUDE_ROLES_OPTION_NAME ), esc_attr( $role ), esc_attr( $role ), esc_attr( $checked ) );
printf( '<label for="%s">%s</label><br />', esc_attr( $role ), esc_html( $label ) );
}
echo '</fieldset>';

echo '<p class="description">' . __( 'Choose the roles you would like to exclude from tracking.', 'fathom-analytics' ) . '</p>';
echo '<p class="description">' . esc_html__( 'Choose the roles you would like to exclude from tracking.', 'fathom-analytics' ) . '</p>';
}

/**
Expand All @@ -317,8 +349,8 @@ function fathom_print_site_id_setting_field($args = array())
{
$value = get_option(FATHOM_SITE_ID_OPTION_NAME);
$placeholder = 'ABCDEF';
echo sprintf('<input type="text" name="%s" id="%s" class="regular-text" value="%s" placeholder="%s" />', FATHOM_SITE_ID_OPTION_NAME, FATHOM_SITE_ID_OPTION_NAME, esc_attr($value), esc_attr($placeholder));
echo '<p class="description">' . __('This is the <a href="https://usefathom.com/support/wordpress" target="_blank">unique Tracking ID</a> for your site', 'fathom-analytics') . '</p>';
printf('<input type="text" name="%s" id="%s" class="regular-text" value="%s" placeholder="%s" />', esc_attr( FATHOM_SITE_ID_OPTION_NAME ), esc_attr( FATHOM_SITE_ID_OPTION_NAME ), esc_attr($value), esc_attr($placeholder));
echo '<p class="description">' . wp_kses_post( __('This is the <a href="https://usefathom.com/support/wordpress" target="_blank">unique Tracking ID</a> for your site', 'fathom-analytics') ) . '</p>';
}

add_action( 'wp_enqueue_scripts', 'fathom_enqueue_js_snippet' );
Expand Down Expand Up @@ -475,7 +507,7 @@ function fathom_exclude_from_litespeed( $excluded ) {
*/

function allow_fathom_script($allowed, $handle) {
if ($handle === 'fathom-snippet') {
if ( 'fathom-snippet' === $handle ) {
return true;
}
return $allowed;
Expand All @@ -492,9 +524,9 @@ function allow_fathom_script($allowed, $handle) {
*/

function exclude_fathom_script_from_cookiebot() {
if ( in_array( 'cookiebot/cookiebot.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
if ( in_array( 'cookiebot/cookiebot.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ), true ) ) {
return 'data-cookieconsent="ignore"';
} else {
return '';
}
}
}
Loading