From c4e0839fba0abf26e21fcb6cf868290e1a88baf7 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 12:29:25 +0100 Subject: [PATCH 1/7] Removed unused code. --- src/Admin/Provisioning.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Admin/Provisioning.php b/src/Admin/Provisioning.php index f988d40..6b522c3 100644 --- a/src/Admin/Provisioning.php +++ b/src/Admin/Provisioning.php @@ -16,7 +16,6 @@ use Plausible\Analytics\WP\ClientFactory; use Plausible\Analytics\WP\Helpers; use Plausible\Analytics\WP\Integrations; -use Plausible\Analytics\WP\Integrations\WooCommerce; class Provisioning { const CUSTOM_PROPERTIES = [ From e8c614f3aef9d9534057c24190a162da39fe7186 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 12:34:57 +0100 Subject: [PATCH 2/7] Improved: Enhanced Measurements of which the required capabilities are not in the plan are now disabled. --- src/Admin/Settings/Page.php | 107 +++++++++++++++++++++--------------- src/Ajax.php | 35 ++++++++++++ src/Client.php | 2 +- 3 files changed, 100 insertions(+), 44 deletions(-) diff --git a/src/Admin/Settings/Page.php b/src/Admin/Settings/Page.php index 742e570..2914191 100644 --- a/src/Admin/Settings/Page.php +++ b/src/Admin/Settings/Page.php @@ -54,6 +54,14 @@ class Page extends API { 'hook_type' => 'success', ]; + const CAP_GOALS = 'goals'; + + const CAP_PROPS = 'props'; + + const CAP_FUNNELS = 'funnels'; + + const CAP_REVENUE = 'revenue'; + /** * @var array|array[] $fields */ @@ -117,15 +125,11 @@ public function __construct() { 'value' => $settings[ 'api_token' ], ], [ - 'label' => empty( $settings[ 'domain_name' ] ) || empty( $settings[ 'api_token' ] ) ? - esc_html__( 'Connect', 'plausible-analytics' ) : + 'label' => empty( $settings[ 'domain_name' ] ) || empty( $settings[ 'api_token' ] ) ? esc_html__( 'Connect', 'plausible-analytics' ) : esc_html__( 'Connected', 'plausible-analytics' ), 'slug' => 'connect_plausible_analytics', 'type' => 'button', - 'disabled' => empty( $settings[ 'domain_name' ] ) || - empty( $settings[ 'api_token' ] ) || - ! $this->client instanceof Client || - $this->client->is_api_token_valid(), + 'disabled' => empty( $settings[ 'domain_name' ] ) || empty( $settings[ 'api_token' ] ) || ! $this->client instanceof Client || $this->client->is_api_token_valid(), ], ], ], @@ -145,6 +149,7 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => '404', + 'caps' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), ], 'outbound-links' => [ 'label' => esc_html__( 'Outbound links', 'plausible-analytics' ), @@ -152,27 +157,31 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'outbound-links', + 'caps' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), ], 'file-downloads' => [ - 'label' => esc_html__( 'File downloads', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-file-downloads', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'file-downloads', + 'label' => esc_html__( 'File downloads', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-file-downloads', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'file-downloads', + 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), ], 'search' => [ - 'label' => esc_html__( 'Search queries', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-site-search-tracking', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'search', + 'label' => esc_html__( 'Search queries', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-site-search-tracking', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'search', + 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), ], 'tagged-events' => [ - 'label' => esc_html__( 'Custom events', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-setup-custom-events-to-track-goal-conversions', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'tagged-events', + 'label' => esc_html__( 'Custom events', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-setup-custom-events-to-track-goal-conversions', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'tagged-events', + 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), ], 'revenue' => [ 'label' => esc_html__( 'Ecommerce revenue', 'plausible-analytics' ), @@ -180,21 +189,23 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'revenue', - 'disabled' => ! empty( $settings[ 'self_hosted_domain' ] ), + 'disabled' => ! empty( $settings[ 'self_hosted_domain' ] ) || ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_FUNNELS, self::CAP_PROPS, self::CAP_REVENUE ] ), ], 'pageview-props' => [ - 'label' => esc_html__( 'Authors and categories', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-send-custom-properties', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'pageview-props', + 'label' => esc_html__( 'Authors and categories', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-send-custom-properties', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'pageview-props', + 'disabled' => ! $this->token_has_cap( [ self::CAP_PROPS ] ), ], 'form-completions' => [ - 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-form-completions', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'form-completions', + 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-form-completions', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'form-completions', + 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), ], 'hash' => [ 'label' => esc_html__( 'Hash-based routing', 'plausible-analytics' ), @@ -227,8 +238,7 @@ public function __construct() { get_site_url( null, rest_get_url_prefix() ), empty( Helpers::get_settings()[ 'proxy_enabled' ] - ) ? 'a random directory/file for storing the JS file' : - 'a JS file, called ' . str_replace( + ) ? 'a random directory/file for storing the JS file' : 'a JS file, called ' . str_replace( ABSPATH, '', Helpers::get_proxy_resource( 'cache_dir' ) . Helpers::get_proxy_resource( @@ -263,8 +273,7 @@ public function __construct() { 'slug' => 'enable_analytics_dashboard', 'type' => 'checkbox', 'value' => 'on', - 'disabled' => empty( Helpers::get_settings()[ 'api_token' ] ) && - empty( Helpers::get_settings()[ 'self_hosted_domain' ] ), + 'disabled' => empty( Helpers::get_settings()[ 'api_token' ] ) && empty( Helpers::get_settings()[ 'self_hosted_domain' ] ), ], ], ], @@ -367,8 +376,7 @@ public function __construct() { 'label' => esc_html__( 'Domain name', 'plausible-analytics' ), 'slug' => 'self_hosted_domain', 'type' => 'text', - 'value' => defined( 'PLAUSIBLE_SELF_HOSTED_DOMAIN' ) ? PLAUSIBLE_SELF_HOSTED_DOMAIN : - $settings[ 'self_hosted_domain' ], + 'value' => defined( 'PLAUSIBLE_SELF_HOSTED_DOMAIN' ) ? PLAUSIBLE_SELF_HOSTED_DOMAIN : $settings[ 'self_hosted_domain' ], 'placeholder' => 'e.g. ' . Helpers::get_domain(), 'disabled' => Helpers::proxy_enabled(), ], @@ -478,6 +486,22 @@ private function init() { new Hooks(); } + private function token_has_cap( $caps ) { + static $stored_caps = []; + + if ( empty( $stored_caps ) ) { + $stored_caps = get_option( 'plausible_analytics_api_token_caps', [] ); + } + + foreach ( $caps as $cap ) { + if ( empty( $stored_caps[ $cap ] ) ) { + return false; + } + } + + return true; + } + /** * Load all available user roles as a list (sorted alphabetically) of checkboxes to be processed by the Settings * API. @@ -552,8 +576,7 @@ public function register_menu() { /** * Don't show the Analytics dashboard, if View Stats is disabled. */ - if ( ! empty( $settings[ 'enable_analytics_dashboard' ] ) || - ( ! empty( $settings[ 'self_hosted_domain' ] ) && ! empty( $settings[ 'self_hosted_shared_link' ] ) ) ) { + if ( ! empty( $settings[ 'enable_analytics_dashboard' ] ) || ( ! empty( $settings[ 'self_hosted_domain' ] ) && ! empty( $settings[ 'self_hosted_shared_link' ] ) ) ) { // Setup `Analytics` page under Dashboard. add_dashboard_page( esc_html__( 'Analytics', 'plausible-analytics' ), @@ -649,9 +672,7 @@ public function render_analytics_dashboard() { * For regular users, the shared link is provisioned by the API, so it shouldn't be empty. * @since v2.0.3 */ - if ( ( ! $self_hosted && ! empty( $analytics_enabled ) && ! empty( $shared_link ) ) || - ( $self_hosted && ! empty( $shared_link ) ) || - strpos( $shared_link, 'XXXXXX' ) !== false ) { + if ( ( ! $self_hosted && ! empty( $analytics_enabled ) && ! empty( $shared_link ) ) || ( $self_hosted && ! empty( $shared_link ) ) || strpos( $shared_link, 'XXXXXX' ) !== false ) { $page_url = isset( $_GET[ 'page-url' ] ) ? esc_url( $_GET[ 'page-url' ] ) : ''; // Append individual page URL if it exists. diff --git a/src/Ajax.php b/src/Ajax.php index dc41a71..bb9c68a 100644 --- a/src/Ajax.php +++ b/src/Ajax.php @@ -290,6 +290,7 @@ public function save_options() { // Validate Plugin Token, if this is the Plugin Token field. if ( $option->name === 'api_token' ) { $this->validate_api_token( $option->value ); + $this->store_capabilities( $option->value ); $additional = $this->maybe_render_additional_message( $option->name, $option->value ); @@ -334,4 +335,38 @@ private function validate_api_token( $token = '' ) { wp_send_json_error( 'invalid_api_token', 400 ); } } + + /** + * Stores the capabilities for the currently entered API token in the DB for later use. + * + * @param $token + * + * @return void + */ + private function store_capabilities( $token = '' ) { + $client_factory = new ClientFactory( $token ); + /** @var Client $client */ + $client = $client_factory->build(); + + if ( ! $client instanceof Client ) { + return; + } + + /** @var Client\Model\CapabilitiesFeatures $features */ + $features = $client->get_features(); + + if ( ! $features ) { + return; + } + + $caps = [ + 'funnels' => $features->getFunnels(), + 'goals' => $features->getGoals(), + 'props' => $features->getProps(), + 'revenue' => $features->getRevenueGoals(), + 'stats' => $features->getStatsApi(), + ]; + + update_option( 'plausible_analytics_api_token_caps', $caps ); + } } diff --git a/src/Client.php b/src/Client.php index b9f7e26..a972d02 100644 --- a/src/Client.php +++ b/src/Client.php @@ -87,7 +87,7 @@ public function is_api_token_valid() { * * @return false|Client\Model\CapabilitiesFeatures */ - private function get_features() { + public function get_features() { $capabilities = $this->get_capabilities(); if ( $capabilities instanceof Capabilities ) { From f13c0880d731ed6c398a55ec2e030099fef2e083 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:45:52 +0100 Subject: [PATCH 3/7] Improved: capabilities are refreshed when clicking "Connect" and options are refreshed accordingly. --- assets/src/js/admin/main.js | 41 ++++++- assets/src/js/admin/main.min.js | 2 +- src/Admin/Settings/API.php | 183 ++++++++++++++++---------------- src/Admin/Settings/Page.php | 34 +++--- src/Ajax.php | 16 ++- 5 files changed, 163 insertions(+), 113 deletions(-) diff --git a/assets/src/js/admin/main.js b/assets/src/js/admin/main.js index 0b830f9..ab5fc65 100644 --- a/assets/src/js/admin/main.js +++ b/assets/src/js/admin/main.js @@ -109,11 +109,13 @@ document.addEventListener('DOMContentLoaded', () => { // Toggle: off button.classList.replace('bg-indigo-600', 'bg-gray-200'); toggle.classList.replace('translate-x-5', 'translate-x-0'); + button.dataset.status = 'off'; toggleStatus = ''; } else { // Toggle: on button.classList.replace('bg-gray-200', 'bg-indigo-600'); toggle.classList.replace('translate-x-0', 'translate-x-5'); + button.dataset.status = 'on'; toggleStatus = 'on'; } @@ -134,7 +136,7 @@ document.addEventListener('DOMContentLoaded', () => { * * @param e */ - saveOption: function (e) { + saveOption: async function (e) { const button = e.target; const section = button.closest('.plausible-analytics-section'); const inputs = section.querySelectorAll('input, textarea'); @@ -157,7 +159,42 @@ document.addEventListener('DOMContentLoaded', () => { button.setAttribute('disabled', 'disabled'); - plausible.ajax(form, button); + let data = await plausible.ajax(form, button); + + if (data.capabilities === undefined) { + return; + } + + plausible.maybeDisableOptions(data.capabilities); + }, + + /** + * Disable options based on the capabilities retrieved from the API. + * + * @param capabilities + */ + maybeDisableOptions: function (capabilities) { + let options = document.querySelectorAll('button[data-caps]'); + + options.forEach(function (option) { + let caps = option.dataset.caps.split(','); + let disabled = false; + option.removeAttribute('disabled'); + + caps.forEach(function (cap) { + if (capabilities[cap] === false) { + disabled = true; + } + }); + + if (disabled === true) { + option.setAttribute('disabled', 'disabled'); + // Trigger a click to make sure the option is disabled. + if (option.dataset.status === 'on') { + option.dispatchEvent(new Event('click', {bubbles: true})); + } + } + }); }, /** diff --git a/assets/src/js/admin/main.min.js b/assets/src/js/admin/main.min.js index b9929c6..fa779f3 100644 --- a/assets/src/js/admin/main.min.js +++ b/assets/src/js/admin/main.min.js @@ -1 +1 @@ -document.addEventListener("DOMContentLoaded",()=>{if(!document.location.href.includes("plausible_analytics")){return}let plausible={nonceElem:document.getElementById("_wpnonce"),nonce:"",showWizardElem:document.getElementById("show_wizard"),domainNameElem:document.getElementById("domain_name"),apiTokenElem:document.getElementById("api_token"),createAPITokenElems:document.getElementsByClassName("plausible-create-api-token"),buttonElems:document.getElementsByClassName("plausible-analytics-button"),stepElems:document.getElementsByClassName("plausible-analytics-wizard-next-step"),init:function(){if(document.location.hash===""&&document.getElementById("plausible-analytics-wizard")!==null){document.location.hash="#welcome_slide"}if(this.nonceElem!==null){this.nonce=this.nonceElem.value}this.toggleWizardStep();window.addEventListener("hashchange",this.toggleWizardStep);if(this.showWizardElem!==null){this.showWizardElem.addEventListener("click",this.showWizard)}if(this.domainNameElem!==null){this.domainNameElem.addEventListener("keyup",this.disableConnectButton)}if(this.apiTokenElem!==null){this.apiTokenElem.addEventListener("keyup",this.disableConnectButton)}if(this.createAPITokenElems.length>0){for(let i=0;i0){for(let i=0;i0){for(let i=0;i0){button.children[0].classList.remove("hidden")}button.setAttribute("disabled","disabled");plausible.ajax(form,button)},validateInput:function(input){if(input.name==="domain_name"&&input.value.match(/^(https?:\/\/)?(www.)?/).length>0){input.value=input.value.replace(/^(https?:\/\/)?(www.)?/,"")}return input},saveOptionOnNext:function(e){let hash=document.location.hash.replace("#","");if(hash!=="api_token_slide"&&hash!=="domain_name_slide"){return}let form=e.target.closest(".plausible-analytics-wizard-step-section");let inputs=form.getElementsByTagName("INPUT");let options=[];for(let input of inputs){input=plausible.validateInput(input);options.push({name:input.name,value:input.value})}let data=new FormData;data.append("action","plausible_analytics_save_options");data.append("options",JSON.stringify(options));data.append("_nonce",plausible.nonce);plausible.ajax(data).then(response=>{if(hash==="api_token_slide"&&response.success===true){let stats_button=document.getElementById("enable_analytics_dashboard_view_stats_in_wordpress");stats_button.removeAttribute("disabled")}})},disableConnectButton:function(e){let target=e.target;let button=document.getElementById("connect_plausible_analytics");let buttonIsHref=false;if(button===null){let slide_id=document.location.hash;button=document.querySelector(slide_id+" .plausible-analytics-wizard-next-step");buttonIsHref=true}if(button===null){return}if(target.value!==""){if(!buttonIsHref){button.disabled=false}else{button.classList.remove("pointer-events-none");button.classList.replace("bg-gray-200","bg-indigo-600")}return}if(!buttonIsHref){button.disabled=true;button.innerHTML=button.innerHTML.replace("Connected","Connect")}else{button.classList+=" pointer-events-none";button.classList.replace("bg-indigo-600","bg-gray-200")}},createAPIToken:function(e){e.preventDefault();let domain=document.getElementById("domain_name").value;domain=domain.replaceAll("/","%2F");window.open(`${plausible_analytics_hosted_domain}/${domain}/settings/integrations?new_token=WordPress`,"_blank","location=yes,height=768,width=1024,scrollbars=yes,status=no")},showWizard:function(e){let data=new FormData;data.append("action","plausible_analytics_show_wizard");data.append("_nonce",e.target.dataset.nonce);plausible.ajax(data)},toggleWizardStep:function(){if(document.getElementById("plausible-analytics-wizard")===null){return}const hash=document.location.hash.substring(1).replace("_slide","");let allSteps=document.querySelectorAll(".plausible-analytics-wizard-step");let activeSteps=document.querySelectorAll(".plausible-analytics-wizard-active-step");let completedSteps=document.querySelectorAll(".plausible-analytics-wizard-completed-step");for(let i=0;in);if(currentlyCompletedSteps.length<1){return}currentlyCompletedSteps.forEach(function(step){let completedStep=document.getElementById("completed-step-"+step);let inactiveStep=document.getElementById("step-"+step);completedStep.classList.remove("hidden");inactiveStep.classList+=" hidden"})},ajax:function(data,button=null,showMessages=true){return fetch(ajaxurl,{method:"POST",body:data}).then(response=>{if(button){if(button.children.length>0){button.children[0].classList+=" hidden"}if(button.id==="connect_plausible_analytics"&&response.status===200){button.innerText=plausible_analytics_i18n.connected}else{button.removeAttribute("disabled")}}if(response.status===200){return response.json()}return false}).then(response=>{if(showMessages===true){plausible.showMessages()}let event=new CustomEvent("plausibleAjaxDone",{detail:response});document.dispatchEvent(event);if(response.data!==undefined){return response.data}else{return response}})},showMessages:function(){let messages=plausible.fetchMessages();messages.then(function(messages){if(messages.error!==false){plausible.showMessage(messages.error,"error")}else if(messages.notice!==false){plausible.showMessage(messages.notice,"notice")}else if(messages.success!==false){plausible.showMessage(messages.success,"success")}if(messages.additional.length===0||document.getElementById("plausible-analytics-wizard")!==null){return}if(messages.additional.id!==undefined&&messages.additional.message){plausible.showAdditionalMessage(messages.additional.message,messages.additional.id)}else if(messages.additional.id!==undefined&&messages.additional.message===""){plausible.removeAdditionalMessage(messages.additional.id)}})},fetchMessages:function(){let data=new FormData;data.append("action","plausible_analytics_messages");let result=plausible.ajax(data,null,false);return result.then(function(response){return response})},showMessage:function(message,type="success"){if(type==="error"){document.getElementById("icon-error").classList.remove("hidden");document.getElementById("icon-success").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}else if(type==="notice"){document.getElementById("icon-notice").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-success").classList.add("hidden")}else{document.getElementById("icon-success").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}let notice=document.getElementById("plausible-analytics-notice");document.getElementById("plausible-analytics-notice-text").innerHTML=message;notice.classList.remove("hidden");setTimeout(function(){notice.classList.replace("opacity-0","opacity-100")},200);if(type!=="error"){setTimeout(function(){notice.classList.replace("opacity-100","opacity-0");setTimeout(function(){notice.classList+=" hidden"},200)},2e3)}},showAdditionalMessage:function(html,target){let targetElem=document.querySelector(`[name='${target}']`);let container=targetElem.closest(".plausible-analytics-group");if(container.children.length>0){for(let i=0;i0){for(let i=0;i{if(!document.location.href.includes("plausible_analytics")){return}let plausible={nonceElem:document.getElementById("_wpnonce"),nonce:"",showWizardElem:document.getElementById("show_wizard"),domainNameElem:document.getElementById("domain_name"),apiTokenElem:document.getElementById("api_token"),createAPITokenElems:document.getElementsByClassName("plausible-create-api-token"),buttonElems:document.getElementsByClassName("plausible-analytics-button"),stepElems:document.getElementsByClassName("plausible-analytics-wizard-next-step"),init:function(){if(document.location.hash===""&&document.getElementById("plausible-analytics-wizard")!==null){document.location.hash="#welcome_slide"}if(this.nonceElem!==null){this.nonce=this.nonceElem.value}this.toggleWizardStep();window.addEventListener("hashchange",this.toggleWizardStep);if(this.showWizardElem!==null){this.showWizardElem.addEventListener("click",this.showWizard)}if(this.domainNameElem!==null){this.domainNameElem.addEventListener("keyup",this.disableConnectButton)}if(this.apiTokenElem!==null){this.apiTokenElem.addEventListener("keyup",this.disableConnectButton)}if(this.createAPITokenElems.length>0){for(let i=0;i0){for(let i=0;i0){for(let i=0;i0){button.children[0].classList.remove("hidden")}button.setAttribute("disabled","disabled");let data=await plausible.ajax(form,button);if(data.capabilities===undefined){return}plausible.maybeDisableOptions(data.capabilities)},maybeDisableOptions:function(capabilities){let options=document.querySelectorAll("button[data-caps]");options.forEach(function(option){let caps=option.dataset.caps.split(",");let disabled=false;option.removeAttribute("disabled");caps.forEach(function(cap){if(capabilities[cap]===false){disabled=true}});if(disabled===true){option.setAttribute("disabled","disabled");if(option.dataset.status==="on"){option.dispatchEvent(new Event("click",{bubbles:true}))}}})},validateInput:function(input){if(input.name==="domain_name"&&input.value.match(/^(https?:\/\/)?(www.)?/).length>0){input.value=input.value.replace(/^(https?:\/\/)?(www.)?/,"")}return input},saveOptionOnNext:function(e){let hash=document.location.hash.replace("#","");if(hash!=="api_token_slide"&&hash!=="domain_name_slide"){return}let form=e.target.closest(".plausible-analytics-wizard-step-section");let inputs=form.getElementsByTagName("INPUT");let options=[];for(let input of inputs){input=plausible.validateInput(input);options.push({name:input.name,value:input.value})}let data=new FormData;data.append("action","plausible_analytics_save_options");data.append("options",JSON.stringify(options));data.append("_nonce",plausible.nonce);plausible.ajax(data).then(response=>{if(hash==="api_token_slide"&&response.success===true){let stats_button=document.getElementById("enable_analytics_dashboard_view_stats_in_wordpress");stats_button.removeAttribute("disabled")}})},disableConnectButton:function(e){let target=e.target;let button=document.getElementById("connect_plausible_analytics");let buttonIsHref=false;if(button===null){let slide_id=document.location.hash;button=document.querySelector(slide_id+" .plausible-analytics-wizard-next-step");buttonIsHref=true}if(button===null){return}if(target.value!==""){if(!buttonIsHref){button.disabled=false}else{button.classList.remove("pointer-events-none");button.classList.replace("bg-gray-200","bg-indigo-600")}return}if(!buttonIsHref){button.disabled=true;button.innerHTML=button.innerHTML.replace("Connected","Connect")}else{button.classList+=" pointer-events-none";button.classList.replace("bg-indigo-600","bg-gray-200")}},createAPIToken:function(e){e.preventDefault();let domain=document.getElementById("domain_name").value;domain=domain.replaceAll("/","%2F");window.open(`${plausible_analytics_hosted_domain}/${domain}/settings/integrations?new_token=WordPress`,"_blank","location=yes,height=768,width=1024,scrollbars=yes,status=no")},showWizard:function(e){let data=new FormData;data.append("action","plausible_analytics_show_wizard");data.append("_nonce",e.target.dataset.nonce);plausible.ajax(data)},toggleWizardStep:function(){if(document.getElementById("plausible-analytics-wizard")===null){return}const hash=document.location.hash.substring(1).replace("_slide","");let allSteps=document.querySelectorAll(".plausible-analytics-wizard-step");let activeSteps=document.querySelectorAll(".plausible-analytics-wizard-active-step");let completedSteps=document.querySelectorAll(".plausible-analytics-wizard-completed-step");for(let i=0;in);if(currentlyCompletedSteps.length<1){return}currentlyCompletedSteps.forEach(function(step){let completedStep=document.getElementById("completed-step-"+step);let inactiveStep=document.getElementById("step-"+step);completedStep.classList.remove("hidden");inactiveStep.classList+=" hidden"})},ajax:function(data,button=null,showMessages=true){return fetch(ajaxurl,{method:"POST",body:data}).then(response=>{if(button){if(button.children.length>0){button.children[0].classList+=" hidden"}if(button.id==="connect_plausible_analytics"&&response.status===200){button.innerText=plausible_analytics_i18n.connected}else{button.removeAttribute("disabled")}}if(response.status===200){return response.json()}return false}).then(response=>{if(showMessages===true){plausible.showMessages()}let event=new CustomEvent("plausibleAjaxDone",{detail:response});document.dispatchEvent(event);if(response.data!==undefined){return response.data}else{return response}})},showMessages:function(){let messages=plausible.fetchMessages();messages.then(function(messages){if(messages.error!==false){plausible.showMessage(messages.error,"error")}else if(messages.notice!==false){plausible.showMessage(messages.notice,"notice")}else if(messages.success!==false){plausible.showMessage(messages.success,"success")}if(messages.additional.length===0||document.getElementById("plausible-analytics-wizard")!==null){return}if(messages.additional.id!==undefined&&messages.additional.message){plausible.showAdditionalMessage(messages.additional.message,messages.additional.id)}else if(messages.additional.id!==undefined&&messages.additional.message===""){plausible.removeAdditionalMessage(messages.additional.id)}})},fetchMessages:function(){let data=new FormData;data.append("action","plausible_analytics_messages");let result=plausible.ajax(data,null,false);return result.then(function(response){return response})},showMessage:function(message,type="success"){if(type==="error"){document.getElementById("icon-error").classList.remove("hidden");document.getElementById("icon-success").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}else if(type==="notice"){document.getElementById("icon-notice").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-success").classList.add("hidden")}else{document.getElementById("icon-success").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}let notice=document.getElementById("plausible-analytics-notice");document.getElementById("plausible-analytics-notice-text").innerHTML=message;notice.classList.remove("hidden");setTimeout(function(){notice.classList.replace("opacity-0","opacity-100")},200);if(type!=="error"){setTimeout(function(){notice.classList.replace("opacity-100","opacity-0");setTimeout(function(){notice.classList+=" hidden"},200)},2e3)}},showAdditionalMessage:function(html,target){let targetElem=document.querySelector(`[name='${target}']`);let container=targetElem.closest(".plausible-analytics-group");if(container.children.length>0){for(let i=0;i0){for(let i=0;islides_description['success'] = sprintf( + if ( empty( $settings[ 'enable_analytics_dashboard' ] ) ) { + $this->slides_description[ 'success' ] = sprintf( __( '

Congrats! Your traffic is now being counted without compromising the user experience and privacy of your visitors.

Note that visits from logged in users aren\'t tracked. If you want to track visits for certain user roles, then please specify them in the plugin\'s settings.

Need help? Our documentation is the best place to find most answers right away.

Still haven\'t found the answer you\'re looking for? We\'re here to help. Please contact our support.

', 'plausible-analytics' ), - admin_url( 'admin-ajax.php?action=plausible_analytics_quit_wizard&_nonce=' ) . - wp_create_nonce( 'plausible_analytics_quit_wizard' ) . - '&redirect=tracked_user_roles', + admin_url( 'admin-ajax.php?action=plausible_analytics_quit_wizard&_nonce=' ) . wp_create_nonce( 'plausible_analytics_quit_wizard' ) . '&redirect=tracked_user_roles', 'https://plausible.io/docs?utm_source=WordPress&utm_medium=Referral&utm_campaign=WordPress+plugin', 'https://plausible.io/contact?utm_source=WordPress&utm_medium=Referral&utm_campaign=WordPress+plugin' ); @@ -130,7 +126,7 @@ public function settings_page() { /** * Settings screen */ - $current_tab = ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'general'; + $current_tab = ! empty( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'general'; ?>
@@ -157,7 +153,7 @@ public function settings_page() { fields[ $current_tab ] as $tab => $field ): ?>
get_wizard_option_properties( $id ); if ( ! empty( $field ) ) { - $hide_header = $field['type'] === 'group'; - - echo call_user_func( [ - $this, - "render_{$field['type']}_field" - ], $field, $hide_header ); + $hide_header = $field[ 'type' ] === 'group'; + + echo call_user_func( + [ + $this, + "render_{$field['type']}_field", + ], + $field, + $hide_header + ); } ?> @@ -423,13 +423,13 @@ class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">

* @return array|mixed */ private function get_wizard_option_properties( $slug ) { - foreach ( $this->fields['general'] as $group ) { - if ( $group['slug'] === $slug ) { + foreach ( $this->fields[ 'general' ] as $group ) { + if ( $group[ 'slug' ] === $slug ) { return $group; } - foreach ( $group['fields'] as $field ) { - if ( $field['slug'] === $slug ) { + foreach ( $group[ 'fields' ] as $field ) { + if ( $field[ 'slug' ] === $slug ) { return $field; } } @@ -441,9 +441,9 @@ private function get_wizard_option_properties( $slug ) { /** * Render Header Navigation. * - * @return void * @since 1.3.0 * @access public + * @return void */ public function render_navigation() { $screen = get_current_screen(); @@ -453,7 +453,7 @@ public function render_navigation() { return; } - $current_tab = ! empty( $_GET['tab'] ) ? $_GET['tab'] : ''; + $current_tab = ! empty( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : ''; $tabs = apply_filters( 'plausible_analytics_settings_navigation_tabs', [ @@ -474,9 +474,9 @@ public function render_navigation() { foreach ( $tabs as $tab ) { printf( '%3$s', - esc_url( $tab['url'] ), - esc_attr( $tab['class'] ), - esc_html( $tab['name'] ) + esc_url( $tab[ 'url' ] ), + esc_attr( $tab[ 'class' ] ), + esc_html( $tab[ 'name' ] ) ); } ?> @@ -486,8 +486,8 @@ public function render_navigation() { /** * Render Quick Actions * - * @return string * @since 1.3.0 + * @return string */ private function render_quick_actions() { ob_start(); @@ -495,13 +495,13 @@ private function render_quick_actions() { ?> 0 ) : ?> - + - - + + @@ -512,8 +512,8 @@ private function render_quick_actions() { /** * Get Quick Actions. * - * @return array * @since 1.3.0 + * @return array */ private function get_quick_actions() { $settings = Helpers::get_settings(); @@ -525,7 +525,7 @@ private function get_quick_actions() { 'url' => admin_url( "admin-ajax.php?action=plausible_analytics_show_wizard&_nonce=$nonce&redirect=1" ), 'id' => 'show_wizard', 'target' => '_self', - 'disabled' => ! empty( $settings['self_hosted_domain'] ), + 'disabled' => ! empty( $settings[ 'self_hosted_domain' ] ), ], 'view-docs' => [ 'label' => esc_html__( 'Documentation', 'plausible-analytics' ), @@ -545,13 +545,13 @@ private function get_quick_actions() { /** * Render Group Field. * - * @return string * @since 1.3.0 * @access public + * @return string */ public function render_group_field( array $group, $hide_header = false ) { - $toggle = $group['toggle'] ?? []; - $fields = $group['fields']; + $toggle = $group[ 'toggle' ] ?? []; + $fields = $group[ 'fields' ]; ob_start(); ?>

+ id="">
- +
- - + + @@ -578,7 +578,7 @@ class="
+ for="">
/>
@@ -630,12 +630,12 @@ class="block w-full !border-gray-300 !dark:border-gray-700 !rounded-md focus:rin */ public function render_button_field( array $field ) { ob_start(); - $disabled = isset( $field['disabled'] ) && $field['disabled'] === true; + $disabled = isset( $field[ 'disabled' ] ) && $field[ 'disabled' ] === true; ?>
- - - - - + + + @@ -700,29 +695,29 @@ class="plausible-analytics-toggle
+ name="">
-
@@ -776,9 +771,9 @@ class="w-5 h-12 text-yellow-400">
-
+
-

+

diff --git a/src/Admin/Settings/Page.php b/src/Admin/Settings/Page.php index 2914191..a1ebe0b 100644 --- a/src/Admin/Settings/Page.php +++ b/src/Admin/Settings/Page.php @@ -144,20 +144,22 @@ public function __construct() { ), 'fields' => [ '404' => [ - 'label' => esc_html__( '404 error pages', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-404-error-pages', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => '404', - 'caps' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), + 'label' => esc_html__( '404 error pages', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-404-error-pages', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => '404', + 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), + 'caps' => [ self::CAP_GOALS ], ], 'outbound-links' => [ - 'label' => esc_html__( 'Outbound links', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-external-link-clicks', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'outbound-links', - 'caps' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), + 'label' => esc_html__( 'Outbound links', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-external-link-clicks', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'outbound-links', + 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), + 'caps' => [ self::CAP_GOALS ], ], 'file-downloads' => [ 'label' => esc_html__( 'File downloads', 'plausible-analytics' ), @@ -166,6 +168,7 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'file-downloads', 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), + 'caps' => [ self::CAP_GOALS ], ], 'search' => [ 'label' => esc_html__( 'Search queries', 'plausible-analytics' ), @@ -174,6 +177,7 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'search', 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), + 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], ], 'tagged-events' => [ 'label' => esc_html__( 'Custom events', 'plausible-analytics' ), @@ -182,6 +186,7 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'tagged-events', 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), + 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], ], 'revenue' => [ 'label' => esc_html__( 'Ecommerce revenue', 'plausible-analytics' ), @@ -190,6 +195,7 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'revenue', 'disabled' => ! empty( $settings[ 'self_hosted_domain' ] ) || ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_FUNNELS, self::CAP_PROPS, self::CAP_REVENUE ] ), + 'caps' => [ self::CAP_GOALS, self::CAP_FUNNELS, self::CAP_PROPS, self::CAP_REVENUE ], ], 'pageview-props' => [ 'label' => esc_html__( 'Authors and categories', 'plausible-analytics' ), @@ -198,6 +204,7 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'pageview-props', 'disabled' => ! $this->token_has_cap( [ self::CAP_PROPS ] ), + 'caps' => [ self::CAP_PROPS ], ], 'form-completions' => [ 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), @@ -206,6 +213,7 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'form-completions', 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), + 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], ], 'hash' => [ 'label' => esc_html__( 'Hash-based routing', 'plausible-analytics' ), @@ -213,6 +221,7 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'hash', + 'caps' => [], ], 'compat' => [ 'label' => esc_html__( 'IE compatibility', 'plausible-analytics' ), @@ -220,6 +229,7 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'compat', + 'caps' => [], ], ], ], diff --git a/src/Ajax.php b/src/Ajax.php index bb9c68a..b627523 100644 --- a/src/Ajax.php +++ b/src/Ajax.php @@ -283,6 +283,8 @@ public function save_options() { wp_send_json_error( null, 400 ); } + $caps = []; + foreach ( $options as $option ) { // Clean spaces $settings[ $option->name ] = trim( $option->value ); @@ -290,7 +292,7 @@ public function save_options() { // Validate Plugin Token, if this is the Plugin Token field. if ( $option->name === 'api_token' ) { $this->validate_api_token( $option->value ); - $this->store_capabilities( $option->value ); + $caps = $this->store_capabilities( $option->value ); $additional = $this->maybe_render_additional_message( $option->name, $option->value ); @@ -302,6 +304,10 @@ public function save_options() { Messages::set_success( __( 'Settings saved.', 'plausible-analytics' ) ); + if ( ! empty( $caps ) ) { + wp_send_json_success( [ 'capabilities' => $caps ], 200 ); + } + wp_send_json_success( null, 200 ); } @@ -341,7 +347,7 @@ private function validate_api_token( $token = '' ) { * * @param $token * - * @return void + * @return false|array */ private function store_capabilities( $token = '' ) { $client_factory = new ClientFactory( $token ); @@ -349,14 +355,14 @@ private function store_capabilities( $token = '' ) { $client = $client_factory->build(); if ( ! $client instanceof Client ) { - return; + return false; } /** @var Client\Model\CapabilitiesFeatures $features */ $features = $client->get_features(); if ( ! $features ) { - return; + return false; } $caps = [ @@ -368,5 +374,7 @@ private function store_capabilities( $token = '' ) { ]; update_option( 'plausible_analytics_api_token_caps', $caps ); + + return $caps; } } From fe05b8316301943051f2e6e338abfdcdcb99bf89 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 19:27:39 +0100 Subject: [PATCH 4/7] Improved: capabilities are now updated when an option is toggled. --- assets/src/js/admin/main.js | 32 ++++---- assets/src/js/admin/main.min.js | 2 +- src/Admin/Settings/Page.php | 136 +++++++++++++++----------------- src/Ajax.php | 45 +---------- src/Client.php | 40 +++++++++- 5 files changed, 122 insertions(+), 133 deletions(-) diff --git a/assets/src/js/admin/main.js b/assets/src/js/admin/main.js index ab5fc65..f3f36ab 100644 --- a/assets/src/js/admin/main.js +++ b/assets/src/js/admin/main.js @@ -84,7 +84,7 @@ document.addEventListener('DOMContentLoaded', () => { * * @param e */ - toggleOption: function (e) { + toggleOption: async function (e) { /** * Make sure event target is a toggle. */ @@ -128,7 +128,13 @@ document.addEventListener('DOMContentLoaded', () => { form.append('is_list', button.dataset.list); form.append('_nonce', plausible.nonce); - plausible.ajax(form); + let data = await plausible.ajax(form); + + if (data.capabilities === undefined) { + return; + } + + plausible.maybeDisableOptions(data.capabilities); }, /** @@ -136,7 +142,7 @@ document.addEventListener('DOMContentLoaded', () => { * * @param e */ - saveOption: async function (e) { + saveOption: function (e) { const button = e.target; const section = button.closest('.plausible-analytics-section'); const inputs = section.querySelectorAll('input, textarea'); @@ -159,13 +165,7 @@ document.addEventListener('DOMContentLoaded', () => { button.setAttribute('disabled', 'disabled'); - let data = await plausible.ajax(form, button); - - if (data.capabilities === undefined) { - return; - } - - plausible.maybeDisableOptions(data.capabilities); + plausible.ajax(form, button); }, /** @@ -178,17 +178,15 @@ document.addEventListener('DOMContentLoaded', () => { options.forEach(function (option) { let caps = option.dataset.caps.split(','); - let disabled = false; - option.removeAttribute('disabled'); + let disable = false; caps.forEach(function (cap) { if (capabilities[cap] === false) { - disabled = true; + disable = true; } }); - if (disabled === true) { - option.setAttribute('disabled', 'disabled'); + if (disable === true) { // Trigger a click to make sure the option is disabled. if (option.dataset.status === 'on') { option.dispatchEvent(new Event('click', {bubbles: true})); @@ -412,7 +410,8 @@ document.addEventListener('DOMContentLoaded', () => { } } - if (response.status === 200) { + // We still want the data, if it's a Payment Required error. + if (response.status === 200 || response.status === 402) { return response.json(); } @@ -515,6 +514,7 @@ document.addEventListener('DOMContentLoaded', () => { }, 2000); } }, + /** * Renders a HTML box containing additional information about the enabled option. * diff --git a/assets/src/js/admin/main.min.js b/assets/src/js/admin/main.min.js index fa779f3..79eb2d3 100644 --- a/assets/src/js/admin/main.min.js +++ b/assets/src/js/admin/main.min.js @@ -1 +1 @@ -document.addEventListener("DOMContentLoaded",()=>{if(!document.location.href.includes("plausible_analytics")){return}let plausible={nonceElem:document.getElementById("_wpnonce"),nonce:"",showWizardElem:document.getElementById("show_wizard"),domainNameElem:document.getElementById("domain_name"),apiTokenElem:document.getElementById("api_token"),createAPITokenElems:document.getElementsByClassName("plausible-create-api-token"),buttonElems:document.getElementsByClassName("plausible-analytics-button"),stepElems:document.getElementsByClassName("plausible-analytics-wizard-next-step"),init:function(){if(document.location.hash===""&&document.getElementById("plausible-analytics-wizard")!==null){document.location.hash="#welcome_slide"}if(this.nonceElem!==null){this.nonce=this.nonceElem.value}this.toggleWizardStep();window.addEventListener("hashchange",this.toggleWizardStep);if(this.showWizardElem!==null){this.showWizardElem.addEventListener("click",this.showWizard)}if(this.domainNameElem!==null){this.domainNameElem.addEventListener("keyup",this.disableConnectButton)}if(this.apiTokenElem!==null){this.apiTokenElem.addEventListener("keyup",this.disableConnectButton)}if(this.createAPITokenElems.length>0){for(let i=0;i0){for(let i=0;i0){for(let i=0;i0){button.children[0].classList.remove("hidden")}button.setAttribute("disabled","disabled");let data=await plausible.ajax(form,button);if(data.capabilities===undefined){return}plausible.maybeDisableOptions(data.capabilities)},maybeDisableOptions:function(capabilities){let options=document.querySelectorAll("button[data-caps]");options.forEach(function(option){let caps=option.dataset.caps.split(",");let disabled=false;option.removeAttribute("disabled");caps.forEach(function(cap){if(capabilities[cap]===false){disabled=true}});if(disabled===true){option.setAttribute("disabled","disabled");if(option.dataset.status==="on"){option.dispatchEvent(new Event("click",{bubbles:true}))}}})},validateInput:function(input){if(input.name==="domain_name"&&input.value.match(/^(https?:\/\/)?(www.)?/).length>0){input.value=input.value.replace(/^(https?:\/\/)?(www.)?/,"")}return input},saveOptionOnNext:function(e){let hash=document.location.hash.replace("#","");if(hash!=="api_token_slide"&&hash!=="domain_name_slide"){return}let form=e.target.closest(".plausible-analytics-wizard-step-section");let inputs=form.getElementsByTagName("INPUT");let options=[];for(let input of inputs){input=plausible.validateInput(input);options.push({name:input.name,value:input.value})}let data=new FormData;data.append("action","plausible_analytics_save_options");data.append("options",JSON.stringify(options));data.append("_nonce",plausible.nonce);plausible.ajax(data).then(response=>{if(hash==="api_token_slide"&&response.success===true){let stats_button=document.getElementById("enable_analytics_dashboard_view_stats_in_wordpress");stats_button.removeAttribute("disabled")}})},disableConnectButton:function(e){let target=e.target;let button=document.getElementById("connect_plausible_analytics");let buttonIsHref=false;if(button===null){let slide_id=document.location.hash;button=document.querySelector(slide_id+" .plausible-analytics-wizard-next-step");buttonIsHref=true}if(button===null){return}if(target.value!==""){if(!buttonIsHref){button.disabled=false}else{button.classList.remove("pointer-events-none");button.classList.replace("bg-gray-200","bg-indigo-600")}return}if(!buttonIsHref){button.disabled=true;button.innerHTML=button.innerHTML.replace("Connected","Connect")}else{button.classList+=" pointer-events-none";button.classList.replace("bg-indigo-600","bg-gray-200")}},createAPIToken:function(e){e.preventDefault();let domain=document.getElementById("domain_name").value;domain=domain.replaceAll("/","%2F");window.open(`${plausible_analytics_hosted_domain}/${domain}/settings/integrations?new_token=WordPress`,"_blank","location=yes,height=768,width=1024,scrollbars=yes,status=no")},showWizard:function(e){let data=new FormData;data.append("action","plausible_analytics_show_wizard");data.append("_nonce",e.target.dataset.nonce);plausible.ajax(data)},toggleWizardStep:function(){if(document.getElementById("plausible-analytics-wizard")===null){return}const hash=document.location.hash.substring(1).replace("_slide","");let allSteps=document.querySelectorAll(".plausible-analytics-wizard-step");let activeSteps=document.querySelectorAll(".plausible-analytics-wizard-active-step");let completedSteps=document.querySelectorAll(".plausible-analytics-wizard-completed-step");for(let i=0;in);if(currentlyCompletedSteps.length<1){return}currentlyCompletedSteps.forEach(function(step){let completedStep=document.getElementById("completed-step-"+step);let inactiveStep=document.getElementById("step-"+step);completedStep.classList.remove("hidden");inactiveStep.classList+=" hidden"})},ajax:function(data,button=null,showMessages=true){return fetch(ajaxurl,{method:"POST",body:data}).then(response=>{if(button){if(button.children.length>0){button.children[0].classList+=" hidden"}if(button.id==="connect_plausible_analytics"&&response.status===200){button.innerText=plausible_analytics_i18n.connected}else{button.removeAttribute("disabled")}}if(response.status===200){return response.json()}return false}).then(response=>{if(showMessages===true){plausible.showMessages()}let event=new CustomEvent("plausibleAjaxDone",{detail:response});document.dispatchEvent(event);if(response.data!==undefined){return response.data}else{return response}})},showMessages:function(){let messages=plausible.fetchMessages();messages.then(function(messages){if(messages.error!==false){plausible.showMessage(messages.error,"error")}else if(messages.notice!==false){plausible.showMessage(messages.notice,"notice")}else if(messages.success!==false){plausible.showMessage(messages.success,"success")}if(messages.additional.length===0||document.getElementById("plausible-analytics-wizard")!==null){return}if(messages.additional.id!==undefined&&messages.additional.message){plausible.showAdditionalMessage(messages.additional.message,messages.additional.id)}else if(messages.additional.id!==undefined&&messages.additional.message===""){plausible.removeAdditionalMessage(messages.additional.id)}})},fetchMessages:function(){let data=new FormData;data.append("action","plausible_analytics_messages");let result=plausible.ajax(data,null,false);return result.then(function(response){return response})},showMessage:function(message,type="success"){if(type==="error"){document.getElementById("icon-error").classList.remove("hidden");document.getElementById("icon-success").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}else if(type==="notice"){document.getElementById("icon-notice").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-success").classList.add("hidden")}else{document.getElementById("icon-success").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}let notice=document.getElementById("plausible-analytics-notice");document.getElementById("plausible-analytics-notice-text").innerHTML=message;notice.classList.remove("hidden");setTimeout(function(){notice.classList.replace("opacity-0","opacity-100")},200);if(type!=="error"){setTimeout(function(){notice.classList.replace("opacity-100","opacity-0");setTimeout(function(){notice.classList+=" hidden"},200)},2e3)}},showAdditionalMessage:function(html,target){let targetElem=document.querySelector(`[name='${target}']`);let container=targetElem.closest(".plausible-analytics-group");if(container.children.length>0){for(let i=0;i0){for(let i=0;i{if(!document.location.href.includes("plausible_analytics")){return}let plausible={nonceElem:document.getElementById("_wpnonce"),nonce:"",showWizardElem:document.getElementById("show_wizard"),domainNameElem:document.getElementById("domain_name"),apiTokenElem:document.getElementById("api_token"),createAPITokenElems:document.getElementsByClassName("plausible-create-api-token"),buttonElems:document.getElementsByClassName("plausible-analytics-button"),stepElems:document.getElementsByClassName("plausible-analytics-wizard-next-step"),init:function(){if(document.location.hash===""&&document.getElementById("plausible-analytics-wizard")!==null){document.location.hash="#welcome_slide"}if(this.nonceElem!==null){this.nonce=this.nonceElem.value}this.toggleWizardStep();window.addEventListener("hashchange",this.toggleWizardStep);if(this.showWizardElem!==null){this.showWizardElem.addEventListener("click",this.showWizard)}if(this.domainNameElem!==null){this.domainNameElem.addEventListener("keyup",this.disableConnectButton)}if(this.apiTokenElem!==null){this.apiTokenElem.addEventListener("keyup",this.disableConnectButton)}if(this.createAPITokenElems.length>0){for(let i=0;i0){for(let i=0;i0){for(let i=0;i0){button.children[0].classList.remove("hidden")}button.setAttribute("disabled","disabled");plausible.ajax(form,button)},maybeDisableOptions:function(capabilities){let options=document.querySelectorAll("button[data-caps]");options.forEach(function(option){let caps=option.dataset.caps.split(",");let disable=false;caps.forEach(function(cap){if(capabilities[cap]===false){disable=true}});if(disable===true){if(option.dataset.status==="on"){option.dispatchEvent(new Event("click",{bubbles:true}))}}})},validateInput:function(input){if(input.name==="domain_name"&&input.value.match(/^(https?:\/\/)?(www.)?/).length>0){input.value=input.value.replace(/^(https?:\/\/)?(www.)?/,"")}return input},saveOptionOnNext:function(e){let hash=document.location.hash.replace("#","");if(hash!=="api_token_slide"&&hash!=="domain_name_slide"){return}let form=e.target.closest(".plausible-analytics-wizard-step-section");let inputs=form.getElementsByTagName("INPUT");let options=[];for(let input of inputs){input=plausible.validateInput(input);options.push({name:input.name,value:input.value})}let data=new FormData;data.append("action","plausible_analytics_save_options");data.append("options",JSON.stringify(options));data.append("_nonce",plausible.nonce);plausible.ajax(data).then(response=>{if(hash==="api_token_slide"&&response.success===true){let stats_button=document.getElementById("enable_analytics_dashboard_view_stats_in_wordpress");stats_button.removeAttribute("disabled")}})},disableConnectButton:function(e){let target=e.target;let button=document.getElementById("connect_plausible_analytics");let buttonIsHref=false;if(button===null){let slide_id=document.location.hash;button=document.querySelector(slide_id+" .plausible-analytics-wizard-next-step");buttonIsHref=true}if(button===null){return}if(target.value!==""){if(!buttonIsHref){button.disabled=false}else{button.classList.remove("pointer-events-none");button.classList.replace("bg-gray-200","bg-indigo-600")}return}if(!buttonIsHref){button.disabled=true;button.innerHTML=button.innerHTML.replace("Connected","Connect")}else{button.classList+=" pointer-events-none";button.classList.replace("bg-indigo-600","bg-gray-200")}},createAPIToken:function(e){e.preventDefault();let domain=document.getElementById("domain_name").value;domain=domain.replaceAll("/","%2F");window.open(`${plausible_analytics_hosted_domain}/${domain}/settings/integrations?new_token=WordPress`,"_blank","location=yes,height=768,width=1024,scrollbars=yes,status=no")},showWizard:function(e){let data=new FormData;data.append("action","plausible_analytics_show_wizard");data.append("_nonce",e.target.dataset.nonce);plausible.ajax(data)},toggleWizardStep:function(){if(document.getElementById("plausible-analytics-wizard")===null){return}const hash=document.location.hash.substring(1).replace("_slide","");let allSteps=document.querySelectorAll(".plausible-analytics-wizard-step");let activeSteps=document.querySelectorAll(".plausible-analytics-wizard-active-step");let completedSteps=document.querySelectorAll(".plausible-analytics-wizard-completed-step");for(let i=0;in);if(currentlyCompletedSteps.length<1){return}currentlyCompletedSteps.forEach(function(step){let completedStep=document.getElementById("completed-step-"+step);let inactiveStep=document.getElementById("step-"+step);completedStep.classList.remove("hidden");inactiveStep.classList+=" hidden"})},ajax:function(data,button=null,showMessages=true){return fetch(ajaxurl,{method:"POST",body:data}).then(response=>{if(button){if(button.children.length>0){button.children[0].classList+=" hidden"}if(button.id==="connect_plausible_analytics"&&response.status===200){button.innerText=plausible_analytics_i18n.connected}else{button.removeAttribute("disabled")}}if(response.status===200||response.status===402){return response.json()}return false}).then(response=>{if(showMessages===true){plausible.showMessages()}let event=new CustomEvent("plausibleAjaxDone",{detail:response});document.dispatchEvent(event);if(response.data!==undefined){return response.data}else{return response}})},showMessages:function(){let messages=plausible.fetchMessages();messages.then(function(messages){if(messages.error!==false){plausible.showMessage(messages.error,"error")}else if(messages.notice!==false){plausible.showMessage(messages.notice,"notice")}else if(messages.success!==false){plausible.showMessage(messages.success,"success")}if(messages.additional.length===0||document.getElementById("plausible-analytics-wizard")!==null){return}if(messages.additional.id!==undefined&&messages.additional.message){plausible.showAdditionalMessage(messages.additional.message,messages.additional.id)}else if(messages.additional.id!==undefined&&messages.additional.message===""){plausible.removeAdditionalMessage(messages.additional.id)}})},fetchMessages:function(){let data=new FormData;data.append("action","plausible_analytics_messages");let result=plausible.ajax(data,null,false);return result.then(function(response){return response})},showMessage:function(message,type="success"){if(type==="error"){document.getElementById("icon-error").classList.remove("hidden");document.getElementById("icon-success").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}else if(type==="notice"){document.getElementById("icon-notice").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-success").classList.add("hidden")}else{document.getElementById("icon-success").classList.remove("hidden");document.getElementById("icon-error").classList.add("hidden");document.getElementById("icon-notice").classList.add("hidden")}let notice=document.getElementById("plausible-analytics-notice");document.getElementById("plausible-analytics-notice-text").innerHTML=message;notice.classList.remove("hidden");setTimeout(function(){notice.classList.replace("opacity-0","opacity-100")},200);if(type!=="error"){setTimeout(function(){notice.classList.replace("opacity-100","opacity-0");setTimeout(function(){notice.classList+=" hidden"},200)},2e3)}},showAdditionalMessage:function(html,target){let targetElem=document.querySelector(`[name='${target}']`);let container=targetElem.closest(".plausible-analytics-group");if(container.children.length>0){for(let i=0;i0){for(let i=0;i [ '404' => [ - 'label' => esc_html__( '404 error pages', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-404-error-pages', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => '404', - 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), - 'caps' => [ self::CAP_GOALS ], + 'label' => esc_html__( '404 error pages', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-404-error-pages', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => '404', + 'caps' => [ self::CAP_GOALS ], ], 'outbound-links' => [ - 'label' => esc_html__( 'Outbound links', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-external-link-clicks', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'outbound-links', - 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), - 'caps' => [ self::CAP_GOALS ], + 'label' => esc_html__( 'Outbound links', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-external-link-clicks', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'outbound-links', + 'caps' => [ self::CAP_GOALS ], ], 'file-downloads' => [ - 'label' => esc_html__( 'File downloads', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-file-downloads', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'file-downloads', - 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS ] ), - 'caps' => [ self::CAP_GOALS ], + 'label' => esc_html__( 'File downloads', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-file-downloads', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'file-downloads', + 'caps' => [ self::CAP_GOALS ], ], 'search' => [ - 'label' => esc_html__( 'Search queries', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-site-search-tracking', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'search', - 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), - 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], + 'label' => esc_html__( 'Search queries', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-site-search-tracking', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'search', + 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], ], 'tagged-events' => [ - 'label' => esc_html__( 'Custom events', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-setup-custom-events-to-track-goal-conversions', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'tagged-events', - 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), - 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], + 'label' => esc_html__( 'Custom events', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-setup-custom-events-to-track-goal-conversions', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'tagged-events', + 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], ], 'revenue' => [ - 'label' => esc_html__( 'Ecommerce revenue', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-ecommerce-revenue', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'revenue', - 'disabled' => ! empty( $settings[ 'self_hosted_domain' ] ) || ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_FUNNELS, self::CAP_PROPS, self::CAP_REVENUE ] ), - 'caps' => [ self::CAP_GOALS, self::CAP_FUNNELS, self::CAP_PROPS, self::CAP_REVENUE ], + 'label' => esc_html__( 'Ecommerce revenue', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-ecommerce-revenue', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'revenue', + 'caps' => [ self::CAP_GOALS, self::CAP_FUNNELS, self::CAP_PROPS, self::CAP_REVENUE ], ], 'pageview-props' => [ - 'label' => esc_html__( 'Authors and categories', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-send-custom-properties', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'pageview-props', - 'disabled' => ! $this->token_has_cap( [ self::CAP_PROPS ] ), - 'caps' => [ self::CAP_PROPS ], + 'label' => esc_html__( 'Authors and categories', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-send-custom-properties', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'pageview-props', + 'caps' => [ self::CAP_PROPS ], ], 'form-completions' => [ - 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), - 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-form-completions', - 'slug' => 'enhanced_measurements', - 'type' => 'checkbox', - 'value' => 'form-completions', - 'disabled' => ! $this->token_has_cap( [ self::CAP_GOALS, self::CAP_PROPS ] ), - 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], + 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-form-completions', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'form-completions', + 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], ], 'hash' => [ 'label' => esc_html__( 'Hash-based routing', 'plausible-analytics' ), @@ -496,22 +488,6 @@ private function init() { new Hooks(); } - private function token_has_cap( $caps ) { - static $stored_caps = []; - - if ( empty( $stored_caps ) ) { - $stored_caps = get_option( 'plausible_analytics_api_token_caps', [] ); - } - - foreach ( $caps as $cap ) { - if ( empty( $stored_caps[ $cap ] ) ) { - return false; - } - } - - return true; - } - /** * Load all available user roles as a list (sorted alphabetically) of checkboxes to be processed by the Settings * API. @@ -751,4 +727,20 @@ public function render_analytics_dashboard() { name ] = trim( $option->value ); @@ -292,7 +292,6 @@ public function save_options() { // Validate Plugin Token, if this is the Plugin Token field. if ( $option->name === 'api_token' ) { $this->validate_api_token( $option->value ); - $caps = $this->store_capabilities( $option->value ); $additional = $this->maybe_render_additional_message( $option->name, $option->value ); @@ -304,10 +303,6 @@ public function save_options() { Messages::set_success( __( 'Settings saved.', 'plausible-analytics' ) ); - if ( ! empty( $caps ) ) { - wp_send_json_success( [ 'capabilities' => $caps ], 200 ); - } - wp_send_json_success( null, 200 ); } @@ -341,40 +336,4 @@ private function validate_api_token( $token = '' ) { wp_send_json_error( 'invalid_api_token', 400 ); } } - - /** - * Stores the capabilities for the currently entered API token in the DB for later use. - * - * @param $token - * - * @return false|array - */ - private function store_capabilities( $token = '' ) { - $client_factory = new ClientFactory( $token ); - /** @var Client $client */ - $client = $client_factory->build(); - - if ( ! $client instanceof Client ) { - return false; - } - - /** @var Client\Model\CapabilitiesFeatures $features */ - $features = $client->get_features(); - - if ( ! $features ) { - return false; - } - - $caps = [ - 'funnels' => $features->getFunnels(), - 'goals' => $features->getGoals(), - 'props' => $features->getProps(), - 'revenue' => $features->getRevenueGoals(), - 'stats' => $features->getStatsApi(), - ]; - - update_option( 'plausible_analytics_api_token_caps', $caps ); - - return $caps; - } } diff --git a/src/Client.php b/src/Client.php index a972d02..ae623a5 100644 --- a/src/Client.php +++ b/src/Client.php @@ -212,7 +212,45 @@ private function send_json_error( $e, $error_message ) { Messages::set_error( sprintf( $error_message, $message ) ); - wp_send_json_error( null, $code ); + $caps = $this->store_capabilities(); + + wp_send_json_error( [ 'capabilities' => $caps ], $code ); + } + + /** + * Stores the capabilities for the currently entered API token in the DB for later use. + * + * @param $token + * + * @return false|array + */ + private function store_capabilities( $token = '' ) { + $client_factory = new ClientFactory( $token ); + /** @var Client $client */ + $client = $client_factory->build(); + + if ( ! $client instanceof Client ) { + return false; + } + + /** @var Client\Model\CapabilitiesFeatures $features */ + $features = $client->get_features(); + + if ( ! $features ) { + return false; + } + + $caps = [ + 'funnels' => $features->getFunnels(), + 'goals' => $features->getGoals(), + 'props' => $features->getProps(), + 'revenue' => $features->getRevenueGoals(), + 'stats' => $features->getStatsApi(), + ]; + + update_option( 'plausible_analytics_api_token_caps', $caps ); + + return $caps; } /** From 274f79f30b9e2c23fb2c89f1e8b4653f4a79cf9e Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 19:33:13 +0100 Subject: [PATCH 5/7] Fixed: capabilities of Form completions and Custom Events. --- src/Admin/Settings/Page.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Admin/Settings/Page.php b/src/Admin/Settings/Page.php index 7116d7e..f87c60a 100644 --- a/src/Admin/Settings/Page.php +++ b/src/Admin/Settings/Page.php @@ -181,7 +181,7 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'tagged-events', - 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], + 'caps' => [ self::CAP_GOALS ], ], 'revenue' => [ 'label' => esc_html__( 'Ecommerce revenue', 'plausible-analytics' ), @@ -205,7 +205,7 @@ public function __construct() { 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'form-completions', - 'caps' => [ self::CAP_GOALS, self::CAP_PROPS ], + 'caps' => [ self::CAP_GOALS ], ], 'hash' => [ 'label' => esc_html__( 'Hash-based routing', 'plausible-analytics' ), From b216a31237c75538e28b787884cd4dcdef39211c Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 21:33:45 +0100 Subject: [PATCH 6/7] Minor refactor. --- src/Client.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.php b/src/Client.php index ae623a5..e6e4406 100644 --- a/src/Client.php +++ b/src/Client.php @@ -212,7 +212,7 @@ private function send_json_error( $e, $error_message ) { Messages::set_error( sprintf( $error_message, $message ) ); - $caps = $this->store_capabilities(); + $caps = $this->update_capabilities(); wp_send_json_error( [ 'capabilities' => $caps ], $code ); } @@ -224,7 +224,7 @@ private function send_json_error( $e, $error_message ) { * * @return false|array */ - private function store_capabilities( $token = '' ) { + private function update_capabilities( $token = '' ) { $client_factory = new ClientFactory( $token ); /** @var Client $client */ $client = $client_factory->build(); From 83711d960a6ba8f05ea97d065eafdc4d6fb82ea4 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 19 Mar 2025 21:44:55 +0100 Subject: [PATCH 7/7] Nothing to test here. --- src/Client.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Client.php b/src/Client.php index e6e4406..8a70453 100644 --- a/src/Client.php +++ b/src/Client.php @@ -223,6 +223,8 @@ private function send_json_error( $e, $error_message ) { * @param $token * * @return false|array + * + * @codeCoverageIgnore */ private function update_capabilities( $token = '' ) { $client_factory = new ClientFactory( $token );