api = $api; $this->automation = $automation; $this->logger = $logger; add_action( 'admin_menu', array( $this, 'menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'assets' ) ); add_action( 'wp_ajax_authkey_action', array( $this, 'ajax' ) ); add_filter( 'plugin_action_links_' . plugin_basename( AUTHKEY_FILE ), array( $this, 'settings_link' ) ); } /** Return one scalar POST value with WordPress's request slashes removed. */ private function post_string( string $key, string $default = '' ) : string { if ( ! isset( $_POST[ $key ] ) || ! is_scalar( $_POST[ $key ] ) ) { return $default; } return wp_unslash( (string) $_POST[ $key ] ); } /** Return one scalar GET value with WordPress's request slashes removed. */ private function get_string( string $key, string $default = '' ) : string { if ( ! isset( $_GET[ $key ] ) || ! is_scalar( $_GET[ $key ] ) ) { return $default; } return wp_unslash( (string) $_GET[ $key ] ); } /** Read a bounded, de-duplicated list of selected WooCommerce order IDs. */ private function posted_order_ids() : array { if ( ! isset( $_POST['order_ids'] ) || ! is_array( $_POST['order_ids'] ) ) { return array(); } $order_ids = array_filter( wp_unslash( $_POST['order_ids'] ), 'is_scalar' ); $order_ids = array_map( 'absint', $order_ids ); $order_ids = array_filter( $order_ids ); return array_slice( array_values( array_unique( $order_ids ) ), 0, 50 ); } public function settings_link( array $links ) : array { array_unshift( $links, '' . esc_html__( 'Settings', 'authkey' ) . '' ); return $links; } public function menu() : void { $cap = 'manage_options'; add_menu_page( __( 'Authkey', 'authkey' ), __( 'Authkey', 'authkey' ), $cap, 'authkey', array( $this, 'page' ), 'dashicons-whatsapp', 56 ); foreach ( array( 'authkey' => __( 'Dashboard', 'authkey' ), 'authkey-sync-data' => __( 'Sync Data', 'authkey' ), 'authkey-automations' => __( 'WooCommerce Automation', 'authkey' ), 'authkey-templates' => __( 'Templates', 'authkey' ), 'authkey-settings' => __( 'Settings', 'authkey' ), 'authkey-logs' => __( 'Logs', 'authkey' ), 'authkey-help' => __( 'Help', 'authkey' ) ) as $slug => $title ) { add_submenu_page( 'authkey', $title, $title, $cap, $slug, array( $this, 'page' ) ); } } public function assets( string $hook ) : void { if ( false === strpos( $hook, 'authkey' ) ) { return; } wp_enqueue_style( 'authkey-admin', AUTHKEY_URL . 'assets/css/admin.css', array(), AUTHKEY_VERSION ); wp_enqueue_script( 'authkey-admin', AUTHKEY_URL . 'assets/js/admin.js', array( 'jquery' ), AUTHKEY_VERSION, true ); wp_localize_script( 'authkey-admin', 'authkeyAdmin', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'authkey_admin' ), 'strings' => array( 'confirm' => __( 'Are you sure?', 'authkey' ), 'connected' => __( 'Connected.', 'authkey' ), 'template_variable' => __( 'Template variable', 'authkey' ), 'map_template_variables' => __( 'Map template variables', 'authkey' ), 'map_template_values' => __( 'Choose the WooCommerce value to send for each variable.', 'authkey' ), 'no_template_variables' => __( 'This template has no variables to map.', 'authkey' ), 'mapping_choices' => array( 'customer_name' => __( 'Customer name', 'authkey' ), 'billing_first_name' => __( 'Customer first name', 'authkey' ), 'billing_last_name' => __( 'Customer last name', 'authkey' ), 'order_id' => __( 'Order ID', 'authkey' ), 'order_number' => __( 'Order number', 'authkey' ), 'product_names' => __( 'Product name(s)', 'authkey' ), 'order_total' => __( 'Order amount', 'authkey' ), 'currency' => __( 'Order currency', 'authkey' ), 'order_status' => __( 'Order status', 'authkey' ), 'billing_phone' => __( 'Billing phone', 'authkey' ), 'billing_email' => __( 'Billing email', 'authkey' ), 'store_name' => __( 'Store name', 'authkey' ), 'store_url' => __( 'Store URL', 'authkey' ) ) ) ) ); } public function ajax() : void { check_ajax_referer( 'authkey_admin', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'authkey' ) ), 403 ); } $action = sanitize_key( $this->post_string( 'task' ) ); if ( 'login' === $action ) { $result = $this->api->login( sanitize_text_field( $this->post_string( 'username' ) ), $this->post_string( 'password' ) ); $this->json( $result ); } if ( 'sync_templates' === $action ) { $this->json( $this->api->templates( true ) ); } if ( 'disconnect' === $action ) { Settings::disconnect(); wp_send_json_success(); } if ( 'save_automation' === $action ) { $this->save_automation(); } if ( 'delete_automation' === $action ) { $automation_id = absint( $this->post_string( 'id' ) ); if ( ! $automation_id ) { wp_send_json_error( array( 'message' => __( 'A valid automation is required.', 'authkey' ) ), 400 ); } global $wpdb; $wpdb->delete( Database::table( 'automations' ), array( 'id' => $automation_id ), array( '%d' ) ); wp_send_json_success(); } if ( 'toggle_automation' === $action ) { $automation_id = absint( $this->post_string( 'id' ) ); if ( ! $automation_id ) { wp_send_json_error( array( 'message' => __( 'A valid automation is required.', 'authkey' ) ), 400 ); } global $wpdb; $wpdb->update( Database::table( 'automations' ), array( 'enabled' => '1' === $this->post_string( 'enabled' ) ? 1 : 0 ), array( 'id' => $automation_id ), array( '%d' ), array( '%d' ) ); wp_send_json_success(); } wp_send_json_error( array( 'message' => __( 'Unknown action.', 'authkey' ) ), 400 ); } private function json( array|\WP_Error $result ) : void { is_wp_error( $result ) ? wp_send_json_error( array( 'message' => $result->get_error_message() ), 400 ) : wp_send_json_success( $result ); } private function save_automation() : void { $raw_mappings = $this->post_string( 'mappings', '{}' ); $mappings = json_decode( $raw_mappings, true ); if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $mappings ) ) { wp_send_json_error( array( 'message' => __( 'Template mappings must contain valid JSON.', 'authkey' ) ), 400 ); } $sanitized_mappings = Automation::sanitize_mappings( $mappings ); if ( count( $sanitized_mappings ) !== count( $mappings ) ) { wp_send_json_error( array( 'message' => __( 'One or more template mappings are invalid.', 'authkey' ) ), 400 ); } $trigger = sanitize_key( $this->post_string( 'trigger_name' ) ); if ( ! array_key_exists( $trigger, $this->triggers() ) ) { wp_send_json_error( array( 'message' => __( 'Choose a valid WooCommerce trigger.', 'authkey' ) ), 400 ); } $data = array( 'name' => sanitize_text_field( $this->post_string( 'name' ) ), 'enabled' => '1' === $this->post_string( 'enabled' ) ? 1 : 0, 'trigger_name' => $trigger, 'template' => sanitize_text_field( $this->post_string( 'template' ) ), 'template_id' => absint( $this->post_string( 'template_id' ) ), 'recipient_type' => 'billing', 'recipient_value' => '', 'mappings' => wp_json_encode( $sanitized_mappings ), 'conditions' => wp_json_encode( array() ), 'delay_minutes' => 0, 'updated_at' => current_time( 'mysql', true ), ); if ( empty( $data['name'] ) || empty( $data['template'] ) || empty( $data['template_id'] ) ) { wp_send_json_error( array( 'message' => __( 'Automation name and a WhatsApp template are required.', 'authkey' ) ), 400 ); } $id = absint( $this->post_string( 'id' ) ); global $wpdb; $saved = $id ? $wpdb->update( Database::table( 'automations' ), $data, array( 'id' => $id ) ) : false; if ( ! $id ) { $data['created_at'] = current_time( 'mysql', true ); $saved = $wpdb->insert( Database::table( 'automations' ), $data ); } if ( false === $saved ) { wp_send_json_error( array( 'message' => __( 'Could not save this automation. Please refresh this page to apply the database update and try again.', 'authkey' ) ), 500 ); } wp_send_json_success( array( 'id' => $id ?: $wpdb->insert_id ) ); } public function page() : void { if ( ! current_user_can( 'manage_options' ) ) { return; } $page = sanitize_key( $this->get_string( 'page', 'authkey' ) ); echo '
' . esc_html__( 'Monitor your Authkey connection, balance, and WooCommerce automations.', 'authkey' ) . '
' . esc_html( Settings::connected() ? __( 'Connected', 'authkey' ) : __( 'Not connected', 'authkey' ) ) . '
' . esc_html( $c['user_name'] ) . '
' . esc_html( $c['balance'] . ' ' . $c['currency'] ) . '
' . esc_html( (string) $count ) . '
' . esc_html__( 'Latest delivery activity from your automations.', 'authkey' ) . '
' . esc_html__( 'Configure the plugin and connect your Authkey account.', 'authkey' ) . '
' . esc_html__( 'Settings saved.', 'authkey' ) . '
' . esc_html__( 'Sign in to connect your Authkey account.', 'authkey' ) . '
' . esc_html__( 'Review the WhatsApp templates available in your Authkey account.', 'authkey' ) . '
' . esc_html( $templates->get_error_message() ) . '
| ' . esc_html__( 'Template Name', 'authkey' ) . ' | ' . esc_html__( 'Category', 'authkey' ) . ' | ' . esc_html__( 'Language', 'authkey' ) . ' | ' . esc_html__( 'Status', 'authkey' ) . ' | ' . esc_html__( 'Variables', 'authkey' ) . ' | ' . esc_html__( 'Preview', 'authkey' ) . ' |
|---|---|---|---|---|---|
| ' . esc_html( $template['temp_name'] ?? $template['name'] ?? $template['template_name'] ?? '' ) . ' | ' . esc_html( $template['temp_category'] ?? $template['category'] ?? '' ) . ' | ' . esc_html( $template['temp_language'] ?? $template['language'] ?? '' ) . ' | ' . esc_html( $status ) . ' | ' . esc_html( implode( ', ', $matches[0] ?? array() ) ) . ' | ' . esc_html( wp_trim_words( $body, 15 ) ) . ' |
' . esc_html__( 'Add WooCommerce billing contacts to an Authkey contact list for campaigns.', 'authkey' ) . '
' . esc_html__( 'WooCommerce must be active to sync order contacts.', 'authkey' ) . '
' . esc_html__( 'Enter a contact list name and select at least one order.', 'authkey' ) . '
' . esc_html( sprintf( __( '%1$d contact(s) synced to "%2$s". %3$d failed.', 'authkey' ), $synced, $list_name, $failed ) ) . '
' . esc_html__( 'Send the right WhatsApp template when an order or customer event occurs.', 'authkey' ) . '
' . esc_html__( 'Connect Authkey and sync templates before creating an automation.', 'authkey' ) . '
| ' . esc_html__( 'Name', 'authkey' ) . ' | ' . esc_html__( 'Trigger', 'authkey' ) . ' | ' . esc_html__( 'Template', 'authkey' ) . ' | ' . esc_html__( 'Status', 'authkey' ) . ' | ' . esc_html__( 'Actions', 'authkey' ) . ' |
|---|---|---|---|---|
| ' . esc_html( $rule->name ) . ' | ' . esc_html( $this->triggers()[ $rule->trigger_name ] ?? $rule->trigger_name ) . ' | ' . esc_html( $rule->template ) . ' | ' . esc_html( $rule->enabled ? __( 'Enabled', 'authkey' ) : __( 'Disabled', 'authkey' ) ) . ' |
' . esc_html__( 'Review recent Authkey delivery activity.', 'authkey' ) . '
| ' . esc_html__( 'Date', 'authkey' ) . ' | ' . esc_html__( 'Order', 'authkey' ) . ' | ' . esc_html__( 'Template', 'authkey' ) . ' | ' . esc_html__( 'Trigger', 'authkey' ) . ' | ' . esc_html__( 'Status', 'authkey' ) . ' |
|---|---|---|---|---|
| ' . esc_html( $log->created_at ) . ' | ' . esc_html( $log->order_id ) . ' | ' . esc_html( $log->template ) . ' | ' . esc_html( $log->trigger_name ) . ' | ' . esc_html( $log->status ) . ' |
' . esc_html__( 'Find documentation and support for the Authkey WooCommerce plugin.', 'authkey' ) . '
' . esc_html__( 'Documentation and website', 'authkey' ) . '
' . esc_html__( 'Support', 'authkey' ) . '
' . esc_html( sprintf( __( 'Version %s', 'authkey' ), AUTHKEY_VERSION ) ) . '