From 5af1eef1fc8235aef1644000e5b34c4161d741aa Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Thu, 31 Oct 2024 12:57:48 +0200 Subject: [PATCH 1/2] refactor: use tsdk_translate_link upgrade links and load Formbricks from SDK --- inc/class-main.php | 2 +- inc/class-pro.php | 2 +- inc/class-registration.php | 4 +- inc/plugins/class-dashboard.php | 106 ++++++++++++++++++++------------ otter-blocks.php | 2 +- package-lock.json | 37 +++++------ package.json | 3 +- src/dashboard/index.js | 31 +++++----- 8 files changed, 103 insertions(+), 84 deletions(-) diff --git a/inc/class-main.php b/inc/class-main.php index 814eca3c6..f91b3e90b 100644 --- a/inc/class-main.php +++ b/inc/class-main.php @@ -579,7 +579,7 @@ public function about_page() { 'location' => 'otter', 'logo' => esc_url_raw( OTTER_BLOCKS_URL . 'assets/images/logo-alt.png' ), 'has_upgrade_menu' => ! DEFINED( 'OTTER_PRO_VERSION' ), - 'upgrade_link' => tsdk_utmify( Pro::get_url(), 'editor', Pro::get_reference() ), + 'upgrade_link' => tsdk_translate_link( tsdk_utmify( Pro::get_url(), 'editor', Pro::get_reference() ) ), 'upgrade_text' => __( 'Get Otter Pro', 'otter-blocks' ), ); } diff --git a/inc/class-pro.php b/inc/class-pro.php index 56dfd0478..7ef94b576 100644 --- a/inc/class-pro.php +++ b/inc/class-pro.php @@ -250,7 +250,7 @@ public function render_metabox_upsell( $post_type ) {
  • - + diff --git a/inc/class-registration.php b/inc/class-registration.php index 3d3c01737..1c50d22cb 100644 --- a/inc/class-registration.php +++ b/inc/class-registration.php @@ -252,8 +252,8 @@ public function enqueue_block_editor_assets() { 'hasNeve' => defined( 'NEVE_VERSION' ), 'hasPro' => Pro::is_pro_installed(), 'isProActive' => Pro::is_pro_active(), - 'upgradeLink' => tsdk_utmify( Pro::get_url(), 'editor', Pro::get_reference() ), - 'patternsLink' => tsdk_utmify( Pro::get_patterns_url(), 'editor', Pro::get_reference() ), + 'upgradeLink' => tsdk_translate_link( tsdk_utmify( Pro::get_url(), 'editor', Pro::get_reference() ) ), + 'patternsLink' => tsdk_translate_link( tsdk_utmify( Pro::get_patterns_url(), 'editor', Pro::get_reference() ) ), 'should_show_upsell' => Pro::should_show_upsell(), 'assetsPath' => OTTER_BLOCKS_URL . 'assets', 'updatePath' => admin_url( 'update-core.php' ), diff --git a/inc/plugins/class-dashboard.php b/inc/plugins/class-dashboard.php index df3306463..9a519a138 100644 --- a/inc/plugins/class-dashboard.php +++ b/inc/plugins/class-dashboard.php @@ -179,7 +179,7 @@ public function form_submissions_callback() { Otter Form Submissions Upsell

    - + OTTER_BLOCKS_VERSION, - 'assetsPath' => OTTER_BLOCKS_URL . 'assets/', - 'stylesExist' => is_dir( $basedir ) || boolval( get_transient( 'otter_animations_parsed' ) ), - 'hasPro' => Pro::is_pro_installed(), - 'upgradeLink' => tsdk_utmify( Pro::get_url(), 'options', Pro::get_reference() ), - 'docsLink' => Pro::get_docs_url(), - 'showFeedbackNotice' => $this->should_show_feedback_notice(), - 'deal' => ! Pro::is_pro_installed() ? $offer->get_localized_data() : array(), - 'hasOnboarding' => false !== get_theme_support( FSE_Onboarding::SUPPORT_KEY ), - 'days_since_install' => round( ( time() - get_option( 'otter_blocks_install', time() ) ) / DAY_IN_SECONDS ), - 'rootUrl' => get_site_url(), - 'neveThemePreviewUrl' => esc_url( - add_query_arg( - array( - 'theme' => 'neve', - ), - admin_url( 'theme-install.php' ) - ) + $this->get_dashboard_data() + ); + + $this->load_survey(); + } + + /** + * Get the dashboard data to store in global object. + * + * @return array + */ + public function get_dashboard_data() { + $wp_upload_dir = wp_upload_dir( null, false ); + $basedir = $wp_upload_dir['basedir'] . '/themeisle-gutenberg/'; + $offer = new LimitedOffers(); + + $global_data = array( + 'version' => OTTER_BLOCKS_VERSION, + 'assetsPath' => OTTER_BLOCKS_URL . 'assets/', + 'stylesExist' => is_dir( $basedir ) || boolval( get_transient( 'otter_animations_parsed' ) ), + 'hasPro' => Pro::is_pro_installed(), + 'upgradeLink' => tsdk_translate_link( tsdk_utmify( Pro::get_url(), 'options', Pro::get_reference() ) ), + 'docsLink' => Pro::get_docs_url(), + 'showFeedbackNotice' => $this->should_show_feedback_notice(), + 'deal' => ! Pro::is_pro_installed() ? $offer->get_localized_data() : array(), + 'hasOnboarding' => false !== get_theme_support( FSE_Onboarding::SUPPORT_KEY ), + 'days_since_install' => (int) round( ( time() - get_option( 'otter_blocks_install', time() ) ) / DAY_IN_SECONDS ), + 'rootUrl' => get_site_url(), + 'neveThemePreviewUrl' => esc_url( + add_query_arg( + array( + 'theme' => 'neve', ), - 'neveThemeActivationUrl' => esc_url( - add_query_arg( - array( - 'action' => 'activate', - 'stylesheet' => 'neve', - '_wpnonce' => wp_create_nonce( 'switch-theme_neve' ), - ), - admin_url( 'themes.php' ) - ) + admin_url( 'theme-install.php' ) + ) + ), + 'neveThemeActivationUrl' => esc_url( + add_query_arg( + array( + 'action' => 'activate', + 'stylesheet' => 'neve', + '_wpnonce' => wp_create_nonce( 'switch-theme_neve' ), ), - 'neveDashboardUrl' => esc_url( - add_query_arg( - array( - 'page' => 'neve-welcome', - ), - admin_url( 'admin.php' ) - ) + admin_url( 'themes.php' ) + ) + ), + 'neveDashboardUrl' => esc_url( + add_query_arg( + array( + 'page' => 'neve-welcome', ), - 'neveInstalled' => defined( 'NEVE_VERSION' ), + admin_url( 'admin.php' ) ) - ) + ), + 'neveInstalled' => defined( 'NEVE_VERSION' ), ); + + $this->load_survey(); } /** @@ -720,6 +734,18 @@ public function form_submissions_widget_content() { ! defined( 'OTTER_PRO_VERSION' ), 'pro_name' => __( 'Otter Blocks Pro', 'otter-blocks' ), 'logo' => OTTER_BLOCKS_URL . '/assets/images/logo-alt.png', - 'cta_link' => tsdk_utmify( 'https://themeisle.com/plugins/otter-blocks/upgrade/?discount=LOYALUSER583&dvalue=60#pricing', 'otter-welcome', 'notice' ), + 'cta_link' => tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/plugins/otter-blocks/upgrade/?discount=LOYALUSER583&dvalue=60#pricing', 'otter-welcome', 'notice' ) ), ]; } ); diff --git a/package-lock.json b/package-lock.json index 5632a1465..97c0c062f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,9 @@ "packages": { "": { "name": "otter-blocks", - "version": "3.0.4", + "version": "3.0.5", "license": "GPL-2.0+", "dependencies": { - "@formbricks/js": "^2.2.0", "@wordpress/icons": "^9.46.0", "array-move": "^3.0.1", "classnames": "^2.5.1", @@ -25,7 +24,7 @@ }, "devDependencies": { "@automattic/babel-plugin-replace-textdomain": "^1.0.35", - "@playwright/test": "^1.48.1", + "@playwright/test": "^1.48.2", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", @@ -2774,15 +2773,6 @@ "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==", "dev": true }, - "node_modules/@formbricks/js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@formbricks/js/-/js-2.2.0.tgz", - "integrity": "sha512-sKduJRgMZInOY141tIQN6RSsA4Wah6liqzCDDdGgv9t2FgTaNjSYGNrLlBEdQN62lRbVm5dk0+ZzMWyYrNhZWw==", - "license": "MIT", - "peerDependencies": { - "zod": "3.x" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -3670,13 +3660,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.48.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.1.tgz", - "integrity": "sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==", + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.48.1" + "playwright": "1.48.2" }, "bin": { "playwright": "cli.js" @@ -27886,13 +27876,13 @@ } }, "node_modules/playwright": { - "version": "1.48.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.1.tgz", - "integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==", + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.1" + "playwright-core": "1.48.2" }, "bin": { "playwright": "cli.js" @@ -27905,9 +27895,9 @@ } }, "node_modules/playwright-core": { - "version": "1.48.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.1.tgz", - "integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==", + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -34930,6 +34920,7 @@ "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 098acc830..ef12f812e 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "lasttranslator": "Themeisle Translate Team " }, "dependencies": { - "@formbricks/js": "^2.2.0", "@wordpress/icons": "^9.46.0", "array-move": "^3.0.1", "classnames": "^2.5.1", @@ -80,7 +79,7 @@ }, "devDependencies": { "@automattic/babel-plugin-replace-textdomain": "^1.0.35", - "@playwright/test": "^1.48.1", + "@playwright/test": "^1.48.2", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", diff --git a/src/dashboard/index.js b/src/dashboard/index.js index 844a12a64..0590a2ffc 100644 --- a/src/dashboard/index.js +++ b/src/dashboard/index.js @@ -2,8 +2,6 @@ /** * External dependencies. */ -/* eslint-disable import/no-unresolved */ -import formbricks from '@formbricks/js/app'; /** * WordPress dependencies. @@ -52,18 +50,6 @@ const convertToCategory = ( number, scale = 1 ) => { } }; -if ( 'undefined' !== typeof window ) { - formbricks.init({ - environmentId: 'clp9hqm8c1osfdl2ixwd0k0iz', - apiHost: 'https://app.formbricks.com', - userId: 'otter_' + ( undefined !== window.otterObj?.license?.key ? window.otterObj.license.key : window.otterObj.rootUrl.replace( /[^\w\d]*/g, '' ) ), - attributes: { - plan: undefined !== window.otterObj?.license?.type ? window.otterObj.license.type : 'free', - days_since_install: convertToCategory( window.otterObj.days_since_install ) - } - }); -} - const App = () => { const [ currentTab, setTab ] = useState( getInitialStateFromURLQuery() ); @@ -89,3 +75,20 @@ const App = () => { const root = createRoot( document.getElementById( 'otter' ) ); root.render( ); + +/** + * Initialize the formbricks survey. + * + * @see https://github.com/formbricks/setup-examples/tree/main/html + */ +window.addEventListener('themeisle:survey:loaded', function () { + window?.tsdk_formbricks?.init?.({ + environmentId: 'clp9hqm8c1osfdl2ixwd0k0iz', + apiHost: 'https://app.formbricks.com', + userId: 'otter_' + ( undefined !== window.otterObj?.license?.key ? window.otterObj.license.key : window.otterObj.rootUrl.replace( /[^\w\d]*/g, '' ) ), + attributes: { + plan: undefined !== window.otterObj?.license?.type ? window.otterObj.license.type : 'free', + days_since_install: convertToCategory( window.otterObj.days_since_install ) + } + }); +}); From f0956eabce0ea5099f2ca7b88743b4fbb91c12e0 Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Thu, 31 Oct 2024 13:40:08 +0200 Subject: [PATCH 2/2] refactor: add unit test for dashboard data --- inc/plugins/class-dashboard.php | 8 +-- tests/test-dashboard.php | 100 ++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 tests/test-dashboard.php diff --git a/inc/plugins/class-dashboard.php b/inc/plugins/class-dashboard.php index 9a519a138..3fb619f7d 100644 --- a/inc/plugins/class-dashboard.php +++ b/inc/plugins/class-dashboard.php @@ -192,9 +192,7 @@ public function form_submissions_callback() { * @access public */ public function enqueue_options_assets() { - $wp_upload_dir = wp_upload_dir( null, false ); - $basedir = $wp_upload_dir['basedir'] . '/themeisle-gutenberg/'; - $asset_file = include OTTER_BLOCKS_PATH . '/build/dashboard/index.asset.php'; + $asset_file = include OTTER_BLOCKS_PATH . '/build/dashboard/index.asset.php'; wp_enqueue_style( 'otter-blocks-styles', @@ -213,8 +211,6 @@ public function enqueue_options_assets() { wp_set_script_translations( 'otter-blocks-scripts', 'otter-blocks' ); - $offer = new LimitedOffers(); - wp_localize_script( 'otter-blocks-scripts', 'otterObj', @@ -275,7 +271,7 @@ public function get_dashboard_data() { 'neveInstalled' => defined( 'NEVE_VERSION' ), ); - $this->load_survey(); + return apply_filters( 'otter_dashboard_data', $global_data ); } /** diff --git a/tests/test-dashboard.php b/tests/test-dashboard.php new file mode 100644 index 000000000..dbc36f417 --- /dev/null +++ b/tests/test-dashboard.php @@ -0,0 +1,100 @@ +dashboard = Dashboard::instance(); + + + if ( ! function_exists( 'tsdk_translate_link' ) ) { + function tsdk_translate_link( $link ) { + return $link; + } + } + + if ( ! function_exists( 'tsdk_utmify' ) ) { + function tsdk_utmify( $link ) { + return $link; + } + } + } + + /** + * Test get_dashboard_data returns expected structure + */ + public function test_get_dashboard_data() { + $data = $this->dashboard->get_dashboard_data(); + + // Test required keys exist + $required_keys = array( + 'version', + 'assetsPath', + 'stylesExist', + 'hasPro', + 'upgradeLink', + 'docsLink', + 'showFeedbackNotice', + 'deal', + 'hasOnboarding', + 'days_since_install', + 'rootUrl', + 'neveThemePreviewUrl', + 'neveThemeActivationUrl', + 'neveDashboardUrl', + 'neveInstalled', + ); + + foreach ( $required_keys as $key ) { + $this->assertArrayHasKey( $key, $data, "Dashboard data missing required key: {$key}" ); + } + + // Test specific value types + $this->assertIsString( $data['version'], 'Version should be a string' ); + $this->assertIsString( $data['assetsPath'], 'AssetsPath should be a string' ); + $this->assertIsBool( $data['stylesExist'], 'StylesExist should be a boolean' ); + $this->assertIsBool( $data['hasPro'], 'HasPro should be a boolean' ); + $this->assertIsString( $data['upgradeLink'], 'UpgradeLink should be a string' ); + $this->assertIsString( $data['docsLink'], 'DocsLink should be a string' ); + $this->assertIsBool( $data['showFeedbackNotice'], 'ShowFeedbackNotice should be a boolean' ); + $this->assertIsArray( $data['deal'], 'Deal should be an array' ); + $this->assertIsBool( $data['hasOnboarding'], 'HasOnboarding should be a boolean' ); + $this->assertIsInt( $data['days_since_install'], 'DaysSinceInstall should be an integer' ); + $this->assertIsString( $data['rootUrl'], 'RootUrl should be a string' ); + $this->assertIsString( $data['neveThemePreviewUrl'], 'NeveThemePreviewUrl should be a string' ); + $this->assertIsString( $data['neveThemeActivationUrl'], 'NeveThemeActivationUrl should be a string' ); + $this->assertIsString( $data['neveDashboardUrl'], 'NeveDashboardUrl should be a string' ); + $this->assertIsBool( $data['neveInstalled'], 'NeveInstalled should be a boolean' ); + + // Test version matches constant + $this->assertEquals( OTTER_BLOCKS_VERSION, $data['version'], 'Version should match OTTER_BLOCKS_VERSION constant' ); + + // Test assets path + $this->assertStringContainsString( 'assets/', $data['assetsPath'], 'AssetsPath should contain "assets/" directory' ); + } + + /** + * Clean up test environment. + */ + public function tear_down() { + parent::tear_down(); + } +}