'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
)
);
register_setting(
'magicgeo_settings',
'magicgeo_auto_schema',
array(
'type' => 'boolean',
'sanitize_callback' => 'rest_sanitize_boolean',
'default' => true,
)
);
}
/**
* Enqueue admin scripts and styles.
*
* @param string $hook Current admin page hook.
*/
function magicgeo_admin_scripts( $hook ) {
if ( strpos( $hook, 'magicgeo' ) === false ) {
return;
}
wp_enqueue_style(
'magicgeo-admin',
MAGICGEO_PLUGIN_URL . 'assets/css/admin.css',
array(),
MAGICGEO_VERSION
);
wp_enqueue_script(
'magicgeo-admin',
MAGICGEO_PLUGIN_URL . 'assets/js/admin.js',
array( 'jquery' ),
MAGICGEO_VERSION,
true
);
wp_localize_script(
'magicgeo-admin',
'magicgeoAdmin',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'magicgeo_nonce' ),
'apiUrl' => MAGICGEO_API_URL,
'strings' => array(
'optimizing' => esc_html__( 'Optimizing...', 'magicgeo' ),
'success' => esc_html__( 'Product optimized successfully!', 'magicgeo' ),
'error' => esc_html__( 'Optimization failed. Please try again.', 'magicgeo' ),
),
)
);
}
/**
* Main admin page.
*/
function magicgeo_admin_page() {
$api_key = get_option( 'magicgeo_api_key', '' );
if ( empty( $api_key ) ) {
magicgeo_settings_page();
return;
}
// Get products.
$products = wc_get_products(
array(
'status' => 'publish',
'limit' => 50,
)
);
?>
|
|
|
|
get_id(), '_magicgeo_score', true );
$geo_status = get_post_meta( $product_item->get_id(), '_magicgeo_status', true );
$score_class = '';
if ( $geo_score ) {
$geo_score_int = absint( $geo_score );
if ( $geo_score_int >= 70 ) {
$score_class = 'good';
} elseif ( $geo_score_int >= 40 ) {
$score_class = 'medium';
} else {
$score_class = 'low';
}
}
$status_class = $geo_status ? sanitize_html_class( $geo_status ) : 'pending';
$status_label = $geo_status ? ucfirst( sanitize_text_field( $geo_status ) ) : esc_html__( 'Not analyzed', 'magicgeo' );
?>
get_name() ); ?>
get_sku() ); ?>
|
|
|
|
' . esc_html__( 'Settings saved successfully.', 'magicgeo' ) . '
';
}
$api_key = get_option( 'magicgeo_api_key', '' );
$auto_schema = get_option( 'magicgeo_auto_schema', true );
?>
get_id(), '_magicgeo_schema', true );
if ( ! empty( $stored_schema ) ) {
echo wp_kses(
$stored_schema,
array(
'script' => array(
'type' => array(),
),
)
);
return;
}
// Generate default schema if none stored.
$schema = magicgeo_generate_default_schema( $product );
?>
'https://schema.org',
'@type' => 'Product',
'name' => $wc_product->get_name(),
'description' => wp_strip_all_tags( $wc_product->get_short_description() ? $wc_product->get_short_description() : $wc_product->get_description() ),
'sku' => $wc_product->get_sku(),
'url' => get_permalink( $wc_product->get_id() ),
);
// Add image.
$image_id = $wc_product->get_image_id();
if ( $image_id ) {
$image_url = wp_get_attachment_url( $image_id );
$schema['image'] = $image_url;
}
// Add brand if available.
$brand = $wc_product->get_attribute( 'brand' );
if ( ! empty( $brand ) ) {
$schema['brand'] = array(
'@type' => 'Brand',
'name' => $brand,
);
}
// Add offers.
$schema['offers'] = array(
'@type' => 'Offer',
'url' => get_permalink( $wc_product->get_id() ),
'priceCurrency' => get_woocommerce_currency(),
'price' => $wc_product->get_price(),
'availability' => $wc_product->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
'itemCondition' => 'https://schema.org/NewCondition',
);
// Add seller.
$schema['offers']['seller'] = array(
'@type' => 'Organization',
'name' => get_bloginfo( 'name' ),
);
return $schema;
}
/**
* AJAX handler for product optimization.
*/
function magicgeo_ajax_optimize_product() {
// Verify nonce.
check_ajax_referer( 'magicgeo_nonce', 'nonce' );
// Check capabilities.
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'magicgeo' ) ) );
}
// Validate and sanitize product ID.
if ( ! isset( $_POST['product_id'] ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid product ID.', 'magicgeo' ) ) );
}
$product_id = absint( wp_unslash( $_POST['product_id'] ) );
if ( ! $product_id ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid product ID.', 'magicgeo' ) ) );
}
$product_obj = wc_get_product( $product_id );
if ( ! $product_obj ) {
wp_send_json_error( array( 'message' => esc_html__( 'Product not found.', 'magicgeo' ) ) );
}
$api_key = get_option( 'magicgeo_api_key', '' );
if ( empty( $api_key ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'API key not configured.', 'magicgeo' ) ) );
}
// Prepare product data.
$product_data = array(
'id' => $product_obj->get_id(),
'title' => $product_obj->get_name(),
'description' => $product_obj->get_description(),
'sku' => $product_obj->get_sku(),
'price' => $product_obj->get_price(),
'categories' => wp_get_post_terms( $product_obj->get_id(), 'product_cat', array( 'fields' => 'names' ) ),
);
// Call MagicGEO API.
$response = wp_remote_post(
MAGICGEO_API_URL . '/woocommerce-optimize',
array(
'timeout' => 60,
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'body' => wp_json_encode( $product_data ),
)
);
if ( is_wp_error( $response ) ) {
wp_send_json_error( array( 'message' => esc_html( $response->get_error_message() ) ) );
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( isset( $body['error'] ) ) {
wp_send_json_error( array( 'message' => esc_html( $body['error'] ) ) );
}
// Save optimization results.
if ( isset( $body['geo_score'] ) ) {
update_post_meta( $product_id, '_magicgeo_score', absint( $body['geo_score'] ) );
}
if ( isset( $body['status'] ) ) {
update_post_meta( $product_id, '_magicgeo_status', sanitize_text_field( $body['status'] ) );
}
if ( isset( $body['schema_markup'] ) ) {
update_post_meta( $product_id, '_magicgeo_schema', wp_kses_post( $body['schema_markup'] ) );
}
if ( isset( $body['seo_description'] ) ) {
update_post_meta( $product_id, '_magicgeo_seo_description', sanitize_textarea_field( $body['seo_description'] ) );
}
wp_send_json_success(
array(
'message' => esc_html__( 'Product optimized successfully!', 'magicgeo' ),
'geo_score' => isset( $body['geo_score'] ) ? absint( $body['geo_score'] ) : 0,
'status' => isset( $body['status'] ) ? sanitize_text_field( $body['status'] ) : 'optimized',
)
);
}
add_action( 'wp_ajax_magicgeo_optimize_product', 'magicgeo_ajax_optimize_product' );
/**
* AJAX handler for product analysis.
*/
function magicgeo_ajax_analyze_product() {
// Verify nonce.
check_ajax_referer( 'magicgeo_nonce', 'nonce' );
// Check capabilities.
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'magicgeo' ) ) );
}
// Validate and sanitize product ID.
if ( ! isset( $_POST['product_id'] ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid product ID.', 'magicgeo' ) ) );
}
$product_id = absint( wp_unslash( $_POST['product_id'] ) );
if ( ! $product_id ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid product ID.', 'magicgeo' ) ) );
}
$product_obj = wc_get_product( $product_id );
if ( ! $product_obj ) {
wp_send_json_error( array( 'message' => esc_html__( 'Product not found.', 'magicgeo' ) ) );
}
// Calculate local GEO score.
$score = 0;
$issues = array();
// Check title length (10 points).
$title_length = strlen( $product_obj->get_name() );
if ( $title_length >= 30 && $title_length <= 60 ) {
$score += 10;
} else {
$issues[] = esc_html__( 'Title should be between 30-60 characters', 'magicgeo' );
}
// Check description (20 points).
$description = $product_obj->get_description();
if ( strlen( $description ) >= 150 ) {
$score += 20;
} else {
$issues[] = esc_html__( 'Description should be at least 150 characters', 'magicgeo' );
}
// Check short description (15 points).
$short_desc = $product_obj->get_short_description();
if ( strlen( $short_desc ) >= 50 ) {
$score += 15;
} else {
$issues[] = esc_html__( 'Short description should be at least 50 characters', 'magicgeo' );
}
// Check images (15 points).
$gallery_ids = $product_obj->get_gallery_image_ids();
$image_count = $product_obj->get_image_id() ? count( $gallery_ids ) + 1 : count( $gallery_ids );
if ( $image_count >= 3 ) {
$score += 15;
} elseif ( $image_count >= 1 ) {
$score += 7;
$issues[] = esc_html__( 'Add more product images (3+ recommended)', 'magicgeo' );
} else {
$issues[] = esc_html__( 'Add product images', 'magicgeo' );
}
// Check SKU (10 points).
if ( ! empty( $product_obj->get_sku() ) ) {
$score += 10;
} else {
$issues[] = esc_html__( 'Add a SKU for better indexing', 'magicgeo' );
}
// Check categories (15 points).
$categories = wp_get_post_terms( $product_obj->get_id(), 'product_cat' );
if ( ! is_wp_error( $categories ) && count( $categories ) >= 1 ) {
$score += 15;
} else {
$issues[] = esc_html__( 'Assign product to at least one category', 'magicgeo' );
}
// Check price (15 points).
if ( $product_obj->get_price() > 0 ) {
$score += 15;
} else {
$issues[] = esc_html__( 'Set a product price', 'magicgeo' );
}
// Save analysis results.
update_post_meta( $product_id, '_magicgeo_score', $score );
if ( $score >= 70 ) {
$status = 'optimized';
} elseif ( $score >= 40 ) {
$status = 'needs_work';
} else {
$status = 'critical';
}
update_post_meta( $product_id, '_magicgeo_status', $status );
wp_send_json_success(
array(
'message' => esc_html__( 'Analysis complete!', 'magicgeo' ),
'geo_score' => $score,
'issues' => $issues,
'status' => $status,
)
);
}
add_action( 'wp_ajax_magicgeo_analyze_product', 'magicgeo_ajax_analyze_product' );
/**
* Add settings link on plugins page.
*
* @param array $links Existing plugin action links.
* @return array Modified links.
*/
function magicgeo_plugin_action_links( $links ) {
$settings_link = '' . esc_html__( 'Settings', 'magicgeo' ) . '';
array_unshift( $links, $settings_link );
return $links;
}
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'magicgeo_plugin_action_links' );
/**
* Plugin activation hook.
*/
function magicgeo_activate() {
// Create necessary options.
add_option( 'magicgeo_api_key', '' );
add_option( 'magicgeo_auto_schema', true );
add_option( 'magicgeo_version', MAGICGEO_VERSION );
}
register_activation_hook( __FILE__, 'magicgeo_activate' );
/**
* Plugin deactivation hook.
*/
function magicgeo_deactivate() {
// Cleanup if needed.
}
register_deactivation_hook( __FILE__, 'magicgeo_deactivate' );