/** * Handle AJAX request for looking up book data (e.g., by ISBN, title, author). * Tries Google Books first, then Open Library as a fallback. */ public function handle_lookup_book() { // Ensure user is logged in (as per your existing check) if (!is_user_logged_in()) { wp_send_json_error(['message' => __('You must be logged in to use lookup.', 'custom-book-library')]); } // Security check: Verify nonce for the lookup action // IMPORTANT: CBL_NONCE_BOOK_LOOKUP needs to be defined in your main plugin file if ( ! defined('CBL_NONCE_BOOK_LOOKUP') ) { $this->cbl_log_error('CBL_NONCE_BOOK_LOOKUP is not defined.'); wp_send_json_error(['message' => __('Security nonce missing (define CBL_NONCE_BOOK_LOOKUP).', 'custom-book-library')]); } check_ajax_referer(CBL_NONCE_BOOK_LOOKUP, 'nonce'); // 'nonce' should be the key sent by JS for this lookup. $query = isset($_POST['query']) ? sanitize_text_field(wp_unslash($_POST['query'])) : ''; if (empty($query)) { wp_send_json_error(['message' => __('Lookup query cannot be empty.', 'custom-book-library')]); } $book_data = array( 'title' => '', 'author' => '', 'isbn' => '', // Will try to return ISBN-13 if available 'publisher' => '', 'publication_year'=> '', 'pages' => '', 'cover_image_url' => '', 'notes' => '', // Description/Synopsis 'genre' => '', // Categories/Subjects 'format' => '', // e.g., "Paperback", "Hardcover" 'series_name' => '', // Generic APIs rarely provide this cleanly 'series_number' => '', // Generic APIs rarely provide this cleanly 'goodreads_book_id' => '', 'tags' => '', ); // --- Attempt Google Books API Search --- $google_books_url = ''; // Check if query is a valid ISBN (10 or 13 digits, with or without hyphens) if ( preg_match( '/^(?:ISBN(?:-13)?:?|(?=[0-9]{13}$))((?:97[89])?[0-9]{9}[0-9X])$/i', str_replace( '-', '', $query ), $matches ) ) { $isbn_clean = $matches[1]; // Clean ISBN without hyphens or "ISBN:" prefix $google_books_url = 'https://www.googleapis.com/books/v1/volumes?q=isbn:' . $isbn_clean; } else { // General search for title or author $google_books_url = 'https://www.googleapis.com/books/v1/volumes?q=' . urlencode( $query ); } // You can append your Google API Key here if you have one: // $google_books_url .= '&key=YOUR_GOOGLE_API_KEY'; $google_response = wp_remote_get( $google_books_url, ['timeout' => 15] ); // Add timeout for robustness if ( ! is_wp_error( $google_response ) && wp_remote_retrieve_response_code( $google_response ) === 200 ) { $google_body = wp_remote_retrieve_body( $google_response ); $google_data = json_decode( $google_body, true ); if ( isset( $google_data['totalItems'] ) && $google_data['totalItems'] > 0 && ! empty( $google_data['items'][0]['volumeInfo'] ) ) { $volume_info = $google_data['items'][0]['volumeInfo']; // Populate from Google Books $book_data['title'] = isset( $volume_info['title'] ) ? sanitize_text_field( $volume_info['title'] ) : ''; $book_data['author'] = isset( $volume_info['authors'] ) ? sanitize_text_field( implode( ', ', $volume_info['authors'] ) ) : ''; // Get ISBN-13 if available, otherwise ISBN-10 if ( isset( $volume_info['industryIdentifiers'] ) ) { foreach ( $volume_info['industryIdentifiers'] as $identifier ) { if ( $identifier['type'] === 'ISBN_13' ) { $book_data['isbn'] = sanitize_text_field( $identifier['identifier'] ); break; // Found ISBN-13, no need to look further } elseif ( $identifier['type'] === 'ISBN_10' && empty($book_data['isbn']) ) { $book_data['isbn'] = sanitize_text_field( $identifier['identifier'] ); } } } $book_data['publisher'] = isset( $volume_info['publisher'] ) ? sanitize_text_field( $volume_info['publisher'] ) : ''; $book_data['publication_year'] = isset( $volume_info['publishedDate'] ) ? substr( sanitize_text_field( $volume_info['publishedDate'] ), 0, 4 ) : ''; $book_data['pages'] = isset( $volume_info['pageCount'] ) ? absint( $volume_info['pageCount'] ) : ''; $book_data['cover_image_url'] = isset( $volume_info['imageLinks']['thumbnail'] ) ? esc_url_raw( $volume_info['imageLinks']['thumbnail'] ) : ''; $book_data['notes'] = isset( $volume_info['description'] ) ? sanitize_textarea_field( $volume_info['description'] ) : ''; $book_data['genre'] = isset( $volume_info['categories'] ) ? sanitize_text_field( implode( ', ', $volume_info['categories'] ) ) : ''; $book_data['format'] = isset( $volume_info['printType'] ) ? sanitize_text_field( $volume_info['printType'] ) : ''; // Google Books generally doesn't provide structured series_name or series_number // If we found a title from Google, assume it's a good result and return if ( ! empty( $book_data['title'] ) ) { wp_send_json_success( array( 'message' => esc_html__( 'Book details found via Google Books!', 'custom-book-library' ), 'book_data' => $book_data ) ); exit; // Exit here if Google Books found something } } } else { $this->cbl_log_error('Google Books API Error: ' . (is_wp_error($google_response) ? $google_response->get_error_message() : wp_remote_retrieve_response_code($google_response))); } // --- If Google Books failed or found no useful data, attempt Open Library API Search --- $open_library_url = ''; $is_isbn_query = false; // Check if query is a valid ISBN for Open Library specific endpoint if ( preg_match( '/^(?:ISBN(?:-13)?:?|(?=[0-9]{13}$))((?:97[89])?[0-9]{9}[0-9X])$/i', str_replace( '-', '', $query ), $matches ) ) { $isbn_clean = $matches[1]; $open_library_url = 'https://openlibrary.org/api/books?bibkeys=ISBN:' . $isbn_clean . '&jscmd=data&format=json'; $is_isbn_query = true; } else { // General search for title or author $open_library_url = 'https://openlibrary.org/search.json?q=' . urlencode( $query ); } $open_library_response = wp_remote_get( $open_library_url, ['timeout' => 15] ); // Add timeout if ( ! is_wp_error( $open_library_response ) && wp_remote_retrieve_response_code( $open_library_response ) === 200 ) { $open_library_body = wp_remote_retrieve_body( $open_library_response ); $open_library_data = json_decode( $open_library_body, true ); if ( $is_isbn_query && ! empty( $open_library_data ) ) { // Open Library /api/books response for ISBN is structured by bibkey $bibkey = 'ISBN:' . $isbn_clean; if ( isset( $open_library_data[ $bibkey ] ) && ! empty( $open_library_data[ $bibkey ]['details'] ) ) { $details = $open_library_data[ $bibkey ]['details']; $cover_url = isset( $open_library_data[ $bibkey ]['thumbnail_url'] ) ? str_replace( '-S.jpg', '-L.jpg', $open_library_data[ $bibkey ]['thumbnail_url'] ) : ''; // Try to get larger cover // Use ternary operator to conditionally assign, or keep existing if not found $book_data['title'] = isset( $details['title'] ) ? sanitize_text_field( $details['title'] ) : $book_data['title']; $book_data['author'] = isset( $details['authors'] ) ? sanitize_text_field( implode( ', ', wp_list_pluck( $details['authors'], 'name' ) ) ) : $book_data['author']; $book_data['isbn'] = isset( $details['isbn_13'][0] ) ? sanitize_text_field( $details['isbn_13'][0] ) : (isset( $details['isbn_10'][0] ) ? sanitize_text_field( $details['isbn_10'][0] ) : $book_data['isbn']); $book_data['publisher'] = isset( $details['publishers'][0] ) ? sanitize_text_field( $details['publishers'][0] ) : $book_data['publisher']; $book_data['publication_year'] = isset( $details['publish_date'] ) ? substr( sanitize_text_field( $details['publish_date'] ), -4 ) : $book_data['publication_year']; $book_data['pages'] = isset( $details['number_of_pages'] ) ? absint( $details['number_of_pages'] ) : $book_data['pages']; $book_data['cover_image_url'] = ! empty( $cover_url ) ? esc_url_raw( $cover_url ) : $book_data['cover_image_url']; $book_data['notes'] = isset( $details['description'] ) ? (is_array($details['description']) && isset($details['description']['value']) ? sanitize_textarea_field($details['description']['value']) : sanitize_textarea_field($details['description'])) : $book_data['notes']; $book_data['genre'] = isset( $details['subjects'] ) ? sanitize_text_field( implode( ', ', $details['subjects'] ) ) : $book_data['genre']; $book_data['format'] = isset( $details['physical_format'] ) ? sanitize_text_field( $details['physical_format'] ) : $book_data['format']; if (isset($details['series']) && is_array($details['series'])) { $book_data['series_name'] = sanitize_text_field( implode(', ', $details['series']) ); } // If a title is found, assume success and return if ( ! empty( $book_data['title'] ) ) { wp_send_json_success( array( 'message' => esc_html__( 'Book details found via Open Library (ISBN)!', 'custom-book-library' ), 'book_data' => $book_data ) ); exit; } } } elseif ( ! $is_isbn_query && isset( $open_library_data['docs'] ) && ! empty( $open_library_data['docs'][0] ) ) { // Open Library /search.json response for general query $doc = $open_library_data['docs'][0]; // Take the first result $book_data['title'] = isset( $doc['title'] ) ? sanitize_text_field( $doc['title'] ) : $book_data['title']; $book_data['author'] = isset( $doc['author_name'][0] ) ? sanitize_text_field( $doc['author_name'][0] ) : $book_data['author']; $book_data['isbn'] = isset( $doc['isbn'][0] ) ? sanitize_text_field( $doc['isbn'][0] ) : $book_data['isbn']; $book_data['publisher'] = isset( $doc['publisher'][0] ) ? sanitize_text_field( $doc['publisher'][0] ) : $book_data['publisher']; $book_data['publication_year'] = isset( $doc['first_publish_year'] ) ? absint( $doc['first_publish_year'] ) : $book_data['publication_year']; $book_data['pages'] = isset( $doc['number_of_pages_median'] ) ? absint( $doc['number_of_pages_median'] ) : $book_data['pages']; $book_data['cover_image_url'] = isset( $doc['cover_i'] ) ? 'https://covers.openlibrary.org/b/id/' . absint( $doc['cover_i'] ) . '-L.jpg' : $book_data['cover_image_url']; $book_data['notes'] = ''; // Search API usually doesn't have descriptions directly $book_data['genre'] = isset( $doc['subject'][0] ) ? sanitize_text_field( implode( ', ', (array)$doc['subject'] ) ) : $book_data['genre']; $book_data['format'] = ''; // Not usually available in search API results if ( ! empty( $book_data['title'] ) ) { wp_send_json_success( array( 'message' => esc_html__( 'Book details found via Open Library (Search)!', 'custom-book-library' ), 'book_data' => $book_data ) ); exit; } } } else { $this->cbl_log_error('Open Library API Error: ' . (is_wp_error($open_library_response) ? $open_library_response->get_error_message() : wp_remote_retrieve_response_code($open_library_response))); } // If no book data was found from either API wp_send_json_error( array( 'message' => esc_html__( 'No book found for the given query. Please try another term or add manually.', 'custom-book-library' ) ) ); }
Fatal error: Uncaught Error: Class "CBL_Ajax_Handler" not found in /var/www/vhosts/bookshive.co.uk/httpdocs/wp-content/plugins/my-custom-book-library/cbl.php:149 Stack trace: #0 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-includes/class-wp-hook.php(324): My_Custom_Book_Library->init_plugin_components() #1 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters() #2 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-includes/plugin.php(517): WP_Hook->do_action() #3 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-settings.php(727): do_action() #4 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-config.php(52): require_once('...') #5 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-load.php(50): require_once('...') #6 /var/www/vhosts/bookshive.co.uk/httpdocs/wp-blog-header.php(13): require_once('...') #7 /var/www/vhosts/bookshive.co.uk/httpdocs/index.php(17): require('...') #8 {main} thrown in /var/www/vhosts/bookshive.co.uk/httpdocs/wp-content/plugins/my-custom-book-library/cbl.php on line 149