diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f2112511..51d1a7242 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +##### [Version 3.11.2](https://github.com/Codeinwp/visualizer/compare/v3.11.1...v3.11.2) (2024-05-23) + +- Fixed Visualizer block widget not loading +- Improved the popup rendering +- Enhanced security + ##### [Version 3.11.1](https://github.com/Codeinwp/visualizer/compare/v3.11.0...v3.11.1) (2024-05-14) - Fixed the permissions of the plans diff --git a/classes/Visualizer/Module/Admin.php b/classes/Visualizer/Module/Admin.php index 281bcca35..614f829f0 100644 --- a/classes/Visualizer/Module/Admin.php +++ b/classes/Visualizer/Module/Admin.php @@ -53,6 +53,7 @@ public function __construct( Visualizer_Plugin $plugin ) { parent::__construct( $plugin ); $this->_addAction( 'load-post.php', 'enqueueMediaScripts' ); $this->_addAction( 'load-post-new.php', 'enqueueMediaScripts' ); + $this->_addAction( 'enqueue_block_editor_assets', 'enqueueMediaScripts' ); $this->_addAction( 'admin_footer', 'renderTemplates' ); $this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 0 ); $this->_addAction( 'admin_menu', 'registerAdminMenu' ); @@ -330,7 +331,9 @@ public function feedbackReviewTrigger( $dumb ) { */ public function enqueueMediaScripts() { global $typenow; - if ( post_type_supports( $typenow, 'editor' ) ) { + global $current_screen; + + if ( post_type_supports( $typenow, 'editor' ) || $current_screen->id === 'widgets' ) { wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION ); // Load all the assets for the different libraries we support. @@ -1019,6 +1022,7 @@ public function renderLibraryPage() { } // enqueue charts array $ajaxurl = admin_url( 'admin-ajax.php' ); + wp_localize_script( 'visualizer-library', 'visualizer', diff --git a/classes/Visualizer/Module/Chart.php b/classes/Visualizer/Module/Chart.php index d13c1f00d..00de88a4e 100644 --- a/classes/Visualizer/Module/Chart.php +++ b/classes/Visualizer/Module/Chart.php @@ -1431,6 +1431,10 @@ public function getQueryData() { wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) ); } + if ( ! Visualizer_Module::is_pro() ) { + wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) ); + } + $params = wp_parse_args( $_POST['params'] ); $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT ); $query = trim( $params['query'], ';' ); @@ -1452,6 +1456,17 @@ public function getQueryData() { public function saveQuery() { check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' ); + if ( ! current_user_can( 'administrator' ) ) { + wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) ); + } + if ( ! is_super_admin() ) { + wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) ); + } + + if ( ! Visualizer_Module::is_pro() ) { + wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) ); + } + $chart_id = filter_input( INPUT_GET, 'chart', diff --git a/classes/Visualizer/Plugin.php b/classes/Visualizer/Plugin.php index 49517b512..af9ab7a30 100644 --- a/classes/Visualizer/Plugin.php +++ b/classes/Visualizer/Plugin.php @@ -28,7 +28,7 @@ class Visualizer_Plugin { const NAME = 'visualizer'; - const VERSION = '3.11.1'; + const VERSION = '3.11.2'; // custom post types const CPT_VISUALIZER = 'visualizer'; @@ -57,6 +57,8 @@ class Visualizer_Plugin { const ACTION_UPLOAD_DATA = 'visualizer-upload-data'; const ACTION_EXPORT_DATA = 'visualizer-export-data'; + const STORE_URL = 'https://store.themeisle.com/'; + /** *Action used for fetching specific users/roles for permissions. */ diff --git a/classes/Visualizer/Render/Library.php b/classes/Visualizer/Render/Library.php index 6263ef646..1c61c5cc5 100644 --- a/classes/Visualizer/Render/Library.php +++ b/classes/Visualizer/Render/Library.php @@ -206,7 +206,55 @@ private function getDisplayForm() { '; } + /** + * Renders pro charts blocker. + * + * @access private + */ + private function _renderProPopupBlocker() { + if ( Visualizer_Module::is_pro() ) { + return; + } + $license = get_option( 'visualizer_pro_license_data', 'free' ); + $license_key = ''; + $download_id = ''; + if ( ! empty( $license ) && is_object( $license ) ) { + $license_key = $license->key; + $download_id = $license->download_id; + } + $admin_license_url = admin_url( 'options-general.php#visualizer_pro_license' ); + $renew_license_url = tsdk_utmify( Visualizer_Plugin::STORE_URL . '?edd_license_key=' . $license_key . '&download_id=' . $download_id, 'visualizer_license_block' ); + echo ' +
+
+

