'; echo '

' . esc_html__( 'Eventbrite for The Events Calendar', 'eb4tec' ) . '

'; // Sync status bar. if ( $last_sync ) { $sync_time = human_time_diff( $last_sync ); echo '

'; printf( esc_html__( 'Last sync: %s ago', 'eb4tec' ), esc_html( $sync_time ) ); if ( is_array( $sync_result ) ) { printf( ' — ' . esc_html__( '%d pulled, %d pushed, %d errors.', 'eb4tec' ), (int) ( $sync_result['pulled'] ?? 0 ), (int) ( $sync_result['pushed'] ?? 0 ), (int) ( $sync_result['errors'] ?? 0 ) ); } echo '

'; } // Sync Now button. echo '
'; wp_nonce_field( 'eb4tec_sync_now', '_eb4tec_sync_nonce' ); echo ''; submit_button( __( 'Sync Now', 'eb4tec' ), 'secondary', 'submit', false ); echo '
'; // Tabs. $tabs = [ 'api' => __( 'API & Credentials', 'eb4tec' ), 'sync' => __( 'Sync Settings', 'eb4tec' ), 'woocommerce' => __( 'WooCommerce', 'eb4tec' ), ]; echo ''; echo '
'; wp_nonce_field( 'eb4tec_save_settings', '_eb4tec_settings_nonce' ); echo ''; echo ''; echo ''; match ( $active_tab ) { 'api' => $this->render_tab_api( $org_name ), 'sync' => $this->render_tab_sync(), 'woocommerce' => $this->render_tab_woocommerce(), default => $this->render_tab_api( $org_name ), }; echo ''; submit_button(); echo '
'; echo ''; } private function render_tab_api( string $org_name ): void { $token = get_option( 'eb4tec_api_token', '' ); $token_display = $token ? str_repeat( '•', 20 ) : ''; $org_id = esc_attr( get_option( 'eb4tec_org_id', '' ) ); ?>



__( 'Hourly', 'eb4tec' ), 'twicedaily' => __( 'Twice Daily', 'eb4tec' ), 'daily' => __( 'Daily', 'eb4tec' ), ]; $directions = [ 'both' => __( 'Both directions', 'eb4tec' ), 'eb_to_tec' => __( 'Eventbrite → WordPress only', 'eb4tec' ), 'tec_to_eb' => __( 'WordPress → Eventbrite only', 'eb4tec' ), ]; ?>

'product_cat', 'hide_empty' => false, ] ); ?>

