/**
* 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