Alert!

+

' . esc_html__( 'In order to edit premium charts, benefit from updates and support for Visualizer Premium plugin, please renew your license code or activate it.', 'visualizer' ) . '

+
+ + + + + + + +
+
+ '; + } /** * Renders library content. * @@ -215,10 +263,14 @@ private function getDisplayForm() { * @access private */ private function _renderLibrary() { + // Added by Ash/Upwork $filterBy = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended // Added by Ash/Upwork echo $this->custom_css; + + $this->_renderProPopupBlocker(); + echo '
'; echo ''; $this->getDisplayForm(); diff --git a/composer.lock b/composer.lock index 1692ba8f4..e496acc34 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "codeinwp/themeisle-sdk", - "version": "3.3.21", + "version": "3.3.22", "source": { "type": "git", "url": "https://github.com/Codeinwp/themeisle-sdk.git", - "reference": "c73f9f8e32ccfa933ef244eb284dd58e0dace8eb" + "reference": "1754febc3c25c4c884424e72801c4d3f2ec7097e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/c73f9f8e32ccfa933ef244eb284dd58e0dace8eb", - "reference": "c73f9f8e32ccfa933ef244eb284dd58e0dace8eb", + "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/1754febc3c25c4c884424e72801c4d3f2ec7097e", + "reference": "1754febc3c25c4c884424e72801c4d3f2ec7097e", "shasum": "" }, "require-dev": { @@ -42,9 +42,9 @@ ], "support": { "issues": "https://github.com/Codeinwp/themeisle-sdk/issues", - "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.21" + "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.22" }, - "time": "2024-04-29T13:20:44+00:00" + "time": "2024-05-17T15:04:51+00:00" }, { "name": "markbaker/complex", diff --git a/css/library.css b/css/library.css index 5fdd3c079..81017ca3d 100644 --- a/css/library.css +++ b/css/library.css @@ -522,6 +522,7 @@ div#visualizer-types ul, div#visualizer-types form p { .vizualizer-renew-notice-overlay { + display: none; position: fixed; top: 0; left: 0; @@ -532,7 +533,7 @@ div#visualizer-types ul, div#visualizer-types form p { } .vizualizer-renew-notice-popup { - display: block; + display: none; position: fixed; top: 50%; left: 50%; @@ -594,8 +595,17 @@ div#visualizer-types ul, div#visualizer-types form p { .vizualizer-renew-notice-close-icon { position: absolute; - top: 10px; - right: 10px; + top: -10px; + right: -70px; cursor: pointer; color: #333; + background: none; + border: none; + padding: 0; + outline: none; + /* Reset button styles */ + display: inline-block; + font: inherit; + text-align: inherit; + text-decoration: none; } diff --git a/css/media.css b/css/media.css index 9ce24b9b1..806f2dedb 100644 --- a/css/media.css +++ b/css/media.css @@ -1,5 +1,5 @@ /* - Version: 3.11.1 + Version: 3.11.2 */ #visualizer-library-view { padding: 30px 10px 10px 30px; diff --git a/index.php b/index.php index c98ccde74..cf5750c1b 100644 --- a/index.php +++ b/index.php @@ -2,8 +2,8 @@ /* Plugin Name: Visualizer: Tables and Charts for WordPress Plugin URI: https://themeisle.com/plugins/visualizer-charts-and-graphs/ - Description: A simple, easy to use and quite powerful tool to create, manage and embed interactive charts into your WordPress posts and pages. The plugin uses Google Visualization API to render charts, which supports cross-browser compatibility (adopting VML for older IE versions) and cross-platform portability to iOS and new Android releases. - Version: 3.11.1 + Description: Effortlessly create and embed responsive charts and tables with Visualizer, a powerful WordPress plugin that enhances data presentation from multiple sources. + Version: 3.11.2 Author: Themeisle Author URI: http://themeisle.com Requires at least: 5.2 diff --git a/js/library.js b/js/library.js index 4718d915c..d0efddd14 100644 --- a/js/library.js +++ b/js/library.js @@ -33,63 +33,13 @@ }); })(wp.media.view); -function createPopupProBlocker() { - - var link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css'; - document.head.appendChild(link); - - var overlay = document.createElement('div'); - overlay.classList.add('vizualizer-renew-notice-overlay'); - overlay.id = 'overlay-visualizer'; - document.body.appendChild(overlay); - - var popup = document.createElement('div'); - popup.classList.add('vizualizer-renew-notice-popup'); - - var closeIcon = document.createElement('i'); - closeIcon.classList.add('fas', 'fa-times', 'vizualizer-renew-notice-close-icon'); - closeIcon.addEventListener('click', function() { - document.body.removeChild(overlay); - popup.style.display = 'none'; - }); - popup.appendChild(closeIcon); - - var heading = document.createElement('h1'); - heading.textContent = 'Alert!'; - heading.classList.add('vizualizer-renew-notice-heading'); - popup.appendChild(heading); - - var message = document.createElement('p'); - message.textContent = 'In order to edit premium charts, benefit from updates and support for Visualizer Premium plugin, please renew your license code or activate it.'; - message.classList.add('vizualizer-renew-notice-message'); - popup.appendChild(message); - - var buttonsContainer = document.createElement('div'); - buttonsContainer.classList.add('vizualizer-renew-notice-buttons-container'); - - var link1 = document.createElement('a'); - link1.href = 'https://store.themeisle.com/'; - link1.target = '_blank'; - var button1 = document.createElement('button'); - button1.innerHTML = ' Renew License'; - button1.classList.add('vizualizer-renew-notice-button', 'vizualizer-renew-notice-renew-button'); - link1.appendChild(button1); - buttonsContainer.appendChild(link1); - - var link2 = document.createElement('a'); - link2.href = '/wp-admin/options-general.php#visualizer_pro_license'; - var button2 = document.createElement('button'); - button2.innerHTML = ' Activate License'; - button2.classList.add('vizualizer-renew-notice-button', 'vizualizer-renew-notice-activate-button'); - link2.appendChild(button2); - buttonsContainer.appendChild(link2); - - popup.appendChild(buttonsContainer); - - document.body.appendChild(popup); - +function createPopupProBlocker( $ , e ) { + if ( ! visualizer.is_pro_user && e.target.classList.contains('viz-is-pro-chart') ) { + $("#overlay-visualizer").css("display", "block"); + $(".vizualizer-renew-notice-popup").css("display", "block"); + return true; + } + return false; } (function ($, vmv, vu) { @@ -135,12 +85,11 @@ function createPopupProBlocker() { $(this).parent('form').submit(); }); - $('.visualizer-chart-shortcode').click(function (e) { + $('.visualizer-chart-shortcode').click(function (event) { - if ( ! visualizer.is_pro_user && e.target.classList.contains('viz-is-pro-chart') ) { - createPopupProBlocker(); - e.preventDefault(); - e.stopPropagation(); + if ( createPopupProBlocker( $, event ) ) { + event.preventDefault(); + event.stopPropagation(); return; } @@ -149,12 +98,12 @@ function createPopupProBlocker() { if (window.getSelection && document.createRange) { selection = window.getSelection(); range = document.createRange(); - range.selectNodeContents(e.target); + range.selectNodeContents(event.target); selection.removeAllRanges(); selection.addRange(range); } else if (document.selection && document.body.createTextRange) { range = document.body.createTextRange(); - range.moveToElementText(e.target); + range.moveToElementText(event.target); range.select(); } }); @@ -195,8 +144,7 @@ function createPopupProBlocker() { $('.visualizer-chart-edit').click(function (event) { - if ( ! visualizer.is_pro_user && event.target.classList.contains('viz-is-pro-chart') ) { - createPopupProBlocker(); + if ( createPopupProBlocker( $, event ) ) { return; } @@ -215,16 +163,14 @@ function createPopupProBlocker() { return false; }); $(".visualizer-chart-clone").on("click", function ( event ) { - if ( ! visualizer.is_pro_user && event.target.classList.contains('viz-is-pro-chart') ) { - createPopupProBlocker(); + if ( createPopupProBlocker( $, event ) ) { event.preventDefault(); } }); $(".visualizer-chart-export").on("click", function (event) { - if ( ! visualizer.is_pro_user && event.target.classList.contains('viz-is-pro-chart') ) { - createPopupProBlocker(); + if ( createPopupProBlocker( $, event ) ) { return; } @@ -249,8 +195,7 @@ function createPopupProBlocker() { }); $(".visualizer-chart-image").on("click", function (event) { - if ( ! visualizer.is_pro_user && event.target.classList.contains('viz-is-pro-chart') ) { - createPopupProBlocker(); + if ( createPopupProBlocker( $, event ) ) { return; } $('body').trigger('visualizer:action:specificchart', {action: 'image', id: $(this).attr("data-chart"), data: null, dataObj: {name: $(this).attr("data-chart-title")}}); diff --git a/package.json b/package.json index 476d19d1c..71ae10712 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "visualizer", - "version": "3.11.1", + "version": "3.11.2", "description": "Visualizer Lite", "repository": { "type": "git", diff --git a/readme.txt b/readme.txt index b29e3a214..e782ee453 100755 --- a/readme.txt +++ b/readme.txt @@ -226,6 +226,15 @@ Pay attention that to turn your shortcodes into graphs, your theme has to have ` == Changelog == +##### [Version 3.11.2](https://github.com/Codeinwp/visualizer/compare/v3.11.1...v3.11.2) (2024-05-23) + +- Fixed Visualizer block widget not loading +- Improved the popup rendering +- Enhanced security + + + + ##### [Version 3.11.1](https://github.com/Codeinwp/visualizer/compare/v3.11.0...v3.11.1) (2024-05-14) - Fixed the permissions of the plans diff --git a/tests/e2e/specs/gutenberg-editor.spec.js b/tests/e2e/specs/gutenberg-editor.spec.js index e5859b8cc..5da67cae7 100644 --- a/tests/e2e/specs/gutenberg-editor.spec.js +++ b/tests/e2e/specs/gutenberg-editor.spec.js @@ -31,7 +31,7 @@ test.describe( 'Charts with Gutenberg Editor', () => { test('new chart creation', async ( { admin, editor, page } ) => { await admin.createNewPost(); await editor.insertBlock( { name: 'visualizer/chart'} ); - + await expect( page.getByText('Make a new chart or display') ).toBeVisible(); await expect( page.getByLabel('Editor content').locator('a') ).toBeVisible(); @@ -40,7 +40,7 @@ test.describe( 'Charts with Gutenberg Editor', () => { // Create chart via popup. await page.frameLocator('iframe').getByRole('button', { name: 'Next' }).click(); await page.frameLocator('iframe').getByRole('button', { name: 'Create Chart' }).click(); - + await expect( page.getByRole('button', { name: 'Save', exact: true }) ).toBeVisible(); await page.getByRole('button', { name: 'Save', exact: true }).click(); await expect( page.getByRole('button', { name: 'Done' }) ).toBeVisible(); @@ -98,7 +98,7 @@ test.describe( 'Charts with Gutenberg Editor', () => { await page.getByRole('button', { name: 'Import from other chart' }).click(); await page.getByRole('button', { name: 'Import data from database' }).click(); - + const upgradeLinks = await page.locator('a').filter({ hasText: 'Upgrade Now' }).count(); expect( upgradeLinks ).toBe( 6 ); @@ -131,4 +131,21 @@ test.describe( 'Charts with Gutenberg Editor', () => { await expect(page.getByLabel('Visualizer', { exact: true }).locator('h1')).toContainText('Visualizer'); await page.getByRole('button', { name: ' Close dialog' }).click(); } ); + + test( 'check widgets', async ( { admin, editor, page } ) => { + await createChartWithAdmin( admin, page ); + + await admin.visitAdminPage( 'widgets.php' ); + + await page.getByLabel('Close', { exact: true }).click(); + await page.getByLabel('Toggle block inserter').click(); + await page.getByPlaceholder('Search').fill('visuali'); + await page.getByRole('option', { name: ' Visualizer Chart' }).click(); + await page.locator('div').filter({ hasText: /^Display an existing chart$/ }).click(); + await page.getByTitle('Insert Chart').first().click(); + + await expect(page.getByLabel('Block: Visualizer Chart')).toContainText('Visualizer'); + await expect(page.locator('rect').first()).toBeVisible(); + + } ); } ); diff --git a/tests/e2e/specs/upsell.spec.js b/tests/e2e/specs/upsell.spec.js index 9e63ffb3e..170b7ef71 100644 --- a/tests/e2e/specs/upsell.spec.js +++ b/tests/e2e/specs/upsell.spec.js @@ -9,99 +9,125 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); const { deleteAllCharts, getAssetFilePath, CHART_JS_LABELS, selectChartAdmin } = require('../utils/common'); test.describe( 'Upsell', () => { - test.beforeEach( async ( { admin, requestUtils, page } ) => { - await deleteAllCharts( requestUtils ); - await admin.visitAdminPage( 'admin.php?page=visualizer' ); - page.setDefaultTimeout( 5000 ); - } ); - - test( 'chart selection on admin', async ( { admin, page } ) => { - await admin.visitAdminPage( 'admin.php?page=visualizer&vaction=addnew' ); - await page.waitForURL( '**/admin.php?page=visualizer&vaction=addnew' ); - await page.waitForSelector('h1:text("Visualizer")'); - - expect( await page.frameLocator('iframe').locator('.pro-upsell').count() ).toBe( 11 ); - - const proUpsellElements = await page.frameLocator('iframe').locator('a.pro-upsell').all(); - - for (const element of proUpsellElements) { - const href = await element.getAttribute('href'); - const searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('charttypes'); - } - } ); - - test( 'chart settings on admin', async ( { admin, page } ) => { - await admin.visitAdminPage( 'admin.php?page=visualizer&vaction=addnew' ); - await page.waitForURL( '**/admin.php?page=visualizer&vaction=addnew' ); - await page.waitForSelector('h1:text("Visualizer")'); - await selectChartAdmin( page.frameLocator('iframe'), CHART_JS_LABELS.pie ); - - await expect( page.frameLocator('iframe').locator( '#viz-tabs' ) ).toBeVisible(); - - expect( await page.frameLocator('iframe').locator('#vz-chart-source .viz-group-title .dashicons-lock').count() ).toBe( 5 ); - - - const uploadFileUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_csv .only-pro-inner a'); - let href = await uploadFileUpsell.getAttribute('href'); - let searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('import-file'); - - const remoteImportUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_json .only-pro-inner a').first(); - href = await remoteImportUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('import-url'); - - const otherChartUpsell = page.frameLocator('iframe').locator('#vz-chart-source .viz-import-from-other .only-pro-inner a'); - href = await otherChartUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('import-chart'); - - const wpImportUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_query_wp .only-pro-inner a'); - href = await wpImportUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('import-wp'); - await page.frameLocator('iframe').getByRole('heading', { name: /Import from WordPress/ }).click(); - await expect(page.frameLocator('iframe').locator('#vz-chart-source')).toContainText('Upgrade to PRO to activate this feature!'); - - const dbImportUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_query .only-pro-inner a'); - href = await dbImportUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('db-query'); - - await page.frameLocator('iframe').getByRole('heading', { name: /Import from database/ }).click(); - await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade to Plus plan to activate this feature!'); - await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade Now'); - - await page.frameLocator('iframe').getByRole('link', { name: 'Settings' }).click(); - - const dataFilterConfigurationUpsell = page.frameLocator('iframe').locator('#vz-data-controls .only-pro-inner a'); - href = await dataFilterConfigurationUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('data-filter-configuration'); - - const frontendActionsUpsell = page.frameLocator('iframe').locator('#vz-frontend-actions .only-pro-inner a'); - href = await frontendActionsUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('frontend-actions'); - - const chartPermissionsUpsell = page.frameLocator('iframe').locator('#vz-permissions .only-pro-inner a'); - href = await chartPermissionsUpsell.getAttribute('href'); - searchParams = new URLSearchParams(href); - expect( searchParams.get('utm_campaign') ).toBe('chart-permissions'); - await page.frameLocator('iframe').getByRole('heading', { name: /Permissions/ }).click(); - await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade to Plus plan to activate this feature!'); - await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade Now'); - }); - - test( 'featured tab in Install Plugin (SDK)', async ( { admin, page } ) => { - await admin.visitAdminPage( 'plugin-install.php' ); - - // Those should be visible only when a PRO product is installed. - await expect( page.getByText('Image Optimization by Optimole') ).toBeHidden(); - await expect( page.locator('#the-list div').filter({ hasText: 'Otter Blocks' }).nth(1) ).toBeHidden(); - - await expect( page.getByLabel('Install Image Optimization by') ).toBeHidden(); - await expect( page.getByLabel('Install Otter Blocks') ).toBeHidden(); - }); + test.beforeEach( async ( { admin, requestUtils, page } ) => { + await deleteAllCharts( requestUtils ); + await admin.visitAdminPage( 'admin.php?page=visualizer' ); + page.setDefaultTimeout( 5000 ); + } ); + + test( 'chart selection on admin', async ( { admin, page } ) => { + await admin.visitAdminPage( 'admin.php?page=visualizer&vaction=addnew' ); + await page.waitForURL( '**/admin.php?page=visualizer&vaction=addnew' ); + await page.waitForSelector('h1:text("Visualizer")'); + + expect( await page.frameLocator('iframe').locator('.pro-upsell').count() ).toBe( 11 ); + + const proUpsellElements = await page.frameLocator('iframe').locator('a.pro-upsell').all(); + + for (const element of proUpsellElements) { + const href = await element.getAttribute('href'); + const searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('charttypes'); + } + } ); + + test( 'chart settings on admin', async ( { admin, page } ) => { + await admin.visitAdminPage( 'admin.php?page=visualizer&vaction=addnew' ); + await page.waitForURL( '**/admin.php?page=visualizer&vaction=addnew' ); + await page.waitForSelector('h1:text("Visualizer")'); + await selectChartAdmin( page.frameLocator('iframe'), CHART_JS_LABELS.pie ); + + await expect( page.frameLocator('iframe').locator( '#viz-tabs' ) ).toBeVisible(); + + expect( await page.frameLocator('iframe').locator('#vz-chart-source .viz-group-title .dashicons-lock').count() ).toBe( 5 ); + + + const uploadFileUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_csv .only-pro-inner a'); + let href = await uploadFileUpsell.getAttribute('href'); + let searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('import-file'); + + const remoteImportUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_json .only-pro-inner a').first(); + href = await remoteImportUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('import-url'); + + const otherChartUpsell = page.frameLocator('iframe').locator('#vz-chart-source .viz-import-from-other .only-pro-inner a'); + href = await otherChartUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('import-chart'); + + const wpImportUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_query_wp .only-pro-inner a'); + href = await wpImportUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('import-wp'); + await page.frameLocator('iframe').getByRole('heading', { name: /Import from WordPress/ }).click(); + await expect(page.frameLocator('iframe').locator('#vz-chart-source')).toContainText('Upgrade to PRO to activate this feature!'); + + const dbImportUpsell = page.frameLocator('iframe').locator('#vz-chart-source .visualizer_source_query .only-pro-inner a'); + href = await dbImportUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('db-query'); + + await page.frameLocator('iframe').getByRole('heading', { name: /Import from database/ }).click(); + await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade to Plus plan to activate this feature!'); + await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade Now'); + + await page.frameLocator('iframe').getByRole('link', { name: 'Settings' }).click(); + + const dataFilterConfigurationUpsell = page.frameLocator('iframe').locator('#vz-data-controls .only-pro-inner a'); + href = await dataFilterConfigurationUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('data-filter-configuration'); + + const frontendActionsUpsell = page.frameLocator('iframe').locator('#vz-frontend-actions .only-pro-inner a'); + href = await frontendActionsUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('frontend-actions'); + + const chartPermissionsUpsell = page.frameLocator('iframe').locator('#vz-permissions .only-pro-inner a'); + href = await chartPermissionsUpsell.getAttribute('href'); + searchParams = new URLSearchParams(href); + expect( searchParams.get('utm_campaign') ).toBe('chart-permissions'); + await page.frameLocator('iframe').getByRole('heading', { name: /Permissions/ }).click(); + await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade to Plus plan to activate this feature!'); + await expect(page.frameLocator('iframe').locator('#vz-db-wizard')).toContainText('Upgrade Now'); + }); + + test( 'featured tab in Install Plugin (SDK)', async ( { admin, page } ) => { + await admin.visitAdminPage( 'plugin-install.php' ); + + // Those should be visible only when a PRO product is installed. + await expect( page.getByText('Image Optimization by Optimole') ).toBeHidden(); + await expect( page.locator('#the-list div').filter({ hasText: 'Otter Blocks' }).nth(1) ).toBeHidden(); + + await expect( page.getByLabel('Install Image Optimization by') ).toBeHidden(); + await expect( page.getByLabel('Install Otter Blocks') ).toBeHidden(); + }); + + test( 'pro chart license lock', async ( { admin, page } ) => { + await admin.visitAdminPage('admin.php?page=visualizer'); + await page.waitForURL('**/admin.php?page=visualizer'); + + // Check if the popup HTML is present + const popupSelector = '.vizualizer-renew-notice-popup'; + const popup = await page.$(popupSelector); + expect(popup).not.toBeNull(); + + const heading = await page.$('h1.vizualizer-renew-notice-heading'); + expect(heading).not.toBeNull(); + + const message = await page.$('p.vizualizer-renew-notice-message'); + expect(message).not.toBeNull(); + + const renewButton = await page.$('button.vizualizer-renew-notice-renew-button'); + expect(renewButton).not.toBeNull(); + + const activateButton = await page.$('button.vizualizer-renew-notice-activate-button'); + expect(activateButton).not.toBeNull(); + + const closeButton = await page.$('button.vizualizer-renew-notice-close-icon'); + expect(closeButton).not.toBeNull(); + }); + } ); diff --git a/tests/test-ajax.php b/tests/test-ajax.php index 416ffd768..a7bd7a6b5 100644 --- a/tests/test-ajax.php +++ b/tests/test-ajax.php @@ -66,6 +66,8 @@ public function setUp() { public function test_ajax_response_get_query_data_valid_query() { $this->_setRole( 'administrator' ); + $this->enable_pro(); + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ); global $wpdb; @@ -93,6 +95,8 @@ public function test_ajax_response_get_query_data_valid_query() { public function test_ajax_response_get_query_data_invalid_query() { $this->_setRole( 'administrator' ); + $this->enable_pro(); + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ); $_POST['params'] = array( @@ -120,6 +124,8 @@ public function test_ajax_response_get_query_data_invalid_query() { public function test_ajax_response_get_query_data_valid_query_with_filtered_columns() { $this->_setRole( 'administrator' ); + $this->enable_pro(); + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ); $_POST['params'] = array( @@ -203,6 +209,8 @@ public function test_ajax_response_get_query_data_subcriber_dissallow() { public function test_ajax_response_get_query_data_invalid_query_subquery() { $this->_setRole( 'administrator' ); + $this->enable_pro(); + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ); $_POST['params'] = array( @@ -230,6 +238,8 @@ public function test_ajax_response_get_query_data_invalid_query_subquery() { public function test_ajax_response_get_query_data_invalid_query_comment() { $this->_setRole( 'administrator' ); + $this->enable_pro(); + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ); $_POST['params'] = array( @@ -264,4 +274,66 @@ public function test_sql_comment_strip() { $source = new Visualizer_Source_Query( "/* SELECT */ DELETE * FROM test_table /* WHERE post_type = 'post' */"); $this->assertEquals( 'DELETE * FROM test_table', $source->get_query() ); } + + /** + * Test Save Query not allowed for subscriber. + */ + public function test_sql_save_chart_subscriber() { + $this->_setRole( 'subscriber' ); + + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION ); + $_GET['chart'] = '1'; + + $_POST['params'] = array( + 'query' => 'SELECT * FROM wp_posts LIMIT 1', + ); + try { + // Trigger the AJAX action + $this->_handleAjax( Visualizer_Plugin::ACTION_SAVE_DB_QUERY ); + } catch ( WPAjaxDieContinueException $e ) { + // We expected this, do nothing. + } + + $response = json_decode( $this->_last_response ); + $this->assertIsObject( $response ); + $this->assertObjectHasAttribute( 'success', $response ); + $this->assertObjectHasAttribute( 'data', $response ); + $this->assertEquals( 'Action not allowed for this user.', $response->data->msg ); + $this->assertFalse( $response->success ); + } + + /** + * Test Save Query not allowed if not pro. + */ + public function test_sql_save_chart_admin() { + wp_set_current_user( $this->admin_user_id ); + $this->_setRole( 'administrator' ); + + $_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION ); + $_GET['chart'] = '1'; + + $_POST['params'] = array( + 'query' => 'SELECT * FROM wp_posts LIMIT 1', + ); + try { + // Trigger the AJAX action + $this->_handleAjax( Visualizer_Plugin::ACTION_SAVE_DB_QUERY ); + } catch ( WPAjaxDieContinueException $e ) { + // We expected this, do nothing. + } + + $response = json_decode( $this->_last_response ); + $this->assertIsObject( $response ); + $this->assertObjectHasAttribute( 'success', $response ); + $this->assertObjectHasAttribute( 'data', $response ); + $this->assertEquals( 'Feature is not available.', $response->data->msg ); + $this->assertFalse( $response->success ); + } + + /** + * Utility method to mock pro version. + */ + private function enable_pro() { + add_filter( 'visualizer_is_pro', '__return_true' ); + } }