--- a/nelio-ab-testing/admin/pages/class-nelio-ab-testing-experiment-list-page.php
+++ b/nelio-ab-testing/admin/pages/class-nelio-ab-testing-experiment-list-page.php
@@ -47,6 +47,7 @@
add_filter( 'manage_edit-nab_experiment_sortable_columns', array( $this, 'set_sortable_experiment_columns' ) );
add_action( 'manage_nab_experiment_posts_custom_column', array( $this, 'set_experiment_column_values' ), 10, 2 );
+ add_filter( 'map_meta_cap', array( $this, 'maybe_disable_title_link' ), 10, 4 );
add_filter( 'post_row_actions', array( $this, 'fix_experiment_list_row_actions' ), 10, 2 );
add_filter( 'bulk_actions-edit-nab_experiment', array( $this, 'remove_edit_from_bulk_actions' ) );
@@ -181,6 +182,41 @@
}
/**
+ * Callback to disable title link if user can’t edit the test.
+ *
+ * This callback was created to account for PHP tests.
+ *
+ * @param array<string> $caps List of capabilities.
+ * @param string $cap Capability.
+ * @param int $user_id User ID.
+ * @param list<mixed> $args Arguments.
+ *
+ * @return array<string>
+ */
+ public function maybe_disable_title_link( $caps, $cap, $user_id, $args ) {
+ if ( 'edit_nab_experiments' !== $cap ) {
+ return $caps;
+ }
+
+ if ( current_user_can( 'edit_nab_php_experiments' ) ) {
+ return $caps;
+ }
+
+ $post_id = absint( $args[0] ?? 0 );
+ $type = get_post_meta( $post_id, '_nab_experiment_type', true );
+ if ( 'nab/php' !== $type ) {
+ return $caps;
+ }
+
+ $status = get_post_status( $post_id );
+ if ( in_array( $status, array( 'nab_running', 'nab_finished' ), true ) ) {
+ return $caps;
+ }
+
+ return array( 'do_not_allow' );
+ }
+
+ /**
* Callback to customize the actions available in each experiment.
*
* @param array<string,string> $actions List of actions.
@@ -248,7 +284,7 @@
$actions['pause'] = $this->get_pause_experiment_action( $experiment );
}
- if ( 'trash' !== $experiment->get_status() ) {
+ if ( ! is_wp_error( $experiment->can_be_duplicated() ) ) {
$actions['duplicate'] = $this->get_duplicate_experiment_action( $experiment );
}
@@ -869,6 +905,11 @@
$die( _x( 'You attempted to dupliacte a test that doesn’t exist. Perhaps it was deleted?', 'user', 'nelio-ab-testing' ) );
}
+ $can_be_duplicated = $experiment->can_be_duplicated();
+ if ( is_wp_error( $can_be_duplicated ) ) {
+ $die( $can_be_duplicated->get_error_message() );
+ }
+
check_admin_referer( 'nab_duplicate_experiment_' . $experiment->get_id() );
$experiment->duplicate();
--- a/nelio-ab-testing/admin/pages/class-nelio-ab-testing-experiment-page.php
+++ b/nelio-ab-testing/admin/pages/class-nelio-ab-testing-experiment-page.php
@@ -94,9 +94,9 @@
wp_die( esc_html_x( 'Experiment not found', 'text', 'nelio-ab-testing' ) );
}
- $status = $experiment->get_status();
- if ( in_array( $status, array( 'running', 'finished' ), true ) ) {
- wp_die( esc_html_x( 'You’re not allowed to view this page.', 'user', 'nelio-ab-testing' ) );
+ $can_be_edited = $experiment->can_be_edited();
+ if ( is_wp_error( $can_be_edited ) ) {
+ wp_die( esc_html( $can_be_edited->get_error_message() ) );
}
}
--- a/nelio-ab-testing/admin/settings/class-nelio-ab-testing-external-page-script.php
+++ b/nelio-ab-testing/admin/settings/class-nelio-ab-testing-external-page-script.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * This file contains a viewer for the external page script.
+ */
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * This class displays the external page script.
+ */
+class Nelio_AB_Testing_External_Page_Script extends Nelio_AB_Testing_Abstract_React_Setting {
+
+ public function __construct() {
+ parent::__construct( '_external_page_script', 'ExternalPageScriptSetting' );
+ }
+
+ // @Overrides
+ protected function get_field_attributes() {
+ $script = $this->get_script();
+ $minified = $script;
+ $minified = str_replace( "n", ' ', $minified );
+ $minified = nab_minify_js( $minified );
+ return array(
+ 'value' => $script,
+ 'minified' => $minified,
+ );
+ }
+
+ public function set_value( $value ) {
+ // Do nothing.
+ }
+
+ // @Implements
+ public function do_sanitize( $input ) {
+ unset( $input['_external_page_script'] );
+ return $input;
+ }
+
+ // @Overrides
+ public function display() {
+ printf( '<div id="%s"><span class="nab-dynamic-setting-loader"></span></div>', esc_attr( $this->get_field_id() ) );
+ ?>
+ <div class="setting-help" style="display:none;">
+ <p><span class="description">
+ <?php
+ echo wp_kses(
+ _x( 'This script loads test variants and tracks events on pages built with external services but still served from your WordPress domain.', 'text', 'nelio-ab-testing' ) .
+ ' ' .
+ _x( 'Add it at the very top of the <code>head</code> of the external page as a manual substitute for the script our plugin would normally insert automatically.', 'user', 'nelio-ab-testing' ),
+ array( 'code' => array() )
+ );
+ ?>
+ </span><p>
+ </div>
+ <?php
+ }
+
+ /**
+ * Generates the script.
+ *
+ * @return string
+ */
+ private function get_script() {
+ /** @var WP_Filesystem_Base $wp_filesystem */
+ global $wp_filesystem;
+ if ( ! function_exists( 'WP_Filesystem' ) ) {
+ nab_require_wp_file( '/wp-admin/includes/file.php' );
+ }
+ WP_Filesystem();
+
+ $filename = nelioab()->plugin_path . '/includes/hooks/compat/external-page-script/script.js';
+ $script = '';
+ if ( file_exists( $filename ) ) {
+ $script = $wp_filesystem->get_contents( $filename );
+ $script = is_string( $script ) ? $script : '';
+ }
+
+ $script = preg_replace( '/t/', ' ', $script );
+ $script = is_string( $script ) ? $script : '';
+ $script = trim( $script );
+
+ /** This filter is documented in includes/utils/functions/helpers.php' */
+ $bgcolor = apply_filters( 'nab_alternative_loading_overlay_color', '#fff' );
+ $bgcolor = is_string( $bgcolor ) ? $bgcolor : '#fff';
+ return sprintf(
+ "<script type="text/javascript" data-overlay-color="%1$s">n%2$sn</script>",
+ esc_attr( $bgcolor ),
+ $script
+ );
+ }
+
+ /**
+ * Returns the ID of this field.
+ *
+ * @return string
+ */
+ private function get_field_id() {
+ return str_replace( '_', '-', $this->name );
+ }
+}
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-css-editor-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-css-editor-page.php
@@ -18,6 +18,7 @@
<title><?php echo esc_html_x( 'Nelio A/B Testing - CSS Editor', 'text', 'nelio-ab-testing' ); ?></title>
<?php
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'admin_enqueue_scripts' );
print_admin_styles();
wp_print_head_scripts();
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-experiment-debug.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-experiment-debug.php
@@ -40,8 +40,8 @@
<div>
<textarea id="experiment-debug-data" readonly style="background:#fcfcfc; border:1px solid grey; width:100%; overflow:auto; height:calc(100vh - 18em ); min-height: 30em; padding:1em; font-family:monospace; white-space:pre;">
<?php
- $aux = Nelio_AB_Testing_Experiment_REST_Controller::instance();
- echo 'test = ' . wp_json_encode( $aux->json( $experiment ), JSON_PRETTY_PRINT ) . ';';
+ $nab_aux = Nelio_AB_Testing_Experiment_REST_Controller::instance();
+ echo 'test = ' . wp_json_encode( $nab_aux->json( $experiment ), JSON_PRETTY_PRINT ) . ';';
?>
</textarea>
</div>
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-heatmap-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-heatmap-page.php
@@ -18,6 +18,7 @@
<title><?php echo esc_html_x( 'Nelio A/B Testing - Heatmap Viewer', 'text', 'nelio-ab-testing' ); ?></title>
<?php
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'admin_enqueue_scripts' );
print_admin_styles();
wp_print_head_scripts();
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-javascript-editor-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-javascript-editor-page.php
@@ -18,6 +18,7 @@
<title><?php echo esc_html_x( 'Nelio A/B Testing - JavaScript Editor', 'text', 'nelio-ab-testing' ); ?></title>
<?php
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'admin_enqueue_scripts' );
print_admin_styles();
wp_print_head_scripts();
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-php-editor-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-php-editor-page.php
@@ -18,6 +18,7 @@
<title><?php echo esc_html_x( 'Nelio A/B Testing - CSS Editor', 'text', 'nelio-ab-testing' ); ?></title>
<?php
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'admin_enqueue_scripts' );
print_admin_styles();
wp_print_head_scripts();
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-settings-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-settings-page.php
@@ -21,9 +21,9 @@
<form method="post" action="options.php" class="nab-settings-form">
<?php
- $settings = Nelio_AB_Testing_Settings::instance();
- settings_fields( $settings->get_option_group() );
- do_settings_sections( $settings->get_settings_page_name() );
+ $nab_settings = Nelio_AB_Testing_Settings::instance();
+ settings_fields( $nab_settings->get_option_group() );
+ do_settings_sections( $nab_settings->get_settings_page_name() );
submit_button();
?>
</form>
--- a/nelio-ab-testing/assets/dist/js/components.asset.php
+++ b/nelio-ab-testing/assets/dist/js/components.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-data', 'nab-date', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6c805c4f310967f33ad8');
+<?php return array('dependencies' => array('lodash', 'nab-data', 'nab-date', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '1e2591d368b759d49243');
--- a/nelio-ab-testing/assets/dist/js/conversion-action-library.asset.php
+++ b/nelio-ab-testing/assets/dist/js/conversion-action-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-warning'), 'version' => '74f20c3c93872660075b');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-warning'), 'version' => '747bfdb058b6477e8fa2');
--- a/nelio-ab-testing/assets/dist/js/css-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/css-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'ff4274470bd727e79167');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '650b9b8f42a49930ff4c');
--- a/nelio-ab-testing/assets/dist/js/css-experiment-public.asset.php
+++ b/nelio-ab-testing/assets/dist/js/css-experiment-public.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-url'), 'version' => '5aa9ef2c1e14a226da1e');
+<?php return array('dependencies' => array('wp-url'), 'version' => '4420e0657889a52003b1');
--- a/nelio-ab-testing/assets/dist/js/editor.asset.php
+++ b/nelio-ab-testing/assets/dist/js/editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'e25c5121c407ccec794b');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '936135518d24f5e3f433');
--- a/nelio-ab-testing/assets/dist/js/experiment-library.asset.php
+++ b/nelio-ab-testing/assets/dist/js/experiment-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-action-library', 'nab-conversion-actions', 'nab-data', 'nab-experiments', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => 'd85614142088cc663882');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-action-library', 'nab-conversion-actions', 'nab-data', 'nab-experiments', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => 'ddecf24d95342eae2a56');
--- a/nelio-ab-testing/assets/dist/js/heatmap-editor.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-editor', 'nab-experiment-library', 'nab-experiments', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'c03de126c1d325749a0d');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-editor', 'nab-experiment-library', 'nab-experiments', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '7148a4253761ea38d99c');
--- a/nelio-ab-testing/assets/dist/js/heatmap-renderer.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-renderer.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '89076c378c203c58c90c');
+<?php return array('dependencies' => array(), 'version' => '3c09c5efa076db896d01');
--- a/nelio-ab-testing/assets/dist/js/heatmap-results-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-results-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-i18n', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '7828182edb4f0e132008');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-i18n', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'fd214693371de579ad6b');
--- a/nelio-ab-testing/assets/dist/js/individual-settings.asset.php
+++ b/nelio-ab-testing/assets/dist/js/individual-settings.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '82c0207de88fefb87fb0');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'd2a315d45827c2e9a912');
--- a/nelio-ab-testing/assets/dist/js/public.asset.php
+++ b/nelio-ab-testing/assets/dist/js/public.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => 'f705cb6568d1021556bf');
+<?php return array('dependencies' => array(), 'version' => '664c2f1a462684bce6bc');
--- a/nelio-ab-testing/includes/data/basic-tab.php
+++ b/nelio-ab-testing/includes/data/basic-tab.php
@@ -459,4 +459,24 @@
),
),
+ array(
+ 'type' => 'section',
+ 'icon' => 'dashicons-admin-generic',
+ 'name' => 'misc',
+ 'ui' => fn() => array(
+ 'label' => _x( 'Misc', 'text', 'nelio-ab-testing' ),
+ ),
+ ),
+
+ array(
+ 'type' => 'custom',
+ 'name' => '_external_page_script',
+ 'default' => '',
+ 'ui' => fn() => array(
+ 'instance' => new Nelio_AB_Testing_External_Page_Script(),
+ 'label' => _x( 'External Page Script', 'text', 'nelio-ab-testing' ),
+ 'desc' => true,
+ ),
+ ),
+
);
--- a/nelio-ab-testing/includes/experiments/class-nelio-ab-testing-experiment-post-type-register.php
+++ b/nelio-ab-testing/includes/experiments/class-nelio-ab-testing-experiment-post-type-register.php
@@ -67,7 +67,7 @@
* @param string $link the current link.
* @param int $post_id the post (or experiment) whose edit link we want.
*
- * @return string the link we want.
+ * @return string|false the link we want.
*
* @since 5.0.0
*/
@@ -77,19 +77,12 @@
return $link;
}
- if ( in_array( get_post_status( $post_id ), array( 'nab_running', 'nab_finished' ), true ) ) {
- $page = 'nelio-ab-testing-experiment-view';
- } else {
- $page = 'nelio-ab-testing-experiment-edit';
+ $experiment = nab_get_experiment( $post_id );
+ if ( is_wp_error( $experiment ) ) {
+ return '';
}
- return add_query_arg(
- array(
- 'page' => $page,
- 'experiment' => $post_id,
- ),
- admin_url( 'admin.php' )
- );
+ return $experiment->get_url();
}
/**
--- a/nelio-ab-testing/includes/experiments/class-nelio-ab-testing-experiment.php
+++ b/nelio-ab-testing/includes/experiments/class-nelio-ab-testing-experiment.php
@@ -431,11 +431,14 @@
*/
public function can_be_edited() {
- if ( ! current_user_can( 'edit_nab_experiments' ) ) {
+ $edit_capability =
+ 'nab/php' === $this->get_type()
+ ? 'edit_nab_php_experiments'
+ : 'edit_nab_experiments';
+ if ( ! current_user_can( $edit_capability ) ) {
return new WP_Error(
'missing-capability',
- // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
- __( 'Sorry, you are not allowed to do that.' )
+ _x( 'Sorry, you are not allowed to edit this test.', 'text', 'nelio-ab-testing' )
);
}
@@ -1742,6 +1745,39 @@
}
/**
+ * Returns whether the experiment can be edited or not.
+ *
+ * @return true|WP_Error
+ */
+ public function can_be_duplicated() {
+
+ $edit_capability =
+ 'nab/php' === $this->get_type()
+ ? 'edit_nab_php_experiments'
+ : 'edit_nab_experiments';
+ if ( ! current_user_can( $edit_capability ) ) {
+ return new WP_Error(
+ 'missing-capability',
+ _x( 'Sorry, you are not allowed to duplicate this test.', 'text', 'nelio-ab-testing' )
+ );
+ }
+
+ if ( 'trash' === $this->post->post_status ) {
+ $helper = Nelio_AB_Testing_Experiment_Helper::instance();
+ return new WP_Error(
+ 'invalid-experiment-status',
+ sprintf(
+ /* translators: %1$s: Experiment name. */
+ _x( 'Test %1$s can’t be duplicate.', 'text', 'nelio-ab-testing' ),
+ $helper->get_non_empty_name( $this )
+ )
+ );
+ }
+
+ return true;
+ }
+
+ /**
* Duplicates the current experiment.
*
* @return Nelio_AB_Testing_Experiment|WP_Error the new, duplicated experiment.
--- a/nelio-ab-testing/includes/hooks/compat/cache/breeze/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/cache/breeze/index.php
@@ -17,6 +17,7 @@
* @return void
*/
function flush_cache() {
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'breeze_clear_all_cache' );
}
add_action( 'nab_flush_all_caches', __NAMESPACE__ . 'flush_cache' );
--- a/nelio-ab-testing/includes/hooks/compat/cache/litespeed/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/cache/litespeed/index.php
@@ -17,6 +17,7 @@
* @return void
*/
function flush_cache() {
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'litespeed_purge_all', 'Nelio A/B Testing' );
}
add_action( 'nab_flush_all_caches', __NAMESPACE__ . 'flush_cache' );
--- a/nelio-ab-testing/includes/hooks/compat/cache/wp-super-cache/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/cache/wp-super-cache/index.php
@@ -28,6 +28,7 @@
/** @var string */
global $supercachedir;
if ( empty( $supercachedir ) && function_exists( 'get_supercache_dir' ) ) {
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$supercachedir = get_supercache_dir();
}
--- a/nelio-ab-testing/includes/hooks/compat/external-page-script/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/external-page-script/index.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * This compat file prints sends JSON data with the info needed to run a test on the current page.
+ */
+namespace Nelio_AB_TestingCompatExternal_Page_Script;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Sends JSON data with info needed to run external script if request has the `nab-external-page-script` query arg in it.
+ *
+ * @return void
+ */
+function print_json_for_external_page_script() {
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_GET['nab-external-page-script'] ) ) {
+ return;
+ }
+
+ /**
+ * Runs before wp_enqueue_scripts action during JSON generation for external page scripts.
+ *
+ * @since 8.2.0
+ */
+ do_action( 'nab_external_page_script_before_enqueue_scripts' );
+
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
+ do_action( 'wp_enqueue_scripts' );
+
+ /**
+ * Runs after wp_enqueue_scripts action during JSON generation for external page scripts.
+ *
+ * @since 8.2.0
+ */
+ do_action( 'nab_external_page_script_after_enqueue_scripts' );
+
+ ob_start();
+ wp_print_styles();
+ wp_print_scripts();
+ /**
+ * Prints additional stuff on the external page’s head.
+ *
+ * @since 8.2.0
+ */
+ do_action( 'nab_external_page_script_print_assets' );
+ $result = ob_get_clean();
+
+ header( 'Content-Type: application/json; charset=utf-8' );
+ echo wp_json_encode( $result );
+ die();
+}
+add_action( 'wp', __NAMESPACE__ . 'print_json_for_external_page_script', 9999 );
+
+/**
+ * Removes unnecessary assets.
+ *
+ * @return void
+ */
+function remove_unnecessary_assets() {
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_GET['nab-external-page-script'] ) ) {
+ return;
+ }
+
+ /** @var WP_Scripts */
+ global $wp_scripts;
+
+ wp_add_inline_script( 'nelio-ab-testing-main', 'const nabKeepOverlayInExternalPageScript = true;', 'before' );
+
+ /**
+ * Filters the scripts that can be added to external pages via Nelio’s external page script.
+ *
+ * @param list<string> $scripts Script handles. Default: `array()`.
+ */
+ $allowed_scripts = apply_filters( 'nab_scripts_to_keep_in_external_pages', array() );
+ foreach ( $wp_scripts->queue as $handle ) {
+ if ( ! is_nab_handle( $handle ) && ! in_array( $handle, $allowed_scripts, true ) ) {
+ wp_dequeue_script( $handle );
+ }
+ }
+
+ /** @var WP_Styles */
+ global $wp_styles;
+
+ /**
+ * Filters the styles that can be added to external pages via Nelio’s external page script.
+ *
+ * @param list<string> $scripts Style handles. Default: `array()`.
+ */
+ $allowed_styles = apply_filters( 'nab_styles_to_keep_in_external_pages', array() );
+ foreach ( $wp_styles->queue as $handle ) {
+ if ( ! is_nab_handle( $handle ) && ! in_array( $handle, $allowed_styles, true ) ) {
+ wp_dequeue_style( $handle );
+ }
+ }
+}
+add_action( 'wp_enqueue_scripts', __NAMESPACE__ . 'remove_unnecessary_assets', 9999 );
+
+/**
+ * Whether the given handle is a Nelio A/B Testing handle or not.
+ *
+ * @param string $handle Handle.
+ *
+ * @return bool
+ */
+function is_nab_handle( $handle ) {
+ return 0 === strpos( $handle, 'nab-' ) || 0 === strpos( $handle, 'nelio-ab-testing-' ) || strpos( $handle, 'nelioab-' );
+}
--- a/nelio-ab-testing/includes/hooks/compat/fluent-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/fluent-forms/index.php
@@ -74,7 +74,7 @@
'thumbnailSrc' => '',
'title' => $form['title'],
'type' => 'nab_fluent_form',
- 'typeLabel' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
+ 'typeLabel' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
'extra' => array(),
);
}
@@ -137,8 +137,8 @@
'statusLabel' => '',
'thumbnailSrc' => '',
'title' => $form['title'],
- 'type' => 'nab_formidable_form',
- 'typeLabel' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
+ 'type' => 'nab_fluent_form',
+ 'typeLabel' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
'extra' => array(),
);
},
--- a/nelio-ab-testing/includes/hooks/compat/forminator/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forminator/index.php
@@ -131,8 +131,8 @@
'statusLabel' => '',
'thumbnailSrc' => '',
'title' => $form->settings['formName'],
- 'type' => 'nab_formidable_form',
- 'typeLabel' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
+ 'type' => 'nab_forminator_form',
+ 'typeLabel' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
'extra' => array(),
);
},
--- a/nelio-ab-testing/includes/hooks/compat/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/index.php
@@ -18,6 +18,7 @@
require_once __DIR__ . '/custom-permalinks/index.php';
require_once __DIR__ . '/divi/index.php';
require_once __DIR__ . '/elementor/index.php';
+require_once __DIR__ . '/external-page-script/index.php';
require_once __DIR__ . '/fluent-forms/index.php';
require_once __DIR__ . '/formcraft/index.php';
require_once __DIR__ . '/formidable-forms/index.php';
--- a/nelio-ab-testing/includes/hooks/experiments/css/edit.php
+++ b/nelio-ab-testing/includes/hooks/experiments/css/edit.php
@@ -136,6 +136,7 @@
echo '<style id="nab-css-style" type="text/css"></style>';
}
add_action( 'wp_head', __NAMESPACE__ . 'add_css_style_tag', 9999 );
+add_action( 'nab_external_page_script_print_assets', __NAMESPACE__ . 'add_css_style_tag', 9999 );
/**
* Callback to register the CSS Editor page in the Dashboard.
--- a/nelio-ab-testing/includes/hooks/experiments/javascript/edit.php
+++ b/nelio-ab-testing/includes/hooks/experiments/javascript/edit.php
@@ -151,27 +151,3 @@
return $disabled;
}
add_filter( 'nab_disable_split_testing', __NAMESPACE__ . 'should_split_testing_be_disabled' );
-
-/**
- * Callback to add a script that sets the iframe loading status in the parent’s nab/data store.
- *
- * @return void
- */
-function set_iframe_loading_status() {
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- if ( ! isset( $_GET['nab-javascript-previewer'] ) ) {
- return;
- }
-
- $mkscript = function ( $enabled ) {
- return function () use ( $enabled ) {
- printf(
- '<script type="text/javascript">window.parent.wp.data.dispatch("nab/data").setPageAttribute("javascript-preview/isLoading",%s)</script>',
- wp_json_encode( $enabled )
- );
- };
- };
- add_action( 'wp_head', $mkscript( true ), 1 );
- add_action( 'wp_footer', $mkscript( false ), 1 );
-}
-add_action( 'init', __NAMESPACE__ . 'set_iframe_loading_status' );
--- a/nelio-ab-testing/includes/hooks/experiments/post/load.php
+++ b/nelio-ab-testing/includes/hooks/experiments/post/load.php
@@ -192,6 +192,7 @@
}
remove_filter( 'the_content', $fix_content, 11 );
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$alt_content = apply_filters( 'the_content', $alt_content );
add_filter( 'the_content', $fix_content, 11 );
return $alt_content;
@@ -214,6 +215,7 @@
}
remove_filter( 'the_excerpt', $fix_excerpt, 11 );
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$alt_excerpt = apply_filters( 'the_excerpt', $alt_excerpt );
add_filter( 'the_excerpt', $fix_excerpt, 11 );
return $alt_excerpt;
--- a/nelio-ab-testing/includes/hooks/woocommerce/compat/custom-hooks.php
+++ b/nelio-ab-testing/includes/hooks/woocommerce/compat/custom-hooks.php
@@ -40,7 +40,7 @@
// Source: WC_Data » get_hook_prefix() . $prop.
add_filter( 'woocommerce_order_item_get_name', $replace_name, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_name', __NAMESPACE__ . 'create_product_name_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_name', __NAMESPACE__ . 'create_product_name_hook', 10, 3 );
/**
* Callback to create product description hook.
@@ -90,7 +90,7 @@
};
add_filter( 'woocommerce_product_get_description', $replace_product_description, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_description', __NAMESPACE__ . 'create_product_description_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_description', __NAMESPACE__ . 'create_product_description_hook', 10, 3 );
/**
* Callback to create product short description hook.
@@ -163,7 +163,7 @@
};
add_filter( 'woocommerce_product_get_short_description', $replace_product_short_description, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_short_description', __NAMESPACE__ . 'create_product_short_description_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_short_description', __NAMESPACE__ . 'create_product_short_description_hook', 10, 3 );
/**
* Callback to pcreate product image ID hook.
@@ -198,7 +198,7 @@
};
add_filter( 'get_post_metadata', $replace_image_id_meta, $priority, 3 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_image_id', __NAMESPACE__ . 'create_product_image_id_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_image_id', __NAMESPACE__ . 'create_product_image_id_hook', 10, 3 );
/**
* Callback to create product gallery hook.
@@ -228,7 +228,7 @@
// Source: WC_Data » get_hook_prefix() . $prop.
add_filter( 'woocommerce_product_get_gallery_image_ids', $replace_gallery_image_ids, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_gallery_ids', __NAMESPACE__ . 'create_product_gallery_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_gallery_ids', __NAMESPACE__ . 'create_product_gallery_hook', 10, 3 );
/**
* Callback to create product regular price hook.
@@ -267,7 +267,7 @@
add_filter( 'woocommerce_product_get_price', $replace_regular_price, $priority, 2 );
add_filter( 'woocommerce_product_get_regular_price', $replace_regular_price, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_regular_price', __NAMESPACE__ . 'create_product_regular_price_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_regular_price', __NAMESPACE__ . 'create_product_regular_price_hook', 10, 3 );
/**
* Callback to create product sale price hook.
@@ -308,7 +308,7 @@
add_filter( 'woocommerce_product_get_price', $replace_sale_price, $priority, 2 );
add_filter( 'woocommerce_product_get_sale_price', $replace_sale_price, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_product_sale_price', __NAMESPACE__ . 'create_product_sale_price_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_product_sale_price', __NAMESPACE__ . 'create_product_sale_price_hook', 10, 3 );
/**
* Callback to fix product on sale.
@@ -457,7 +457,7 @@
// Source: WC_Data » get_hook_prefix() . $prop.
add_filter( 'woocommerce_product_variation_get_description', $replace_description, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_variation_description', __NAMESPACE__ . 'create_variation_description_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_variation_description', __NAMESPACE__ . 'create_variation_description_hook', 10, 3 );
/**
* Callback to create variation image ID hook.
@@ -488,7 +488,7 @@
// Source: WC_Data » get_hook_prefix() . $prop.
add_filter( 'woocommerce_product_variation_get_image_id', $replace_image_id, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_variation_image_id', __NAMESPACE__ . 'create_variation_image_id_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_variation_image_id', __NAMESPACE__ . 'create_variation_image_id_hook', 10, 3 );
/**
* Callback to create variation regular price hook.
@@ -525,7 +525,7 @@
add_filter( 'woocommerce_variation_prices_regular_price', $replace_regular_price, $priority, 2 );
add_filter( 'woocommerce_variation_prices_price', $replace_regular_price, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_variation_regular_price', __NAMESPACE__ . 'create_variation_regular_price_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_variation_regular_price', __NAMESPACE__ . 'create_variation_regular_price_hook', 10, 3 );
/**
* Callback to create variation sale price hook.
@@ -563,7 +563,7 @@
add_filter( 'woocommerce_variation_prices_sale_price', $replace_sale_price, $priority, 2 );
add_filter( 'woocommerce_variation_prices_price', $replace_sale_price, $priority, 2 );
}
-add_action( 'add_nab_filter_for_woocommerce_variation_sale_price', __NAMESPACE__ . 'create_variation_sale_price_hook', 10, 3 );
+add_action( 'nab_add_filter_for_woocommerce_variation_sale_price', __NAMESPACE__ . 'create_variation_sale_price_hook', 10, 3 );
// ========
// INTERNAL
--- a/nelio-ab-testing/includes/hooks/woocommerce/compat/plugins/woocommerce-product-price-based-on-countries.php
+++ b/nelio-ab-testing/includes/hooks/woocommerce/compat/plugins/woocommerce-product-price-based-on-countries.php
@@ -44,7 +44,7 @@
$variation_data = get_post_meta( $alternative_id, '_nab_variation_data', true );
if ( empty( $variation_data ) ) {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_regular_price',
function ( $price, $product_id ) use ( &$alternative, $control_id ) {
/** @var string $price */
@@ -67,7 +67,7 @@
2
);
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_sale_price',
function ( $price, $product_id, $regular_price ) use ( &$alternative, $control_id ) {
/** @var string $price */
@@ -93,7 +93,7 @@
} else {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_variation_regular_price',
function ( $price, $product_id, $variation_id ) use ( &$variation_data, $control_id ) {
/** @var string $price */
@@ -115,7 +115,7 @@
3
);
- add_nab_filter(
+ nab_add_filter(
'woocommerce_variation_sale_price',
function ( $price, $product_id, $regular_price, $variation_id ) use ( &$variation_data, $control_id ) {
/** @var string $price */
--- a/nelio-ab-testing/includes/hooks/woocommerce/experiments/bulk-sale/load.php
+++ b/nelio-ab-testing/includes/hooks/woocommerce/experiments/bulk-sale/load.php
@@ -67,7 +67,7 @@
return $regular_price * ( 100 - $alternative['discount'] ) / 100;
};
- add_nab_filter( 'woocommerce_product_sale_price', $get_sale_price, 99, 3 );
- add_nab_filter( 'woocommerce_variation_sale_price', $get_sale_price, 99, 3 );
+ nab_add_filter( 'woocommerce_product_sale_price', $get_sale_price, 99, 3 );
+ nab_add_filter( 'woocommerce_variation_sale_price', $get_sale_price, 99, 3 );
}
add_action( 'nab_nab/wc-bulk-sale_load_alternative', __NAMESPACE__ . 'load_alternative_discount', 10, 3 );
--- a/nelio-ab-testing/includes/hooks/woocommerce/experiments/product/load.php
+++ b/nelio-ab-testing/includes/hooks/woocommerce/experiments/product/load.php
@@ -92,7 +92,7 @@
do_action( 'nab_load_proper_alternative_woocommerce_product', $alt_product, $experiment_id );
}
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_name',
function ( $name, $product_id ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -111,7 +111,7 @@
);
if ( $alt_product->is_description_supported() ) {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_description',
function ( $description, $product_id ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -130,7 +130,7 @@
);
}
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_short_description',
function ( $short_description, $product_id ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -148,7 +148,7 @@
2
);
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_image_id',
function ( $image_id, $product_id ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -167,7 +167,7 @@
);
if ( $alt_product->is_gallery_supported() ) {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_gallery_ids',
function ( $image_ids, $product_id ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -188,7 +188,7 @@
if ( ! $alt_product->has_variation_data() ) {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_regular_price',
function ( $price, $product_id ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -208,7 +208,7 @@
);
if ( $alt_product->is_sale_price_supported() ) {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_product_sale_price',
function ( $price, $product_id, $regular_price ) use ( &$alt_product ) {
if ( $product_id !== $alt_product->get_control_id() ) {
@@ -229,7 +229,7 @@
}
} else {
- add_nab_filter(
+ nab_add_filter(
'woocommerce_variation_description',
function ( $short_description, $product_id, $variation_id ) use ( &$alt_product ) {
/** @var int $variation_id */
@@ -249,7 +249,7 @@
3
);
- add_nab_filter(
+ nab_add_filter(
'woocommerce_variation_image_id',
function ( $image_id, $product_id, $variation_id ) use ( &$alt_product ) {
/** @var int $variation_id */
@@ -269,7 +269,7 @@
3
);
- add_nab_filter(
+ nab_add_filter(
'woocommerce_variation_regular_price',
function ( $price, $product_id, $variation_id ) use ( &$alt_product ) {
/** @var int $variation_id */
@@ -289,7 +289,7 @@
3
);
- add_nab_filter(
+ nab_add_filter(
'woocommerce_variation_sale_price',
function ( $price, $product_id, $regular_price, $variation_id ) use ( &$alt_product ) {
/** @var int $variation_id */
@@ -410,7 +410,8 @@
return;
}
$previous_global_product = $product;
- $product = $alt_product->get_control();
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
+ $product = $alt_product->get_control();
};
$undo_use_control_in_add_to_cart = function () use ( &$previous_global_product ) {
/** @var WC_Product|null */
@@ -418,6 +419,7 @@
if ( null === $previous_global_product ) {
return;
}
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$product = $previous_global_product;
$previous_global_product = null;
};
@@ -470,6 +472,7 @@
}
$control = $alt_product->get_control();
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
if ( $control && ( $control->has_attributes() || apply_filters( 'wc_product_enable_dimensions_display', $control->has_weight() || $control->has_dimensions() ) ) ) {
$tabs['additional_information'] = array(
'title' => _x( 'Additional information', 'text (woocommerce)', 'nelio-ab-testing' ),
--- a/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-input-setting.php
+++ b/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-input-setting.php
@@ -16,7 +16,7 @@
* @var string $id The identifier of this field.
* @var string $name The name of this field.
* @var string $value The concrete value of this field (or an empty string).
- * @var boolean $disabled Whether this checkbox is disabled or not.
+ * @var boolean $disabled Whether this input is disabled or not.
* @var string $placeholder Optional. A default placeholder.
* @var string $desc Optional. The description of this field.
* @var string $more Optional. A link with more information about this field.
--- a/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-radio-setting.php
+++ b/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-radio-setting.php
@@ -14,7 +14,7 @@
*
* @var list<array{value:string, label:string, desc?:string}> $options The list of options.
* @var string $name The name of this field.
- * @var boolean $disabled Whether this checkbox is disabled or not.
+ * @var boolean $disabled Whether this radio is disabled or not.
* @var string $value The concrete value of this field (or an empty string).
* @var string $desc Optional. The description of this field.
* @var string $more Optional. A link with more information about this field.
@@ -23,7 +23,7 @@
?>
<?php
-foreach ( $options as $option ) {
+foreach ( $options as $nab_option ) {
?>
<p
<?php
@@ -33,11 +33,11 @@
?>
><input type="radio"
name="<?php echo esc_attr( $name ); ?>"
- value="<?php echo esc_attr( $option['value'] ); ?>"
+ value="<?php echo esc_attr( $nab_option['value'] ); ?>"
<?php disabled( $disabled ); ?>
- <?php checked( $option['value'] === $value ); ?> />
+ <?php checked( $nab_option['value'] === $value ); ?> />
<?php
- $this->print_html( $option['label'] );
+ $this->print_html( $nab_option['label'] );
?>
</p>
<?php
@@ -45,10 +45,10 @@
?>
<?php
-$described_options = array();
-foreach ( $options as $option ) {
- if ( isset( $option['desc'] ) ) {
- array_push( $described_options, $option );
+$nab_described_options = array();
+foreach ( $options as $nab_option ) {
+ if ( isset( $nab_option['desc'] ) ) {
+ array_push( $nab_described_options, $nab_option );
}
}
@@ -73,7 +73,7 @@
</span></p>
<?php
- if ( count( $described_options ) > 0 ) {
+ if ( count( $nab_described_options ) > 0 ) {
?>
<ul
style="list-style-type:disc;margin-left:3em;"
@@ -84,11 +84,11 @@
?>
>
<?php
- foreach ( $described_options as $option ) {
+ foreach ( $nab_described_options as $nab_option ) {
?>
<li><span class="description">
- <strong><?php $this->print_html( $option['label'] ); ?>.</strong>
- <?php $this->print_html( $option['desc'] ); ?>
+ <strong><?php $this->print_html( $nab_option['label'] ); ?>.</strong>
+ <?php $this->print_html( $nab_option['desc'] ); ?>
</span></li>
<?php
}
--- a/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-range-setting.php
+++ b/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-range-setting.php
@@ -16,7 +16,7 @@
*
* @var string $id The identifier of this field.
* @var string $name The name of this field.
- * @var boolean $disabled Whether this checkbox is disabled or not.
+ * @var boolean $disabled Whether this range is disabled or not.
* @var int $min The minimum value accepted by this range.
* @var int $max The maximum value accepted by this range.
* @var int $step The step this range uses.
--- a/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-select-setting.php
+++ b/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-select-setting.php
@@ -14,7 +14,7 @@
*
* @var string $id The identifier of this field.
* @var string $name The name of this field.
- * @var boolean $disabled Whether this checkbox is disabled or not.
+ * @var boolean $disabled Whether this select is disabled or not.
* @var list<array{value:string, label:string, desc?: string, disabled?:bool}> $options The list of options.
* @var string $value The concrete value of this field (or an empty string).
* @var string $desc Optional. The description of this field.
@@ -30,21 +30,21 @@
>
<?php
- foreach ( $options as $option ) {
+ foreach ( $options as $nab_option ) {
?>
- <option value="<?php echo esc_attr( $option['value'] ); ?>"
+ <option value="<?php echo esc_attr( $nab_option['value'] ); ?>"
<?php
- if ( $option['value'] === $value ) {
+ if ( $nab_option['value'] === $value ) {
echo ' selected="selected"';
}
?>
<?php
- if ( ! empty( $option['disabled'] ) ) {
+ if ( ! empty( $nab_option['disabled'] ) ) {
echo ' disabled';
}
?>
>
- <?php $this->print_html( $option['label'] ); ?>
+ <?php $this->print_html( $nab_option['label'] ); ?>
</option>
<?php
}
@@ -53,10 +53,10 @@
</select>
<?php
-$described_options = array();
-foreach ( $options as $option ) {
- if ( isset( $option['desc'] ) ) {
- array_push( $described_options, $option );
+$nab_described_options = array();
+foreach ( $options as $nab_option ) {
+ if ( isset( $nab_option['desc'] ) ) {
+ array_push( $nab_described_options, $nab_option );
}
}
@@ -81,7 +81,7 @@
</span></p>
<?php
- if ( count( $described_options ) > 0 ) {
+ if ( count( $nab_described_options ) > 0 ) {
?>
<ul
style="list-style-type:disc;margin-left:3em;"
@@ -92,11 +92,11 @@
?>
>
<?php
- foreach ( $described_options as $option ) {
+ foreach ( $nab_described_options as $nab_option ) {
?>
<li><span class="description">
- <strong><?php $this->print_html( $option['label'] ); ?>.</strong>
- <?php $this->print_html( $option['desc'] ); ?>
+ <strong><?php $this->print_html( $nab_option['label'] ); ?>.</strong>
+ <?php $this->print_html( $nab_option['desc'] ); ?>
</span></li>
<?php
}
--- a/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-tabs.php
+++ b/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-tabs.php
@@ -18,17 +18,17 @@
<h2 class="nav-tab-wrapper">
<?php
-foreach ( $tabs as $current_tab ) {
- if ( $current_tab['name'] === $opened_tab ) {
- $active = ' nav-tab-active';
+foreach ( $tabs as $nab_current_tab ) {
+ if ( $nab_current_tab['name'] === $opened_tab ) {
+ $nab_active = ' nav-tab-active';
} else {
- $active = '';
+ $nab_active = '';
}
printf(
'<a id="%1$s" class="nav-tab%3$s" href="#">%2$s</a>',
- esc_attr( $current_tab['name'] ),
- esc_html( $current_tab['label'] ),
- esc_attr( $active )
+ esc_attr( $nab_current_tab['name'] ),
+ esc_html( $nab_current_tab['label'] ),
+ esc_attr( $nab_active )
);
}
?>
--- a/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-text-area-setting.php
+++ b/nelio-ab-testing/includes/lib/settings/partials/nelio-ab-testing-text-area-setting.php
@@ -15,7 +15,7 @@
* @var string $id The identifier of this field.
* @var string $name The name of this field.
* @var string $value The concrete value of this field (or an empty string).
- * @var boolean $disabled Whether this checkbox is disabled or not.
+ * @var boolean $disabled Whether this textarea is disabled or not.
* @var string $placeholder Optional. A default placeholder.
* @var string $desc Optional. The description of this field.
* @var string $more Optional. A link with more information about this field.
@@ -23,7 +23,7 @@
?>
-<textarea id="<?php echo esc_attr( $id ); ?>" cols="40" rows="4" placeholder="<?php echo esc_attr( $placeholder ); ?>" <?php disabled( $disabled ); ?> name="<?php echo esc_attr( $name ); ?>"><?php echo esc_html( $value ); ?></textarea>
+<textarea id="<?php echo esc_attr( $id ); ?>" placeholder="<?php echo esc_attr( $placeholder ); ?>" <?php disabled( $disabled ); ?> name="<?php echo esc_attr( $name ); ?>"><?php echo esc_html( $value ); ?></textarea>
<?php
if ( ! empty( $desc ) ) {
--- a/nelio-ab-testing/includes/rest/class-nelio-ab-testing-experiment-rest-controller.php
+++ b/nelio-ab-testing/includes/rest/class-nelio-ab-testing-experiment-rest-controller.php
@@ -43,7 +43,6 @@
* @since 5.0.0
*/
public function init() {
-
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
@@ -254,6 +253,13 @@
);
}
+ if ( 'nab/php' === $type && ! current_user_can( 'edit_nab_php_experiments' ) ) {
+ return new WP_Error(
+ 'missing-capability',
+ _x( 'Sorry, you are not allowed to create a PHP test.', 'text', 'nelio-ab-testing' )
+ );
+ }
+
$experiment = nab_create_experiment( $type );
if ( is_wp_error( $experiment ) ) {
return new WP_Error(
@@ -429,6 +435,11 @@
return $experiment;
}
+ $can_be_edited = $experiment->can_be_edited();
+ if ( is_wp_error( $can_be_edited ) ) {
+ return $can_be_edited;
+ }
+
/** @var string */
$value = is_string( $parameters['name'] ) ? $parameters['name'] : '';
$experiment->set_name( $value );
--- a/nelio-ab-testing/includes/templates/public-result.php
+++ b/nelio-ab-testing/includes/templates/public-result.php
@@ -9,26 +9,29 @@
defined( 'ABSPATH' ) || exit;
-$aux = new Nelio_AB_Testing_Results_Page();
-$is_heatmap = $aux->is_heatmap_request();
-$page_title = $is_heatmap ?
+$nab_aux = new Nelio_AB_Testing_Results_Page();
+$nab_is_heatmap = $nab_aux->is_heatmap_request();
+$nab_page_title = $nab_is_heatmap ?
esc_html_x( 'Nelio A/B Testing - Heatmap Viewer', 'text', 'nelio-ab-testing' ) :
esc_html_x( 'Nelio A/B Testing - Results', 'text', 'nelio-ab-testing' );
-$handle = $is_heatmap ? 'nab-heatmap-results-page' : 'nab-results-page';
+$nab_handle = $nab_is_heatmap ? 'nab-heatmap-results-page' : 'nab-results-page';
?><!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title><?php echo esc_html( $page_title ); ?></title>
- <?php do_action( 'wp_enqueue_scripts' ); ?>
- <?php wp_print_styles( array( $handle ) ); ?>
+ <title><?php echo esc_html( $nab_page_title ); ?></title>
+ <?php
+ // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
+ do_action( 'wp_enqueue_scripts' );
+ ?>
+ <?php wp_print_styles( array( $nab_handle ) ); ?>
</head>
<body class="wp-core-ui">
- <?php if ( $is_heatmap ) { ?>
+ <?php if ( $nab_is_heatmap ) { ?>
<main id="nab-main" class="hide-if-no-js"></main>
@@ -42,7 +45,7 @@
<?php }//end if ?>
- <?php wp_print_scripts( array( $handle ) ); ?>
+ <?php wp_print_scripts( array( $nab_handle ) ); ?>
</body>
</html>
--- a/nelio-ab-testing/includes/utils/class-nelio-ab-testing-capability-manager.php
+++ b/nelio-ab-testing/includes/utils/class-nelio-ab-testing-capability-manager.php
@@ -51,7 +51,7 @@
$main_file = nelioab()->plugin_path . '/nelio-ab-testing.php';
register_activation_hook( $main_file, array( $this, 'add_capabilities' ) );
register_deactivation_hook( $main_file, array( $this, 'remove_capabilities' ) );
- add_action( 'nab_updated', array( $this, 'maybe_add_capabilities_on_update' ), 10, 2 );
+ add_action( 'nab_updated', array( $this, 'add_capabilities' ) );
}
/**
@@ -121,23 +121,6 @@
}
/**
- * Checks if we’re updating from a version prior to 6.0.1 and, if so, it adds the required capabilities.
- *
- * @param string $this_version this version.
- * @param string $prev_version previous version.
- *
- * @return void
- *
- * @since 6.0.1
- */
- public function maybe_add_capabilities_on_update( $this_version, $prev_version ) {
- if ( version_compare( $prev_version, '6.0.1', '<' ) ) {
- $this->remove_legacy_capabilities();
- $this->add_capabilities();
- }
- }
-
- /**
* Returns all the custom capabilities defined by Nelio A/B Testing.
*
* @return list<string> list of capabilities
@@ -149,27 +132,6 @@
}
/**
- * Removes legacy capabilities from roles.
- *
- * @return void
- */
- private function remove_legacy_capabilities() {
- $roles = get_option( 'wp_user_roles' );
- $roles = is_array( $roles ) ? array_keys( $roles ) : array();
- foreach ( $roles as $role_name ) {
- $role = get_role( $role_name );
- if ( $role ) {
- $caps = array_keys( $role->capabilities );
- foreach ( $caps as $cap ) {
- if ( 0 < strpos( $cap, 'nab_experiment' ) ) {
- $role->remove_cap( $cap );
- }
- }
- }
- }
- }
-
- /**
* Returns nab capabilities associated to a given role.
*
* @param string $role A role.
@@ -197,7 +159,10 @@
$admin_caps = array_merge(
$editor_caps,
- array( 'manage_nab_account' )
+ array(
+ 'manage_nab_account',
+ 'edit_nab_php_experiments',
+ )
);
$caps = array(
--- a/nelio-ab-testing/includes/utils/functions/core.php
+++ b/nelio-ab-testing/includes/utils/functions/core.php
@@ -12,12 +12,12 @@
/**
* Returns this site's ID.
*
- * @return string|false This site's ID. This option is used for accessing AWS.
+ * @return string This site's ID. This option is used for accessing AWS.
*
* @since 5.0.0
*/
function nab_get_site_id() {
- return get_option( 'nab_site_id', false );
+ return get_option( 'nab_site_id', '' );
}
/**
--- a/nelio-ab-testing/includes/utils/functions/helpers.php
+++ b/nelio-ab-testing/includes/utils/functions/helpers.php
@@ -543,7 +543,7 @@
*
* @since 5.3.4
*/
-function nablog( $log, $pre = false ) {
+function nab_log( $log, $pre = false ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! isset( $_GET['nablog'] ) ) {
return;
@@ -1150,13 +1150,13 @@
*
* @since 6.5.0
*/
-function add_nab_filter( $hook_name, $callback, $priority = 10, $args = 1 ) {
+function nab_add_filter( $hook_name, $callback, $priority = 10, $args = 1 ) {
/**
* Wraps regular WordPress filters into our own.
*
* @since 6.5.0
*/
- do_action( "add_nab_filter_for_{$hook_name}", $callback, $priority, $args );
+ do_action( "nab_add_filter_for_{$hook_name}", $callback, $priority, $args );
}
/**
--- a/nelio-ab-testing/nelio-ab-testing.php
+++ b/nelio-ab-testing/nelio-ab-testing.php
@@ -5,7 +5,7 @@
* Plugin Name: Nelio A/B Testing – AB Tests and Heatmaps for Better Conversion Optimization
* Plugin URI: https://neliosoftware.com/testing/
* Description: Optimize your site based on data, not opinions. With this plugin, you will be able to perform AB testing (and more) on your WordPress site.
- * Version: 8.1.8
+ * Version: 8.2.0
*
* Author: Nelio Software
* Author URI: https://neliosoftware.com
--- a/nelio-ab-testing/public/helpers/class-nelio-ab-testing-alternative-loader.php
+++ b/nelio-ab-testing/public/helpers/class-nelio-ab-testing-alternative-loader.php
@@ -72,7 +72,8 @@
* @return list<string>
*/
public function maybe_add_variant_in_body( $classes ) {
- if ( ! $this->get_number_of_alternatives() ) {
+ $runtime = Nelio_AB_Testing_Runtime::instance();
+ if ( ! $runtime->get_number_of_alternatives() ) {
return $classes;
}
@@ -121,45 +122,4 @@
}
}
-
- /**
- * Returns the number of combined alternatives.
- *
- * @return int
- */
- public function get_number_of_alternatives() {
-
- $gcd = function ( int $n, int $m ) use ( &$gcd ): int {
- if ( 0 === $n || 0 === $m ) {
- return 1;
- }
- if ( $n === $m && $n > 1 ) {
- return $n;
- }
- return $m < $n ? $gcd( $n - $m, $n ) : $gcd( $n, $m - $n );
- };
-
- $lcm = function ( int $n, int $m ) use ( &$gcd ): int {
- return $m * ( $n / $gcd( $n, $m ) );
- };
-
- $runtime = Nelio_AB_Testing_Runtime::instance();
- $experiments = $runtime->get_relevant_running_experiments();
- $alternatives = array_values(
- array_unique(
- array_map(
- function ( $experiment ) {
- return count( $experiment->get_alternatives() );
- },
- $experiments
- )
- )
- );
-
- if ( empty( $alternatives ) ) {
- return 0;
- }
-
- return array_reduce( $alternatives, $lcm, 1 );
- }
}
--- a/nelio-ab-testing/public/helpers/class-nelio-ab-testing-main-script.php
+++ b/nelio-ab-testing/public/helpers/class-nelio-ab-testing-main-script.php
@@ -82,55 +82,12 @@
return;
}
- $experiments = $this->get_running_experiment_summaries();
- $heatmaps = $this->get_relevant_heatmap_summaries();
- if ( $this->can_skip_script_enqueueing( $experiments, $heatmaps ) ) {
+ $settings = $this->get_script_settings();
+ if ( $this->can_skip_script_enqueueing( $settings['experiments'], $settings['heatmaps'] ) ) {
return;
}
- $alt_loader = Nelio_AB_Testing_Alternative_Loader::instance();
$plugin_settings = Nelio_AB_Testing_Settings::instance();
-
- $runtime = Nelio_AB_Testing_Runtime::instance();
- $settings = array(
- 'alternativeUrls' => $this->get_alternative_urls(),
- 'api' => $this->get_api_settings(),
- 'cookieTesting' => $this->get_cookie_testing(),
- 'excludeBots' => ! empty( $plugin_settings->get( 'exclude_bots' ) ),
- 'experiments' => $experiments,
- 'gdprCookie' => $this->get_gdpr_cookie(),
- 'heatmaps' => $heatmaps,
- 'hideQueryArgs' => ! empty( $plugin_settings->get( 'hide_query_args' ) ),
- 'ignoreTrailingSlash' => nab_ignore_trailing_slash_in_alternative_loading(),
- 'isGA4Integrated' => ! empty( $plugin_settings->get( 'google_analytics_tracking' )['enabled'] ),
- 'isStagingSite' => ! empty( nab_is_staging() ),
- 'isTestedPostRequest' => $runtime->is_tested_post_request(),
- 'maxCombinations' => nab_max_combinations(),
- 'nabPosition' => $plugin_settings->get( 'is_nab_first_arg' ) ? 'first' : 'last',
- 'numOfAlternatives' => $alt_loader->get_number_o