'eb4tec-settings', 'tab' => $tab, 'updated' => '1', ], admin_url( 'edit.php?post_type=tribe_events' ) ); wp_safe_redirect( $redirect ); exit; } public function ajax_validate_token(): void { check_ajax_referer( 'eb4tec_admin_ajax', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'eb4tec' ) ] ); } $token = sanitize_text_field( $_POST['token'] ?? '' ); if ( empty( $token ) ) { wp_send_json_error( [ 'message' => __( 'No token provided.', 'eb4tec' ) ] ); } // Temporarily update the token so the API client picks it up. $old_token = get_option( 'eb4tec_api_token', '' ); update_option( 'eb4tec_api_token', $token, false ); $api = new EB4TEC_API_Client(); $result = $api->get_user_me(); if ( is_wp_error( $result ) ) { update_option( 'eb4tec_api_token', $old_token, false ); wp_send_json_error( [ 'message' => $result->get_error_message() ] ); } // Fetch organization ID. $orgs = isset( $result['organizations'] ) ? $result['organizations'] : []; if ( empty( $orgs ) ) { // Try getting org from user profile. $org_id = ''; $org_name = $result['name'] ?? ''; } else { $org = reset( $orgs ); $org_id = $org['id'] ?? ''; $org_name = $org['name'] ?? ''; } update_option( 'eb4tec_org_id', sanitize_text_field( $org_id ) ); update_option( 'eb4tec_org_name', sanitize_text_field( $org_name ) ); wp_send_json_success( [ 'message' => sprintf( __( 'Connected as %s (Org: %s)', 'eb4tec' ), esc_html( $result['name'] ?? '' ), esc_html( $org_name ) ), 'org_id' => $org_id, 'org_name' => $org_name, ] ); } public function ajax_register_webhook(): void { check_ajax_referer( 'eb4tec_admin_ajax', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'eb4tec' ) ] ); } $secret = get_option( 'eb4tec_webhook_secret', '' ); if ( empty( $secret ) ) { $secret = wp_generate_password( 32, false ); update_option( 'eb4tec_webhook_secret', $secret ); } $endpoint_url = add_query_arg( 'eb4tec_webhook', '1', home_url( '/' ) ); $api = new EB4TEC_API_Client(); $result = $api->create_webhook( [ 'webhook' => [ 'endpoint_url' => $endpoint_url, 'actions' => 'event.created,event.published,event.updated,event.unpublished,attendee.updated', ], ] ); if ( is_wp_error( $result ) ) { wp_send_json_error( [ 'message' => $result->get_error_message() ] ); } update_option( 'eb4tec_webhook_id', sanitize_text_field( $result['id'] ?? '' ) ); wp_send_json_success( [ 'message' => __( 'Webhook registered successfully.', 'eb4tec' ) ] ); } public function ajax_delete_webhook(): void { check_ajax_referer( 'eb4tec_admin_ajax', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'eb4tec' ) ] ); } $webhook_id = get_option( 'eb4tec_webhook_id', '' ); if ( empty( $webhook_id ) ) { wp_send_json_error( [ 'message' => __( 'No webhook ID stored.', 'eb4tec' ) ] ); } $api = new EB4TEC_API_Client(); $result = $api->delete_webhook( $webhook_id ); if ( is_wp_error( $result ) ) { wp_send_json_error( [ 'message' => $result->get_error_message() ] ); } delete_option( 'eb4tec_webhook_id' ); wp_send_json_success( [ 'message' => __( 'Webhook removed.', 'eb4tec' ) ] ); } public function admin_enqueue_scripts( string $hook ): void { $screen = get_current_screen(); if ( ! $screen || ! str_contains( $screen->id ?? '', 'eb4tec' ) ) { return; } $nonce = wp_create_nonce( 'eb4tec_admin_ajax' ); wp_add_inline_script( 'jquery', "(function($){ const nonce = '{$nonce}'; $('#eb4tec-validate-token').on('click', function(){ var token = $('#eb4tec_api_token').val(); $.post(ajaxurl, {action:'eb4tec_validate_token',nonce:nonce,token:token}, function(r){ var el = $('#eb4tec-validate-result'); if(r.success){ el.css('color','green').text(r.data.message); $('#eb4tec_org_id').val(r.data.org_id); } else { el.css('color','red').text(r.data.message); } }); }); $('#eb4tec-register-webhook').on('click', function(){ $.post(ajaxurl, {action:'eb4tec_register_webhook',nonce:nonce}, function(r){ var el = $('#eb4tec-webhook-result'); if(r.success){ el.css('color','green').text(r.data.message); } else { el.css('color','red').text(r.data.message); } }); }); $('#eb4tec-delete-webhook').on('click', function(){ $.post(ajaxurl, {action:'eb4tec_delete_webhook',nonce:nonce}, function(r){ var el = $('#eb4tec-webhook-result'); if(r.success){ el.css('color','green').text(r.data.message); } else { el.css('color','red').text(r.data.message); } }); }); })(jQuery);" ); } public function admin_notices(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $screen = get_current_screen(); if ( ! $screen || ! str_contains( $screen->id ?? '', 'eb4tec' ) ) { return; } if ( ! empty( $_GET['updated'] ) ) { echo '

' . esc_html__( 'Settings saved.', 'eb4tec' ) . '

'; } if ( get_transient( 'eb4tec_rate_limit_warning' ) ) { echo '

' . esc_html__( 'Eventbrite API rate limit is nearly exhausted. Sync operations may be delayed.', 'eb4tec' ) . '

'; } // Show "Sync Now" result. $user_id = get_current_user_id(); $sync_result = get_transient( "eb4tec_sync_now_result_{$user_id}" ); if ( is_array( $sync_result ) ) { delete_transient( "eb4tec_sync_now_result_{$user_id}" ); $msg = sprintf( __( 'Sync complete — %d pulled from Eventbrite, %d pushed to Eventbrite, %d errors.', 'eb4tec' ), (int) ( $sync_result['pulled'] ?? 0 ), (int) ( $sync_result['pushed'] ?? 0 ), (int) ( $sync_result['errors'] ?? 0 ) ); $class = ( ( $sync_result['errors'] ?? 0 ) > 0 ) ? 'notice-warning' : 'notice-success'; echo '

' . esc_html( $msg ) . '

'; } } }