Created
July 31, 2025 08:49
-
-
Save fishuke/47c312676afc936f6a0d52fb25eb83be to your computer and use it in GitHub Desktop.
Yith Waitlist Api
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Plugin Name: YITH Waitlist API | |
| * Description: Custom API to fetch YITH waitlist data via HTTP requests | |
| * Version: 1.0.0 | |
| */ | |
| // Prevent direct access | |
| if (!defined('ABSPATH')) { | |
| exit; | |
| } | |
| class YithWaitlistAPI { | |
| private $base_url; | |
| private $cookies = array(); | |
| private $api_key_option = 'yith_waitlist_api_key'; | |
| public function __construct() { | |
| $this->base_url = home_url(); | |
| // Add REST API endpoints | |
| add_action('rest_api_init', array($this, 'register_api_routes')); | |
| // Add admin menu for testing | |
| add_action('admin_menu', array($this, 'add_admin_menu')); | |
| // Generate API key on activation | |
| register_activation_hook(__FILE__, array($this, 'generate_api_key')); | |
| // Add settings page | |
| add_action('admin_init', array($this, 'register_settings')); | |
| } | |
| /** | |
| * Generate API key on plugin activation | |
| */ | |
| public function generate_api_key() { | |
| if (!get_option($this->api_key_option)) { | |
| $api_key = $this->generate_secure_api_key(); | |
| update_option($this->api_key_option, $api_key); | |
| } | |
| } | |
| /** | |
| * Generate a secure API key | |
| */ | |
| private function generate_secure_api_key() { | |
| return 'ywapi_' . bin2hex(random_bytes(32)); | |
| } | |
| /** | |
| * Register plugin settings | |
| */ | |
| public function register_settings() { | |
| register_setting('yith_waitlist_api_settings', $this->api_key_option); | |
| } | |
| /** | |
| * Register REST API routes | |
| */ | |
| public function register_api_routes() { | |
| register_rest_route('yith-waitlist/v1', '/waitlist', array( | |
| 'methods' => 'GET', | |
| 'callback' => array($this, 'get_waitlist_data'), | |
| 'permission_callback' => array($this, 'check_permissions'), | |
| 'args' => array( | |
| 'search' => array( | |
| 'description' => 'Search term for products', | |
| 'type' => 'string', | |
| 'default' => '' | |
| ), | |
| 'orderby' => array( | |
| 'description' => 'Order by field', | |
| 'type' => 'string', | |
| 'default' => 'users' | |
| ), | |
| 'order' => array( | |
| 'description' => 'Order direction', | |
| 'type' => 'string', | |
| 'default' => 'desc' | |
| ), | |
| 'paged' => array( | |
| 'description' => 'Page number', | |
| 'type' => 'integer', | |
| 'default' => 1 | |
| ) | |
| ) | |
| )); | |
| register_rest_route('yith-waitlist/v1', '/waitlist/(?P<list_id>\d+)', array( | |
| 'methods' => 'GET', | |
| 'callback' => array($this, 'get_specific_waitlist'), | |
| 'permission_callback' => array($this, 'check_permissions'), | |
| )); | |
| } | |
| /** | |
| * Check API permissions | |
| */ | |
| public function check_permissions($request) { | |
| // Check for API key first | |
| $api_key = $this->get_api_key_from_request($request); | |
| if ($api_key && $this->validate_api_key($api_key)) { | |
| return true; | |
| } | |
| // Fallback to WordPress user permissions | |
| return current_user_can('manage_options'); | |
| } | |
| /** | |
| * Get API key from request | |
| */ | |
| private function get_api_key_from_request($request) { | |
| // Check URL parameter | |
| $api_key = $request->get_param('api_key'); | |
| if ($api_key) { | |
| return $api_key; | |
| } | |
| // Check Authorization header | |
| $headers = $request->get_headers(); | |
| if (isset($headers['authorization'][0])) { | |
| $auth_header = $headers['authorization'][0]; | |
| if (preg_match('/Bearer\s+(.*)$/i', $auth_header, $matches)) { | |
| return $matches[1]; | |
| } | |
| } | |
| // Check X-API-Key header | |
| if (isset($headers['x_api_key'][0])) { | |
| return $headers['x_api_key'][0]; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Validate API key | |
| */ | |
| private function validate_api_key($provided_key) { | |
| $stored_key = get_option($this->api_key_option); | |
| return $stored_key && hash_equals($stored_key, $provided_key); | |
| } | |
| /** | |
| * Get waitlist data via HTTP request | |
| */ | |
| public function get_waitlist_data($request) { | |
| $search = $request->get_param('search'); | |
| $orderby = $request->get_param('orderby'); | |
| $order = $request->get_param('order'); | |
| $paged = $request->get_param('paged'); | |
| // Build the URL | |
| $url = $this->base_url . '/wp-admin/admin.php'; | |
| $query_params = array( | |
| 'page' => 'yith_wcwtl_panel', | |
| 'tab' => 'waitlistdata', | |
| 'orderby' => $orderby, | |
| 'order' => $order, | |
| 'paged' => $paged | |
| ); | |
| if (!empty($search)) { | |
| $query_params['s'] = $search; | |
| } | |
| $url .= '?' . http_build_query($query_params); | |
| // Make the request | |
| $response = $this->make_authenticated_request($url); | |
| if (is_wp_error($response)) { | |
| return new WP_Error('request_failed', $response->get_error_message(), array('status' => 500)); | |
| } | |
| // Parse the HTML response | |
| $parsed_data = $this->parse_waitlist_html($response['body']); | |
| return rest_ensure_response($parsed_data); | |
| } | |
| /** | |
| * Get specific waitlist data | |
| */ | |
| public function get_specific_waitlist($request) { | |
| $list_id = $request->get_param('list_id'); | |
| $url = $this->base_url . '/wp-admin/admin.php'; | |
| $query_params = array( | |
| 'page' => 'yith_wcwtl_panel', | |
| 'tab' => 'waitlistdata', | |
| 'list_id[]' => $list_id | |
| ); | |
| $url .= '?' . http_build_query($query_params); | |
| $response = $this->make_authenticated_request($url); | |
| if (is_wp_error($response)) { | |
| return new WP_Error('request_failed', $response->get_error_message(), array('status' => 500)); | |
| } | |
| $parsed_data = $this->parse_waitlist_html($response['body']); | |
| return rest_ensure_response($parsed_data); | |
| } | |
| /** | |
| * Make authenticated HTTP request | |
| */ | |
| private function make_authenticated_request($url) { | |
| // Get admin user for internal requests | |
| $admin_user = $this->get_admin_user(); | |
| if (!$admin_user) { | |
| return new WP_Error('no_admin_user', 'No admin user found for authentication'); | |
| } | |
| // Set user context | |
| wp_set_current_user($admin_user->ID); | |
| // Get cookies for this admin user | |
| $cookies = $this->get_admin_user_cookies($admin_user); | |
| $args = array( | |
| 'timeout' => 30, | |
| 'cookies' => $cookies, | |
| 'headers' => array( | |
| 'User-Agent' => 'Mozilla/5.0 (WordPress YITH API)', | |
| 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', | |
| 'Accept-Language' => 'en-US,en;q=0.9', | |
| 'Cache-Control' => 'no-cache', | |
| 'DNT' => '1', | |
| 'Pragma' => 'no-cache', | |
| 'Sec-Fetch-Dest' => 'document', | |
| 'Sec-Fetch-Mode' => 'navigate', | |
| 'Sec-Fetch-Site' => 'same-origin', | |
| 'Upgrade-Insecure-Requests' => '1' | |
| ) | |
| ); | |
| $response = wp_remote_get($url, $args); | |
| if (is_wp_error($response)) { | |
| return $response; | |
| } | |
| $status_code = wp_remote_retrieve_response_code($response); | |
| if ($status_code !== 200) { | |
| return new WP_Error('http_error', 'HTTP Error: ' . $status_code, array('status' => $status_code)); | |
| } | |
| return array( | |
| 'body' => wp_remote_retrieve_body($response), | |
| 'headers' => wp_remote_retrieve_headers($response), | |
| 'status' => $status_code | |
| ); | |
| } | |
| /** | |
| * Get an admin user for authentication | |
| */ | |
| private function get_admin_user() { | |
| $admin_users = get_users(array( | |
| 'role' => 'administrator', | |
| 'number' => 1, | |
| 'orderby' => 'ID', | |
| 'order' => 'ASC' | |
| )); | |
| return !empty($admin_users) ? $admin_users[0] : null; | |
| } | |
| /** | |
| * Get authentication cookies for admin user | |
| */ | |
| private function get_admin_user_cookies($user) { | |
| $cookies = array(); | |
| // Generate WordPress auth cookies | |
| $expiration = time() + (14 * DAY_IN_SECONDS); | |
| $auth_cookie = wp_generate_auth_cookie($user->ID, $expiration, 'logged_in'); | |
| $secure_cookie = wp_generate_auth_cookie($user->ID, $expiration, 'secure_auth'); | |
| $cookies[] = new WP_Http_Cookie(array( | |
| 'name' => LOGGED_IN_COOKIE, | |
| 'value' => $auth_cookie | |
| )); | |
| if (is_ssl()) { | |
| $cookies[] = new WP_Http_Cookie(array( | |
| 'name' => SECURE_AUTH_COOKIE, | |
| 'value' => $secure_cookie | |
| )); | |
| } | |
| return $cookies; | |
| } | |
| /** | |
| * Parse HTML response to extract waitlist data | |
| */ | |
| private function parse_waitlist_html($html) { | |
| $data = array( | |
| 'waitlists' => array(), | |
| 'total_items' => 0 | |
| ); | |
| // Use DOMDocument to parse HTML | |
| $dom = new DOMDocument(); | |
| libxml_use_internal_errors(true); // Suppress HTML parsing warnings | |
| @$dom->loadHTML('<?xml encoding="utf-8" ?>' . $html); | |
| libxml_clear_errors(); | |
| $xpath = new DOMXPath($dom); | |
| // Find waitlist table rows in tbody | |
| $rows = $xpath->query('//tbody[@id="the-list"]//tr'); | |
| foreach ($rows as $row) { | |
| $waitlist_item = array(); | |
| // Extract list ID from checkbox | |
| $checkbox = $xpath->query('.//input[@type="checkbox"][@name="list_id[]"]', $row); | |
| if ($checkbox->length > 0) { | |
| $waitlist_item['list_id'] = $checkbox->item(0)->getAttribute('value'); | |
| } | |
| // Extract product name and ID | |
| $product_cell = $xpath->query('.//td[contains(@class, "column-product")]', $row); | |
| if ($product_cell->length > 0) { | |
| $product_link = $xpath->query('.//a[contains(@href, "post.php")]', $product_cell->item(0)); | |
| if ($product_link->length > 0) { | |
| $waitlist_item['product'] = trim($product_link->item(0)->textContent); | |
| // Extract product ID from edit URL | |
| $edit_url = $product_link->item(0)->getAttribute('href'); | |
| if (preg_match('/post=(\d+)/', $edit_url, $matches)) { | |
| $waitlist_item['product_id'] = intval($matches[1]); | |
| } | |
| } | |
| } | |
| // Extract variation | |
| $variation_cell = $xpath->query('.//td[contains(@class, "column-variation")]', $row); | |
| if ($variation_cell->length > 0) { | |
| $waitlist_item['variation'] = trim($variation_cell->item(0)->textContent); | |
| } | |
| // Extract users count | |
| $users_cell = $xpath->query('.//td[contains(@class, "column-users")]', $row); | |
| if ($users_cell->length > 0) { | |
| $users_link = $xpath->query('.//a', $users_cell->item(0)); | |
| if ($users_link->length > 0) { | |
| $waitlist_item['users'] = intval(trim($users_link->item(0)->textContent)); | |
| } | |
| } | |
| // Only add if we have essential data | |
| if (!empty($waitlist_item['product']) && isset($waitlist_item['users'])) { | |
| $data['waitlists'][] = $waitlist_item; | |
| } | |
| } | |
| // Extract total items count | |
| $total_items_text = $xpath->query('//span[contains(@class, "displaying-num")]'); | |
| if ($total_items_text->length > 0) { | |
| preg_match('/(\d+)/', $total_items_text->item(0)->textContent, $matches); | |
| if (isset($matches[1])) { | |
| $data['total_items'] = intval($matches[1]); | |
| } | |
| } | |
| return $data; | |
| } | |
| /** | |
| * Add admin menu for testing | |
| */ | |
| public function add_admin_menu() { | |
| add_management_page( | |
| 'YITH Waitlist API', | |
| 'Waitlist API', | |
| 'manage_options', | |
| 'yith-waitlist-api', | |
| array($this, 'admin_page') | |
| ); | |
| } | |
| /** | |
| * Main admin page | |
| */ | |
| public function admin_page() { | |
| $current_tab = $_GET['tab'] ?? 'settings'; | |
| ?> | |
| <div class="wrap"> | |
| <h1>YITH Waitlist API</h1> | |
| <nav class="nav-tab-wrapper"> | |
| <a href="?page=yith-waitlist-api&tab=settings" class="nav-tab <?php echo $current_tab === 'settings' ? 'nav-tab-active' : ''; ?>">Settings</a> | |
| <a href="?page=yith-waitlist-api&tab=test" class="nav-tab <?php echo $current_tab === 'test' ? 'nav-tab-active' : ''; ?>">Test API</a> | |
| <a href="?page=yith-waitlist-api&tab=docs" class="nav-tab <?php echo $current_tab === 'docs' ? 'nav-tab-active' : ''; ?>">Documentation</a> | |
| </nav> | |
| <?php | |
| switch ($current_tab) { | |
| case 'settings': | |
| $this->render_settings_tab(); | |
| break; | |
| case 'test': | |
| $this->render_test_tab(); | |
| break; | |
| case 'docs': | |
| $this->render_docs_tab(); | |
| break; | |
| } | |
| ?> | |
| </div> | |
| <?php | |
| } | |
| /** | |
| * Render settings tab | |
| */ | |
| private function render_settings_tab() { | |
| $api_key = get_option($this->api_key_option); | |
| if (isset($_POST['regenerate_key']) && wp_verify_nonce($_POST['_wpnonce'], 'regenerate_api_key')) { | |
| $api_key = $this->generate_secure_api_key(); | |
| update_option($this->api_key_option, $api_key); | |
| echo '<div class="notice notice-success"><p>API key regenerated successfully!</p></div>'; | |
| } | |
| ?> | |
| <div class="card"> | |
| <h2>API Key Settings</h2> | |
| <table class="form-table"> | |
| <tr> | |
| <th scope="row">API Key</th> | |
| <td> | |
| <input type="text" value="<?php echo esc_attr($api_key); ?>" class="large-text code" readonly onclick="this.select()" /> | |
| <p class="description">Use this API key to authenticate external requests. Click to select and copy.</p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th scope="row">Regenerate Key</th> | |
| <td> | |
| <form method="post" style="display: inline;"> | |
| <?php wp_nonce_field('regenerate_api_key'); ?> | |
| <input type="submit" name="regenerate_key" class="button button-secondary" value="Generate New API Key" | |
| onclick="return confirm('This will invalidate the current API key. Are you sure?')" /> | |
| </form> | |
| <p class="description">Generate a new API key. This will invalidate the current key.</p> | |
| </td> | |
| </tr> | |
| </table> | |
| </div> | |
| <div class="card"> | |
| <h2>Security Notes</h2> | |
| <ul> | |
| <li>Keep your API key secure and don't share it publicly</li> | |
| <li>The API key provides full admin access to your waitlist data</li> | |
| <li>You can regenerate the key at any time if it's compromised</li> | |
| <li>API requests are logged in your server access logs</li> | |
| </ul> | |
| </div> | |
| <?php | |
| } | |
| /** | |
| * Render test tab | |
| */ | |
| private function render_test_tab() { | |
| ?> | |
| <div class="card"> | |
| <h2>Test API Call</h2> | |
| <form method="post" action=""> | |
| <?php wp_nonce_field('test_api_call'); ?> | |
| <table class="form-table"> | |
| <tr> | |
| <th scope="row">Search Term</th> | |
| <td><input type="text" name="search" value="<?php echo esc_attr($_POST['search'] ?? ''); ?>" class="regular-text" /></td> | |
| </tr> | |
| <tr> | |
| <th scope="row">Order By</th> | |
| <td> | |
| <select name="orderby"> | |
| <option value="users" <?php selected($_POST['orderby'] ?? 'users', 'users'); ?>>Users</option> | |
| <option value="date" <?php selected($_POST['orderby'] ?? 'users', 'date'); ?>>Date</option> | |
| <option value="product" <?php selected($_POST['orderby'] ?? 'users', 'product'); ?>>Product</option> | |
| </select> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th scope="row">Order</th> | |
| <td> | |
| <select name="order"> | |
| <option value="desc" <?php selected($_POST['order'] ?? 'desc', 'desc'); ?>>Descending</option> | |
| <option value="asc" <?php selected($_POST['order'] ?? 'desc', 'asc'); ?>>Ascending</option> | |
| </select> | |
| </td> | |
| </tr> | |
| </table> | |
| <p class="submit"> | |
| <input type="submit" name="test_api" class="button-primary" value="Test API Call" /> | |
| </p> | |
| </form> | |
| <?php | |
| if (isset($_POST['test_api']) && wp_verify_nonce($_POST['_wpnonce'], 'test_api_call')) { | |
| $request = new WP_REST_Request('GET', '/yith-waitlist/v1/waitlist'); | |
| $request->set_param('search', $_POST['search'] ?? ''); | |
| $request->set_param('orderby', $_POST['orderby'] ?? 'users'); | |
| $request->set_param('order', $_POST['order'] ?? 'desc'); | |
| $response = $this->get_waitlist_data($request); | |
| echo '<h3>API Response:</h3>'; | |
| echo '<pre style="background: #f1f1f1; padding: 15px; overflow: auto; max-height: 400px; border: 1px solid #ddd; border-radius: 4px;">'; | |
| echo esc_html(json_encode($response->get_data(), JSON_PRETTY_PRINT)); | |
| echo '</pre>'; | |
| } | |
| ?> | |
| </div> | |
| <?php | |
| } | |
| /** | |
| * Render documentation tab | |
| */ | |
| private function render_docs_tab() { | |
| $api_key = get_option($this->api_key_option); | |
| $base_url = home_url('/wp-json/yith-waitlist/v1'); | |
| ?> | |
| <div class="card"> | |
| <h2>API Endpoints</h2> | |
| <table class="wp-list-table widefat fixed striped"> | |
| <thead> | |
| <tr> | |
| <th>Endpoint</th> | |
| <th>Method</th> | |
| <th>Description</th> | |
| <th>Parameters</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>/waitlist</code></td> | |
| <td>GET</td> | |
| <td>Get all waitlists</td> | |
| <td>search, orderby, order, paged</td> | |
| </tr> | |
| <tr> | |
| <td><code>/waitlist/{id}</code></td> | |
| <td>GET</td> | |
| <td>Get specific waitlist</td> | |
| <td>-</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="card"> | |
| <h2>Authentication Methods</h2> | |
| <h3>Method 1: URL Parameter</h3> | |
| <pre><code><?php echo esc_html($base_url); ?>/waitlist?api_key=<?php echo esc_html($api_key); ?></code></pre> | |
| <h3>Method 2: Authorization Header</h3> | |
| <pre><code>Authorization: Bearer <?php echo esc_html($api_key); ?></code></pre> | |
| <h3>Method 3: X-API-Key Header</h3> | |
| <pre><code>X-API-Key: <?php echo esc_html($api_key); ?></code></pre> | |
| </div> | |
| <div class="card"> | |
| <h2>Usage Examples</h2> | |
| <h3>cURL</h3> | |
| <pre><code># Using URL parameter | |
| curl "<?php echo esc_html($base_url); ?>/waitlist?api_key=<?php echo esc_html($api_key); ?>" | |
| # Using Authorization header | |
| curl -H "Authorization: Bearer <?php echo esc_html($api_key); ?>" \ | |
| "<?php echo esc_html($base_url); ?>/waitlist" | |
| # Search with parameters | |
| curl -H "X-API-Key: <?php echo esc_html($api_key); ?>" \ | |
| "<?php echo esc_html($base_url); ?>/waitlist?search=STONE+BLOUSE&orderby=users&order=desc"</code></pre> | |
| <h3>JavaScript/Fetch</h3> | |
| <pre><code>// Using Authorization header | |
| fetch('<?php echo esc_html($base_url); ?>/waitlist', { | |
| headers: { | |
| 'Authorization': 'Bearer <?php echo esc_html($api_key); ?>' | |
| } | |
| }) | |
| .then(response => response.json()) | |
| .then(data => console.log(data)); | |
| // Using X-API-Key header | |
| fetch('<?php echo esc_html($base_url); ?>/waitlist?search=STONE+BLOUSE', { | |
| headers: { | |
| 'X-API-Key': '<?php echo esc_html($api_key); ?>' | |
| } | |
| }) | |
| .then(response => response.json()) | |
| .then(data => console.log(data.waitlists));</code></pre> | |
| <h3>PHP</h3> | |
| <pre><code>// Using WordPress HTTP API | |
| $response = wp_remote_get('<?php echo esc_html($base_url); ?>/waitlist', array( | |
| 'headers' => array( | |
| 'X-API-Key' => '<?php echo esc_html($api_key); ?>' | |
| ) | |
| )); | |
| $data = json_decode(wp_remote_retrieve_body($response), true); | |
| print_r($data['waitlists']);</code></pre> | |
| </div> | |
| <div class="card"> | |
| <h2>Response Format</h2> | |
| <pre><code>{ | |
| "waitlists": [ | |
| { | |
| "id": "1674", | |
| "product_name": "STONE BLOUSE BLAUW 277", | |
| "users_count": 25, | |
| "created_date": "2024-01-15", | |
| "actions": [ | |
| { | |
| "text": "View", | |
| "url": "/wp-admin/admin.php?page=yith_wcwtl_panel&tab=waitlistdata&list_id=1674" | |
| } | |
| ] | |
| } | |
| ], | |
| "pagination": [ | |
| { | |
| "text": "1", | |
| "url": "/wp-admin/admin.php?page=yith_wcwtl_panel&tab=waitlistdata&paged=1" | |
| } | |
| ], | |
| "total_items": 150 | |
| }</code></pre> | |
| </div> | |
| <style> | |
| .nav-tab-wrapper { | |
| margin-bottom: 20px; | |
| } | |
| .card { | |
| margin-bottom: 20px; | |
| } | |
| .card h3 { | |
| margin-top: 20px; | |
| margin-bottom: 10px; | |
| } | |
| .card pre { | |
| background: #f8f9fa; | |
| border: 1px solid #e1e5e9; | |
| border-radius: 6px; | |
| padding: 16px; | |
| overflow-x: auto; | |
| font-size: 13px; | |
| } | |
| .card code { | |
| background: #f1f3f4; | |
| padding: 2px 4px; | |
| border-radius: 3px; | |
| font-family: 'Courier New', monospace; | |
| } | |
| .large-text.code { | |
| font-family: 'Courier New', monospace; | |
| font-size: 13px; | |
| } | |
| </style> | |
| <?php | |
| } | |
| } | |
| // Initialize the plugin | |
| new YithWaitlistAPI(); | |
| /** | |
| * Helper function to get waitlist data programmatically | |
| */ | |
| function get_yith_waitlist_data($args = array()) { | |
| $api = new YithWaitlistAPI(); | |
| $request = new WP_REST_Request('GET', '/yith-waitlist/v1/waitlist'); | |
| foreach ($args as $key => $value) { | |
| $request->set_param($key, $value); | |
| } | |
| $response = $api->get_waitlist_data($request); | |
| if (is_wp_error($response)) { | |
| return false; | |
| } | |
| return $response->get_data(); | |
| } | |
| ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment