Move admin tester folders to root
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
use Automattic\WooCommerce\Admin\Notes\Note;
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/admin-notes/add-note/v1',
|
||||
'admin_notes_add_note'
|
||||
);
|
||||
|
||||
|
||||
function admin_notes_add_note( $request ) {
|
||||
$note = new Note();
|
||||
$mock_note_data = get_mock_note_data();
|
||||
$type = $request->get_param( 'type' );
|
||||
$layout = $request->get_param( 'layout' );
|
||||
|
||||
$note->set_name( $request->get_param( 'name' ) );
|
||||
$note->set_title( $request->get_param( 'title' ) );
|
||||
$note->set_content( $mock_note_data[ 'content' ] );
|
||||
$note->set_image( $mock_note_data[ $type ][ $layout ] );
|
||||
$note->set_layout( $layout );
|
||||
$note->set_type( $type );
|
||||
possibly_add_action( $note );
|
||||
|
||||
if ( 'email' === $type ) {
|
||||
add_email_note_params( $note );
|
||||
}
|
||||
|
||||
$note->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function add_email_note_params( $note ) {
|
||||
$additional_data = array(
|
||||
'role' => 'administrator',
|
||||
);
|
||||
$note->set_content_data( (object) $additional_data );
|
||||
}
|
||||
|
||||
function possibly_add_action( $note ) {
|
||||
if ( $note->get_type() === 'info' ) {
|
||||
return;
|
||||
}
|
||||
$action_name = sprintf(
|
||||
'test-action-%s',
|
||||
$note->get_name()
|
||||
);
|
||||
$note->add_action( $action_name, 'Test action', wc_admin_url() );
|
||||
}
|
||||
|
||||
function get_mock_note_data() {
|
||||
$plugin_url = site_url() . '/wp-content/plugins/woocommerce-admin-test-helper/';
|
||||
return array(
|
||||
'content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud.',
|
||||
'info' => array(
|
||||
'banner' => $plugin_url . 'images/admin-notes/banner.jpg',
|
||||
'thumbnail' => $plugin_url . 'images/admin-notes/thumbnail.jpg',
|
||||
'plain' => ''
|
||||
),
|
||||
'email' => array(
|
||||
'plain' => $plugin_url . 'images/admin-notes/woocommerce-logo-vector.png'
|
||||
),
|
||||
'update' => array(
|
||||
'plain' => ''
|
||||
)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/admin-notes/delete-all-notes/v1',
|
||||
'admin_notes_delete_all_notes'
|
||||
);
|
||||
|
||||
function admin_notes_delete_all_notes() {
|
||||
global $wpdb;
|
||||
|
||||
$deleted_note_count = $wpdb->query( "DELETE FROM {$wpdb->prefix}wc_admin_notes" );
|
||||
$deleted_action_count = $wpdb->query( "DELETE FROM {$wpdb->prefix}wc_admin_note_actions" );
|
||||
|
||||
return array(
|
||||
'deleted_note_count' => $deleted_note_count,
|
||||
'deleted_action_count' => $deleted_action_count,
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
function register_woocommerce_admin_test_helper_rest_route( $route, $callback, $additional_options = array() ) {
|
||||
add_action( 'rest_api_init', function() use ( $route, $callback, $additional_options ) {
|
||||
|
||||
$default_options = array(
|
||||
'methods' => 'POST',
|
||||
'callback' => $callback,
|
||||
'permission_callback' => function( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_rest_cannot_edit',
|
||||
__( 'Sorry, you cannot perform this action', 'woocommerce-admin-test-helper' )
|
||||
);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
$default_options = array_merge( $default_options, $additional_options );
|
||||
|
||||
register_rest_route(
|
||||
'wc-admin-test-helper',
|
||||
$route,
|
||||
$default_options
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
require( 'admin-notes/delete-all-notes.php' );
|
||||
require( 'admin-notes/add-note.php' );
|
||||
require( 'tools/trigger-wca-install.php' );
|
||||
require( 'tools/trigger-cron-job.php' );
|
||||
require( 'tools/run-wc-admin-daily.php' );
|
||||
require( 'options/rest-api.php' );
|
||||
require( 'tools/delete-all-products.php');
|
||||
require( 'tools/disable-wc-email.php' );
|
||||
require( 'tools/trigger-update-callbacks.php' );
|
||||
require( 'tracks/tracks-debug-log.php' );
|
||||
require( 'features/features.php' );
|
||||
require( 'rest-api-filters/rest-api-filters.php' );
|
||||
require( 'rest-api-filters/hook.php' );
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
|
||||
const OPTION_NAME_PREFIX = 'wc_admin_helper_feature_values';
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/features/(?P<feature_name>[a-z0-9_\-]+)/toggle',
|
||||
'toggle_feature',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/features',
|
||||
'get_features',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/features/reset',
|
||||
'reset_features',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
function toggle_feature( $request ) {
|
||||
$features = get_features();
|
||||
$custom_feature_values = get_option( OPTION_NAME_PREFIX, array() );
|
||||
$feature_name = $request->get_param( 'feature_name' );
|
||||
|
||||
if ( ! isset( $features[$feature_name ]) ) {
|
||||
return new WP_REST_Response( $features, 204 );
|
||||
}
|
||||
|
||||
if ( isset( $custom_feature_values[$feature_name] ) ) {
|
||||
unset( $custom_feature_values[$feature_name] );
|
||||
} else {
|
||||
$custom_feature_values[$feature_name] = ! $features[ $feature_name ];
|
||||
}
|
||||
|
||||
update_option(OPTION_NAME_PREFIX, $custom_feature_values );
|
||||
return new WP_REST_Response( get_features(), 200 );
|
||||
}
|
||||
|
||||
function reset_features() {
|
||||
delete_option( OPTION_NAME_PREFIX );
|
||||
return new WP_REST_Response( get_features(), 200 );
|
||||
}
|
||||
|
||||
function get_features() {
|
||||
if ( function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
return apply_filters( 'woocommerce_admin_get_feature_config', wc_admin_get_feature_config() );
|
||||
}
|
||||
return array();
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/options',
|
||||
'wca_test_helper_get_options',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'args' => array(
|
||||
'page' => array(
|
||||
'description' => 'Current page of the collection.',
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
),
|
||||
'per_page' => array(
|
||||
'description' => 'Maximum number of items to be returned in result set.',
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'sanitize_callback' => 'absint',
|
||||
),
|
||||
'search' => array(
|
||||
'description' => 'Limit results to those matching a string.',
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/options/(?P<option_names>(.*)+)',
|
||||
'wca_test_helper_delete_option',
|
||||
array(
|
||||
'methods' => 'DELETE',
|
||||
'args' => array(
|
||||
'option_names' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
function wca_test_helper_delete_option( $request ) {
|
||||
global $wpdb;
|
||||
$option_names = explode( ',', $request->get_param( 'option_names' ) );
|
||||
$option_names = array_map( function( $option_name ) {
|
||||
return "'" . $option_name . "'";
|
||||
}, $option_names );
|
||||
|
||||
$option_names = implode( ',', $option_names );
|
||||
$query = "delete from {$wpdb->prefix}options where option_name in ({$option_names})";
|
||||
$wpdb->query( $query );
|
||||
|
||||
return new WP_REST_RESPONSE( null, 204 );
|
||||
}
|
||||
|
||||
function wca_test_helper_get_options( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$per_page = $request->get_param( 'per_page' );
|
||||
$page = $request->get_param( 'page' );
|
||||
$search = $request->get_param( 'search' );
|
||||
|
||||
$query = "
|
||||
select option_id, option_name, option_value, autoload
|
||||
from {$wpdb->prefix}options
|
||||
";
|
||||
|
||||
if ( $search ) {
|
||||
$query .= "where option_name like '%{$search}%'";
|
||||
}
|
||||
|
||||
$query .= ' order by option_id desc limit 30';
|
||||
|
||||
$options = $wpdb->get_results( $query );
|
||||
|
||||
return new WP_REST_Response( $options, 200 );
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
$filters = get_option(WCA_Test_Helper_Rest_Api_Filters::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, [] );
|
||||
|
||||
function array_dot_set( &$array, $key, $value ) {
|
||||
if ( is_null( $key ) ) {
|
||||
return $array = $value;
|
||||
}
|
||||
|
||||
$keys = explode('.', $key);
|
||||
|
||||
while ( count($keys) > 1 ) {
|
||||
$key = array_shift($keys);
|
||||
if (! isset($array[$key]) || ! is_array($array[$key]) ) {
|
||||
$array[$key] = [];
|
||||
}
|
||||
$array = &$array[$key];
|
||||
}
|
||||
|
||||
$array[ array_shift($keys) ] = $value;
|
||||
return $array;
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'rest_request_after_callbacks',
|
||||
function ( $response, array $handler, \WP_REST_Request $request ) use ( $filters ) {
|
||||
if (! $response instanceof \WP_REST_Response ) {
|
||||
return $response;
|
||||
}
|
||||
$route = $request->get_route();
|
||||
$filters = array_filter(
|
||||
$filters, function ( $filter ) use ( $request, $route ) {
|
||||
if ($filter['enabled'] && $filter['endpoint'] == $route ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
$data = $response->get_data();
|
||||
|
||||
foreach ( $filters as $filter ) {
|
||||
array_dot_set($data, $filter['dot_notation'], $filter['replacement']);
|
||||
}
|
||||
|
||||
$response->set_data($data);
|
||||
|
||||
return $response;
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/rest-api-filters',
|
||||
[ WCA_Test_Helper_Rest_Api_Filters::class, 'create' ],
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'args' => array(
|
||||
'endpoint' => array(
|
||||
'description' => 'Rest API endpoint.',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'dot_notation' => array(
|
||||
'description' => 'Dot notation of the target field.',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'replacement' => array(
|
||||
'description' => 'Replacement value for the target field.',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/rest-api-filters',
|
||||
[ WCA_Test_Helper_Rest_Api_Filters::class, 'delete' ],
|
||||
array(
|
||||
'methods' => 'DELETE',
|
||||
'args' => array(
|
||||
'index' => array(
|
||||
'description' => 'Rest API endpoint.',
|
||||
'type' => 'integer',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/rest-api-filters/(?P<index>\d+)/toggle',
|
||||
[ WCA_Test_Helper_Rest_Api_Filters::class, 'toggle' ],
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
class WCA_Test_Helper_Rest_Api_Filters {
|
||||
const WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION = 'wc-admin-test-helper-rest-api-filters';
|
||||
|
||||
public static function create( $request ) {
|
||||
$endpoint = $request->get_param('endpoint');
|
||||
$dot_notation = $request->get_param('dot_notation');
|
||||
$replacement = $request->get_param('replacement');
|
||||
|
||||
self::update(
|
||||
function ( $filters ) use (
|
||||
$endpoint,
|
||||
$dot_notation,
|
||||
$replacement
|
||||
) {
|
||||
$filters[] = array(
|
||||
'endpoint' => $endpoint,
|
||||
'dot_notation' => $dot_notation,
|
||||
'replacement' => filter_var( $replacement, FILTER_VALIDATE_BOOLEAN ),
|
||||
'enabled' => true,
|
||||
);
|
||||
return $filters;
|
||||
}
|
||||
);
|
||||
return new WP_REST_RESPONSE(null, 204);
|
||||
}
|
||||
|
||||
public static function update( callable $callback ) {
|
||||
$filters = get_option(self::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, array());
|
||||
$filters = $callback( $filters );
|
||||
return update_option(self::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, $filters);
|
||||
}
|
||||
|
||||
public static function delete( $request ) {
|
||||
self::update(
|
||||
function ( $filters ) use ( $request ) {
|
||||
array_splice($filters, $request->get_param('index'), 1);
|
||||
return $filters;
|
||||
}
|
||||
);
|
||||
|
||||
return new WP_REST_RESPONSE(null, 204);
|
||||
}
|
||||
|
||||
public static function toggle( $request ) {
|
||||
self::update(
|
||||
function ( $filters ) use ( $request ) {
|
||||
$index = $request->get_param('index');
|
||||
$filters[$index]['enabled'] = !$filters[$index]['enabled'];
|
||||
return $filters;
|
||||
}
|
||||
);
|
||||
return new WP_REST_RESPONSE(null, 204);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/delete-all-products/v1',
|
||||
'tools_delete_all_products'
|
||||
);
|
||||
|
||||
function tools_delete_all_products() {
|
||||
$query = new \WC_Product_Query();
|
||||
$products = $query->get_products();
|
||||
|
||||
foreach ( $products as $product ) {
|
||||
$product->delete( true );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/toggle-emails/v1',
|
||||
'toggle_emails'
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/get-email-status/v1',
|
||||
'get_email_status',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
)
|
||||
);
|
||||
|
||||
function toggle_emails() {
|
||||
$emails_disabled = 'yes';
|
||||
if ( $emails_disabled === get_option( 'wc_admin_test_helper_email_disabled', 'no' ) ) {
|
||||
$emails_disabled = 'no';
|
||||
remove_filter('woocommerce_email_get_option', 'disable_wc_emails' );
|
||||
}
|
||||
update_option('wc_admin_test_helper_email_disabled', $emails_disabled );
|
||||
return new WP_REST_Response( $emails_disabled, 200 );
|
||||
}
|
||||
|
||||
function get_email_status() {
|
||||
$emails_disabled = get_option( 'wc_admin_test_helper_email_disabled', 'no' );
|
||||
return new WP_REST_Response( $emails_disabled, 200 );
|
||||
}
|
||||
|
||||
if ( 'yes' === get_option( 'wc_admin_test_helper_email_disabled', 'no' ) ) {
|
||||
add_filter('woocommerce_email_get_option', 'disable_wc_emails' );
|
||||
add_action( 'woocommerce_email', 'unhook_other_wc_emails' );
|
||||
}
|
||||
|
||||
function disable_wc_emails( $key ) {
|
||||
if ( $key === 'enabled' ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function unhook_other_wc_emails( $email ) {
|
||||
remove_action( 'woocommerce_low_stock_notification', array( $email, 'low_stock' ) );
|
||||
remove_action( 'woocommerce_no_stock_notification', array( $email, 'no_stock' ) );
|
||||
remove_action( 'woocommerce_product_on_backorder_notification', array( $email, 'backorder' ) );
|
||||
remove_action( 'woocommerce_new_customer_note_notification', array( $email->emails['WC_Email_Customer_Note'], 'trigger' ) );
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/run-wc-admin-daily/v1',
|
||||
'tools_run_wc_admin_daily'
|
||||
);
|
||||
|
||||
function tools_run_wc_admin_daily() {
|
||||
do_action('wc_admin_daily');
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/get-cron-list/v1',
|
||||
'tools_get_cron_list',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
)
|
||||
);
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/trigger-selected-cron/v1',
|
||||
'trigger_selected_cron',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'args' => array(
|
||||
'hook' => array(
|
||||
'description' => 'Name of the cron that will be triggered.',
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'signature' => array(
|
||||
'description' => 'Signature of the cron to trigger.',
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
function tools_get_cron_list() {
|
||||
$crons = _get_cron_array();
|
||||
$events = array();
|
||||
|
||||
if ( empty( $crons ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ( $crons as $cron ) {
|
||||
foreach ( $cron as $hook => $data ) {
|
||||
foreach ( $data as $signature => $element ) {
|
||||
$events[ $hook ] = (object) array(
|
||||
'hook' => $hook,
|
||||
'signature' => $signature,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new WP_REST_Response( $events, 200 );
|
||||
}
|
||||
|
||||
function trigger_selected_cron( $request ) {
|
||||
$hook = $request->get_param( 'hook' );
|
||||
$signature = $request->get_param( 'signature' );
|
||||
|
||||
if ( ! isset( $hook ) || ! isset( $signature ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$crons = _get_cron_array();
|
||||
foreach ( $crons as $cron ) {
|
||||
if ( isset( $cron[ $hook ][ $signature ] ) ) {
|
||||
$args = $cron[ $hook ][ $signature ]['args'];
|
||||
delete_transient( 'doing_cron' );
|
||||
$scheduled = schedule_event( $hook, $args );
|
||||
|
||||
if ( false === $scheduled ) {
|
||||
return $scheduled;
|
||||
}
|
||||
|
||||
add_filter( 'cron_request', function( array $cron_request ) {
|
||||
$cron_request['url'] = add_query_arg( 'run-cron', 1, $cron_request['url'] );
|
||||
return $cron_request;
|
||||
} );
|
||||
|
||||
spawn_cron();
|
||||
sleep( 1 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function schedule_event( $hook, $args = array() ) {
|
||||
$event = (object) array(
|
||||
'hook' => $hook,
|
||||
'timestamp' => 1,
|
||||
'schedule' => false,
|
||||
'args' => $args,
|
||||
);
|
||||
$crons = (array) _get_cron_array();
|
||||
$key = md5( serialize( $event->args ) );
|
||||
|
||||
$crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
|
||||
'schedule' => $event->schedule,
|
||||
'args' => $event->args,
|
||||
);
|
||||
uksort( $crons, 'strnatcasecmp' );
|
||||
return _set_cron_array( $crons );
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\Cache;
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/get-update-versions/v1',
|
||||
'tools_get_wc_admin_versions',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
)
|
||||
);
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/trigger-selected-update-callbacks/v1',
|
||||
'trigger_selected_update_callbacks',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'args' => array(
|
||||
'version' => array(
|
||||
'description' => 'Name of the update version',
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
function tools_get_wc_admin_versions() {
|
||||
$db_updates = \WC_Install::get_db_update_callbacks();
|
||||
|
||||
return new WP_REST_Response( array_keys( $db_updates ), 200 );
|
||||
}
|
||||
|
||||
function trigger_selected_update_callbacks( $request ) {
|
||||
$version = $request->get_param( 'version' );
|
||||
if ( ! isset( $version ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db_updates = \WC_Install::get_db_update_callbacks();
|
||||
$update_callbacks = $db_updates[ $version ];
|
||||
|
||||
foreach ( $update_callbacks as $update_callback ) {
|
||||
call_user_func( $update_callback );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/tools/trigger-wca-install/v1',
|
||||
'tools_trigger_wca_install'
|
||||
);
|
||||
|
||||
function tools_trigger_wca_install() {
|
||||
\WC_Install::install();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A class for logging tracked events.
|
||||
*/
|
||||
class TracksDebugLog {
|
||||
/**
|
||||
* Logger class to use.
|
||||
*
|
||||
* @var WC_Logger_Interface|null
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Logger source.
|
||||
*
|
||||
* @var string logger source.
|
||||
*/
|
||||
private $source = 'tracks';
|
||||
|
||||
/**
|
||||
* Initialize hooks.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'woocommerce_tracks_event_properties', array( $this, 'log_event' ), 10, 2 );
|
||||
$logger = wc_get_logger();
|
||||
$this->logger = $logger;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the event.
|
||||
*
|
||||
* @param array $properties Event properties.
|
||||
* @param string $event_name Event name.
|
||||
*/
|
||||
public function log_event( $properties, $event_name ) {
|
||||
$this->logger->debug(
|
||||
$event_name,
|
||||
array( 'source' => $this->source )
|
||||
);
|
||||
foreach ( $properties as $key => $property ) {
|
||||
$this->logger->debug(
|
||||
" - {$key}: {$property}",
|
||||
array( 'source' => $this->source )
|
||||
);
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
||||
|
||||
new TracksDebugLog();
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 5.8 KiB |
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* External dependencies.
|
||||
*/
|
||||
import { useState } from '@wordpress/element';
|
||||
import { Button, SelectControl } from '@wordpress/components';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
|
||||
export const AddNote = () => {
|
||||
const [ isAdding, setIsAdding ] = useState( false );
|
||||
const [ hasAdded, setHasAdded ] = useState( false );
|
||||
const [ errorMessage, setErrorMessage ] = useState( false );
|
||||
const [ noteType, setNoteType ] = useState( 'info' );
|
||||
const [ noteLayout, setNoteLayout ] = useState( 'plain' );
|
||||
|
||||
async function triggerAddNote() {
|
||||
setIsAdding( true );
|
||||
setHasAdded( false );
|
||||
setErrorMessage( false );
|
||||
|
||||
const name = prompt( 'Enter the note name' );
|
||||
if ( ! name ) {
|
||||
setIsAdding( false );
|
||||
return;
|
||||
}
|
||||
|
||||
const title = prompt( 'Enter the note title' );
|
||||
if ( ! title ) {
|
||||
setIsAdding( false );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiFetch( {
|
||||
path: '/wc-admin-test-helper/admin-notes/add-note/v1',
|
||||
method: 'POST',
|
||||
data: {
|
||||
name,
|
||||
type: noteType,
|
||||
layout: noteLayout,
|
||||
title,
|
||||
},
|
||||
} );
|
||||
setHasAdded( true );
|
||||
}
|
||||
catch ( ex ) {
|
||||
setErrorMessage( ex.message );
|
||||
}
|
||||
|
||||
setIsAdding( false );
|
||||
}
|
||||
|
||||
function onTypeChange( val ) {
|
||||
setNoteType( val );
|
||||
if ( val !== 'info' ) {
|
||||
setNoteLayout( 'plain' );
|
||||
}
|
||||
}
|
||||
|
||||
function onLayoutChange( val ) {
|
||||
setNoteLayout( val );
|
||||
}
|
||||
|
||||
function getAddNoteDescription() {
|
||||
switch ( noteType ){
|
||||
case 'email':
|
||||
return (
|
||||
<>
|
||||
This will add a new <strong>email</strong> note. Enable email insights{' '}
|
||||
<a href="/wp-admin/admin.php?page=wc-settings&tab=email">
|
||||
here
|
||||
</a>{' '}
|
||||
and run the cron to send the note by email.
|
||||
</>);
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
This will add a new note. Currently only the note name
|
||||
and title will be used to create the note.
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p><strong>Add a note</strong></p>
|
||||
<div>
|
||||
{ getAddNoteDescription() }
|
||||
<br/>
|
||||
<div className="woocommerce-admin-test-helper__add-notes">
|
||||
<Button
|
||||
onClick={ triggerAddNote }
|
||||
disabled={ isAdding }
|
||||
isPrimary
|
||||
>
|
||||
Add admin note
|
||||
</Button>
|
||||
<SelectControl
|
||||
label="Type"
|
||||
onChange={ onTypeChange }
|
||||
labelPosition="side"
|
||||
options={ [
|
||||
{ label: 'Info', value: 'info' },
|
||||
{ label: 'Update', value: 'update' },
|
||||
{ label: 'Email', value: 'email' },
|
||||
] }
|
||||
value={ noteType }
|
||||
/>
|
||||
<SelectControl
|
||||
label="Layout"
|
||||
onChange={ onLayoutChange }
|
||||
labelPosition="side"
|
||||
options={ [
|
||||
{ label: 'Plain', value: 'plain' },
|
||||
{ label: 'Banner', value: 'banner' },
|
||||
{ label: 'Thumbnail', value: 'thumbnail' },
|
||||
] }
|
||||
disabled={ noteType !== 'info' }
|
||||
value={ noteLayout }
|
||||
/>
|
||||
</div>
|
||||
<br/>
|
||||
<span className="woocommerce-admin-test-helper__action-status">
|
||||
{ isAdding && 'Adding, please wait' }
|
||||
{ hasAdded && 'Note added' }
|
||||
{ errorMessage && (
|
||||
<>
|
||||
<strong>Error:</strong> { errorMessage }
|
||||
</>
|
||||
) }
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import { DeleteAllNotes } from './delete-all-notes';
|
||||
import { AddNote } from './add-note';
|
||||
|
||||
export const AdminNotes = () => {
|
||||
return (
|
||||
<>
|
||||
<h2>Admin notes</h2>
|
||||
<p>This section contains tools for managing admin notes.</p>
|
||||
<AddNote/>
|
||||
<DeleteAllNotes/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* External dependencies.
|
||||
*/
|
||||
import { useState } from '@wordpress/element';
|
||||
import { Button } from '@wordpress/components';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
export const DeleteAllNotes = () => {
|
||||
const [ isDeleting, setIsDeleting ] = useState( false );
|
||||
const [ deleteStatus, setDeleteStatus ] = useState( false );
|
||||
const [ errorMessage, setErrorMessage ] = useState( false );
|
||||
|
||||
async function triggerDeleteAllNotes() {
|
||||
setIsDeleting( true );
|
||||
setErrorMessage( false );
|
||||
setDeleteStatus( false );
|
||||
|
||||
try {
|
||||
const response = await apiFetch( {
|
||||
path: '/wc-admin-test-helper/admin-notes/delete-all-notes/v1',
|
||||
method: 'POST',
|
||||
} );
|
||||
|
||||
setDeleteStatus( response );
|
||||
} catch ( ex ) {
|
||||
setErrorMessage( ex.message );
|
||||
}
|
||||
|
||||
setIsDeleting( false );
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p><strong>Delete all admin notes</strong></p>
|
||||
<p>
|
||||
This will delete all notes from the <code>wp_wc_admin_notes</code>
|
||||
table, and actions from the <code>wp_wc_admin_note_actions</code>
|
||||
table.
|
||||
<br/>
|
||||
<Button
|
||||
onClick={ triggerDeleteAllNotes }
|
||||
disabled={ isDeleting }
|
||||
isPrimary
|
||||
>
|
||||
Delete all notes
|
||||
</Button>
|
||||
<br/>
|
||||
<span className="woocommerce-admin-test-helper__action-status">
|
||||
{ isDeleting && 'Deleting, please wait.' }
|
||||
{ deleteStatus && (
|
||||
<>
|
||||
Deleted{ ' ' }
|
||||
<strong>{ deleteStatus.deleted_note_count }</strong>{ ' ' }
|
||||
admin notes and{ ' ' }
|
||||
<strong>{ deleteStatus.deleted_action_count }</strong>{ ' ' }
|
||||
actions.
|
||||
</>
|
||||
) }
|
||||
{ errorMessage && (
|
||||
<>
|
||||
<strong>Error: </strong>
|
||||
{ errorMessage }
|
||||
</>
|
||||
) }
|
||||
</span>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export { AdminNotes } from './admin-notes.js';
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TabPanel } from '@wordpress/components';
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { AdminNotes } from '../admin-notes';
|
||||
import { default as Tools } from '../tools';
|
||||
import { default as Options } from '../options';
|
||||
import { default as Experiments } from '../experiments';
|
||||
import { default as Features } from '../features';
|
||||
import { default as RestAPIFilters } from '../rest-api-filters';
|
||||
|
||||
const tabs = applyFilters( 'woocommerce_admin_test_helper_tabs', [
|
||||
{
|
||||
name: 'options',
|
||||
title: 'Options',
|
||||
content: <Options />,
|
||||
},
|
||||
{
|
||||
name: 'admin-notes',
|
||||
title: 'Admin notes',
|
||||
content: <AdminNotes />,
|
||||
},
|
||||
{
|
||||
name: 'tools',
|
||||
title: 'Tools',
|
||||
content: <Tools />,
|
||||
},
|
||||
{
|
||||
name: 'experiments',
|
||||
title: 'Experiments',
|
||||
content: <Experiments />,
|
||||
},
|
||||
{
|
||||
name: 'features',
|
||||
title: 'Features',
|
||||
content: <Features />,
|
||||
},
|
||||
{
|
||||
name: 'rest-api-filters',
|
||||
title: 'REST API FIlters',
|
||||
content: <RestAPIFilters />,
|
||||
},
|
||||
] );
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<div className="wrap">
|
||||
<h1>WooCommerce Admin Test Helper</h1>
|
||||
<TabPanel
|
||||
className="woocommerce-admin-test-helper__main-tab-panel"
|
||||
activeClass="active-tab"
|
||||
tabs={ tabs }
|
||||
initialTabName={ tabs[ 0 ].name }
|
||||
>
|
||||
{ ( tab ) => (
|
||||
<>
|
||||
{ tab.content }
|
||||
{ applyFilters(
|
||||
`woocommerce_admin_test_helper_tab_${ tab.name }`,
|
||||
[]
|
||||
) }
|
||||
</>
|
||||
) }
|
||||
</TabPanel>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { App } from './app';
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function NewExperimentForm( { addExperiment } ) {
|
||||
const [ experimentName, setExperimentName ] = useState( null );
|
||||
const [ variation, setVariation ] = useState( 'treatment' );
|
||||
|
||||
const getInputValue = ( event ) => {
|
||||
setExperimentName( event.target.value );
|
||||
};
|
||||
|
||||
const getVariationInput = ( event ) => {
|
||||
setVariation( event.target.value );
|
||||
};
|
||||
|
||||
const AddNewExperiment = () => {
|
||||
addExperiment( experimentName, variation );
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="manual-input">
|
||||
<div className="description">
|
||||
Don't see an experiment you want to test? Add it manually.
|
||||
</div>
|
||||
<input type="text" onChange={ getInputValue } />
|
||||
<select value={ variation } onChange={ getVariationInput }>
|
||||
<option value="treatment">treatment</option>
|
||||
<option value="control">control</option>
|
||||
</select>
|
||||
|
||||
<Button isPrimary onClick={ AddNewExperiment }>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { addExperiment } = dispatch( STORE_KEY );
|
||||
return {
|
||||
addExperiment,
|
||||
};
|
||||
} )
|
||||
)( NewExperimentForm );
|
|
@ -0,0 +1,8 @@
|
|||
const TYPES = {
|
||||
TOGGLE_EXPERIMENT: 'TOGGLE_EXPERIMENT',
|
||||
SET_EXPERIMENTS: 'SET_EXPERIMENTS',
|
||||
ADD_EXPERIMENT: 'ADD_EXPERIMENT',
|
||||
DELETE_EXPERIMENT: 'DELETE_EXPERIMENT',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import {
|
||||
EXPERIMENT_NAME_PREFIX,
|
||||
TRANSIENT_NAME_PREFIX,
|
||||
TRANSIENT_TIMEOUT_NAME_PREFIX,
|
||||
} from './constants';
|
||||
|
||||
function toggleFrontendExperiment( experimentName, newVariation ) {
|
||||
let storageItem = JSON.parse(
|
||||
window.localStorage.getItem( EXPERIMENT_NAME_PREFIX + experimentName )
|
||||
);
|
||||
|
||||
// If the experiment is not in localStorage, consider it as a new.
|
||||
if ( storageItem === null ) {
|
||||
storageItem = {
|
||||
experimentName,
|
||||
retrievedTimestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
storageItem.variationName = newVariation;
|
||||
storageItem.ttl = 3600;
|
||||
|
||||
window.localStorage.setItem(
|
||||
EXPERIMENT_NAME_PREFIX + experimentName,
|
||||
JSON.stringify( storageItem )
|
||||
);
|
||||
}
|
||||
|
||||
function* toggleBackendExperiment( experimentName, newVariation ) {
|
||||
try {
|
||||
const payload = {};
|
||||
payload[ TRANSIENT_NAME_PREFIX + experimentName ] = newVariation;
|
||||
payload[ TRANSIENT_TIMEOUT_NAME_PREFIX + experimentName ] =
|
||||
Math.round( Date.now() / 1000 ) + 3600;
|
||||
|
||||
yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: '/wc-admin/options',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify( payload ),
|
||||
} );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* toggleExperiment( experimentName, currentVariation ) {
|
||||
const newVariation =
|
||||
currentVariation === 'control' ? 'treatment' : 'control';
|
||||
|
||||
toggleFrontendExperiment( experimentName, newVariation );
|
||||
yield toggleBackendExperiment( experimentName, newVariation );
|
||||
|
||||
return {
|
||||
type: TYPES.TOGGLE_EXPERIMENT,
|
||||
experimentName,
|
||||
newVariation,
|
||||
};
|
||||
}
|
||||
|
||||
export function setExperiments( experiments ) {
|
||||
return {
|
||||
type: TYPES.SET_EXPERIMENTS,
|
||||
experiments,
|
||||
};
|
||||
}
|
||||
|
||||
export function* addExperiment( experimentName, variation ) {
|
||||
toggleFrontendExperiment( experimentName, variation );
|
||||
yield toggleBackendExperiment( experimentName, variation );
|
||||
|
||||
return {
|
||||
type: TYPES.ADD_EXPERIMENT,
|
||||
experimentName,
|
||||
variation,
|
||||
};
|
||||
}
|
||||
|
||||
export function* deleteExperiment( experimentName ) {
|
||||
window.localStorage.removeItem( EXPERIMENT_NAME_PREFIX + experimentName );
|
||||
|
||||
const optionNames = [
|
||||
TRANSIENT_NAME_PREFIX + experimentName,
|
||||
TRANSIENT_TIMEOUT_NAME_PREFIX + experimentName,
|
||||
];
|
||||
|
||||
yield apiFetch( {
|
||||
method: 'DELETE',
|
||||
path: '/wc-admin-test-helper/options/' + optionNames.join( ',' ),
|
||||
} );
|
||||
|
||||
return {
|
||||
type: TYPES.DELETE_EXPERIMENT,
|
||||
experimentName,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/experiments';
|
||||
export const EXPERIMENT_NAME_PREFIX = 'explat-experiment--';
|
||||
export const TRANSIENT_NAME_PREFIX = '_transient_abtest_variation_';
|
||||
export const TRANSIENT_TIMEOUT_NAME_PREFIX =
|
||||
'_transient_timeout_abtest_variation';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
experiments: [],
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.DELETE_EXPERIMENT:
|
||||
return {
|
||||
...state,
|
||||
experiments: state.experiments.filter( ( experiment ) => {
|
||||
return experiment.name !== action.experimentName;
|
||||
} ),
|
||||
};
|
||||
case TYPES.ADD_EXPERIMENT:
|
||||
const existingExperimentIndex = state.experiments.findIndex(
|
||||
( element ) => {
|
||||
return element.name === action.experimentName;
|
||||
}
|
||||
);
|
||||
const newExperiment = {
|
||||
name: action.experimentName,
|
||||
variation: action.variation,
|
||||
};
|
||||
const newExperiments =
|
||||
existingExperimentIndex !== -1
|
||||
? state.experiments
|
||||
.slice( 0, existingExperimentIndex )
|
||||
.concat( newExperiment )
|
||||
.concat(
|
||||
state.experiments.slice(
|
||||
existingExperimentIndex + 1
|
||||
)
|
||||
)
|
||||
: [
|
||||
...state.experiments,
|
||||
{
|
||||
name: action.experimentName,
|
||||
variation: action.variation,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
...state,
|
||||
experiments: newExperiments,
|
||||
};
|
||||
case TYPES.TOGGLE_EXPERIMENT:
|
||||
return {
|
||||
...state,
|
||||
experiments: state.experiments.map( ( experiment ) => ( {
|
||||
...experiment,
|
||||
variation:
|
||||
experiment.name === action.experimentName
|
||||
? action.newVariation
|
||||
: experiment.variation,
|
||||
} ) ),
|
||||
};
|
||||
case TYPES.SET_EXPERIMENTS:
|
||||
return {
|
||||
...state,
|
||||
experiments: action.experiments,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { setExperiments } from './actions';
|
||||
import {
|
||||
EXPERIMENT_NAME_PREFIX,
|
||||
TRANSIENT_NAME_PREFIX,
|
||||
API_NAMESPACE,
|
||||
} from './constants';
|
||||
|
||||
function getExperimentsFromFrontend() {
|
||||
const storageItems = Object.entries( { ...window.localStorage } ).filter(
|
||||
( item ) => {
|
||||
return item[ 0 ].indexOf( EXPERIMENT_NAME_PREFIX ) === 0;
|
||||
}
|
||||
);
|
||||
|
||||
return storageItems.map( ( storageItem ) => {
|
||||
const [ key, value ] = storageItem;
|
||||
const objectValue = JSON.parse( value );
|
||||
return {
|
||||
name: key.replace( EXPERIMENT_NAME_PREFIX, '' ),
|
||||
variation: objectValue.variationName || 'control',
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
export function* getExperiments() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/options?search=_transient_abtest_variation_`,
|
||||
} );
|
||||
|
||||
const experimentsFromBackend = response.map( ( experiment ) => {
|
||||
return {
|
||||
name: experiment.option_name.replace(
|
||||
TRANSIENT_NAME_PREFIX,
|
||||
''
|
||||
),
|
||||
variation:
|
||||
experiment.option_value === 'control'
|
||||
? 'control'
|
||||
: 'treatment',
|
||||
};
|
||||
} );
|
||||
|
||||
// Remove duplicate.
|
||||
const experiments = getExperimentsFromFrontend()
|
||||
.concat( experimentsFromBackend )
|
||||
.filter(
|
||||
( value, index, self ) =>
|
||||
index ===
|
||||
self.findIndex(
|
||||
( t ) =>
|
||||
t.place === value.place && t.name === value.name
|
||||
)
|
||||
);
|
||||
|
||||
yield setExperiments( experiments );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export function getExperiments( state ) {
|
||||
return state.experiments;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
import NewExperimentForm from './NewExperimentForm';
|
||||
|
||||
function Experiments( {
|
||||
experiments,
|
||||
toggleExperiment,
|
||||
deleteExperiment,
|
||||
isTrackingEnabled,
|
||||
isResolving,
|
||||
} ) {
|
||||
if ( isResolving ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-experiments">
|
||||
<h2>Experiments</h2>
|
||||
{ isTrackingEnabled === 'no' && (
|
||||
<p className="tracking-disabled">
|
||||
The following list might not be complete without tracking
|
||||
enabled. <br />
|
||||
Please visit
|
||||
<a
|
||||
target="_blank"
|
||||
href={
|
||||
wcSettings.adminUrl +
|
||||
'/admin.php?page=wc-settings&tab=advanced§ion=woocommerce_com'
|
||||
}
|
||||
rel="noreferrer"
|
||||
>
|
||||
WooCommerce → Settings → Advanced →
|
||||
Woocommerce.com
|
||||
</a>
|
||||
and check{ ' ' }
|
||||
<b>Allow usage of WooCommerce to be tracked</b>.
|
||||
</p>
|
||||
) }
|
||||
<NewExperimentForm />
|
||||
<table className="experiments wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Experiment</th>
|
||||
<th>Variation</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ experiments.map( ( { name, variation }, index ) => {
|
||||
return (
|
||||
<tr key={ index }>
|
||||
<td className="experiment-name">{ name }</td>
|
||||
<td align="center">{ variation }</td>
|
||||
<td className="actions" align="center">
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleExperiment( name, variation );
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
deleteExperiment( name );
|
||||
} }
|
||||
className="btn btn-danger"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const { getExperiments } = select( STORE_KEY );
|
||||
const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
|
||||
|
||||
return {
|
||||
experiments: getExperiments(),
|
||||
isTrackingEnabled: getOption( 'woocommerce_allow_tracking' ),
|
||||
isResolving: isResolving( 'getOption', [ 'getExperiments' ] ),
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { toggleExperiment, deleteExperiment } = dispatch( STORE_KEY );
|
||||
|
||||
return {
|
||||
toggleExperiment,
|
||||
deleteExperiment,
|
||||
};
|
||||
} )
|
||||
)( Experiments );
|
|
@ -0,0 +1,7 @@
|
|||
const TYPES = {
|
||||
TOGGLE_FEATURE: 'TOGGLE_FEATURE',
|
||||
SET_FEATURES: 'SET_FEATURES',
|
||||
SET_MODIFIED_FEATURES: 'SET_MODIFIED_FEATURES',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
import { controls } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { API_NAMESPACE, STORE_KEY } from './constants';
|
||||
|
||||
export function* resetModifiedFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/features/reset`,
|
||||
method: 'POST',
|
||||
} );
|
||||
|
||||
yield setModifiedFeatures( [] );
|
||||
yield setFeatures( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* toggleFeature( featureName ) {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: API_NAMESPACE + '/features/' + featureName + '/toggle',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
} );
|
||||
yield setFeatures( response );
|
||||
yield controls.dispatch(
|
||||
STORE_KEY,
|
||||
'invalidateResolutionForStoreSelector',
|
||||
'getModifiedFeatures'
|
||||
);
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function setFeatures( features ) {
|
||||
return {
|
||||
type: TYPES.SET_FEATURES,
|
||||
features,
|
||||
};
|
||||
}
|
||||
|
||||
export function setModifiedFeatures( modifiedFeatures ) {
|
||||
return {
|
||||
type: TYPES.SET_MODIFIED_FEATURES,
|
||||
modifiedFeatures,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/features';
|
||||
export const OPTION_NAME_PREFIX = 'wc_admin_helper_feature_values';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import * as selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import { STORE_KEY } from './constants';
|
||||
|
||||
export default registerStore( STORE_KEY, {
|
||||
actions,
|
||||
selectors,
|
||||
resolvers,
|
||||
controls,
|
||||
reducer,
|
||||
} );
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
features: {},
|
||||
modifiedFeatures: [],
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.SET_MODIFIED_FEATURES:
|
||||
return {
|
||||
...state,
|
||||
modifiedFeatures: action.modifiedFeatures,
|
||||
};
|
||||
case TYPES.SET_FEATURES:
|
||||
return {
|
||||
...state,
|
||||
features: action.features,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { setFeatures, setModifiedFeatures } from './actions';
|
||||
import { API_NAMESPACE, OPTION_NAME_PREFIX } from './constants';
|
||||
|
||||
export function* getModifiedFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `wc-admin/options?options=` + OPTION_NAME_PREFIX,
|
||||
} );
|
||||
|
||||
yield setModifiedFeatures(
|
||||
response && response[ OPTION_NAME_PREFIX ]
|
||||
? Object.keys( response[ OPTION_NAME_PREFIX ] )
|
||||
: []
|
||||
);
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* getFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/features`,
|
||||
} );
|
||||
|
||||
yield setFeatures( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export function getFeatures( state ) {
|
||||
return state.features;
|
||||
}
|
||||
|
||||
export function getModifiedFeatures( state ) {
|
||||
return state.modifiedFeatures;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function Features() {
|
||||
const { features = {}, modifiedFeatures = [] } = useSelect( ( select ) => {
|
||||
const { getFeatures, getModifiedFeatures } = select( STORE_KEY );
|
||||
return {
|
||||
features: getFeatures(),
|
||||
modifiedFeatures: getModifiedFeatures(),
|
||||
};
|
||||
} );
|
||||
|
||||
const { toggleFeature, resetModifiedFeatures } = useDispatch( STORE_KEY );
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-features">
|
||||
<h2>
|
||||
Features
|
||||
<Button
|
||||
disabled={ modifiedFeatures.length === 0 }
|
||||
onClick={ () => resetModifiedFeatures() }
|
||||
isSecondary
|
||||
style={ { marginLeft: '24px' } }
|
||||
>
|
||||
Reset to defaults
|
||||
</Button>
|
||||
</h2>
|
||||
<table className="features wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Feature Name</th>
|
||||
<th>Enabled?</th>
|
||||
<th>Toggle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ Object.keys( features ).map( ( feature_name ) => {
|
||||
return (
|
||||
<tr key={ feature_name }>
|
||||
<td className="feature-name">
|
||||
{ feature_name }
|
||||
</td>
|
||||
<td>{ features[ feature_name ].toString() }</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleFeature( feature_name );
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Features;
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { App } from './app';
|
||||
import './index.scss';
|
||||
|
||||
const appRoot = document.getElementById(
|
||||
'woocommerce-admin-test-helper-app-root'
|
||||
);
|
||||
|
||||
if (appRoot) {
|
||||
render(<App />, appRoot);
|
||||
}
|