__( 'Security check failed.', 'maple-icons' ) ), 403 ); } // 2. Capability check. if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to do this.', 'maple-icons' ) ), 403 ); } // 3. Input validation. $slug = isset( $_POST['slug'] ) ? sanitize_key( $_POST['slug'] ) : ''; if ( empty( $slug ) ) { wp_send_json_error( array( 'message' => __( 'Icon set slug is required.', 'maple-icons' ) ) ); } if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) { wp_send_json_error( array( 'message' => __( 'Invalid icon set.', 'maple-icons' ) ) ); } // Check if already downloaded. $registry = MI_Icon_Registry::get_instance(); if ( $registry->is_downloaded( $slug ) ) { wp_send_json_error( array( 'message' => __( 'This icon set is already downloaded.', 'maple-icons' ) ) ); } // 4. Process download. // Increase time limit for large downloads. set_time_limit( 600 ); // 10 minutes. $downloader = new MI_Downloader(); $result = $downloader->download_set( $slug ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } if ( ! $result['success'] ) { wp_send_json_error( array( 'message' => sprintf( /* translators: %1$d: downloaded count, %2$d: failed count */ __( 'Download completed with errors. %1$d downloaded, %2$d failed.', 'maple-icons' ), $result['downloaded'], $result['failed'] ), 'errors' => array_slice( $result['errors'], 0, 10 ), // Limit errors shown. 'downloaded' => $result['downloaded'], 'failed' => $result['failed'], ) ); } // Mark as downloaded. $registry->mark_downloaded( $slug, $result['downloaded'] ); // If no active set, make this one active. if ( ! $registry->get_active_set() ) { $registry->set_active( $slug ); } $registry->refresh(); wp_send_json_success( array( 'message' => sprintf( /* translators: %d: number of icons */ __( 'Successfully downloaded %d icons.', 'maple-icons' ), $result['downloaded'] ), 'icon_count' => $result['downloaded'], 'is_active' => $registry->get_active_set() === $slug, ) ); } /** * Handle delete set request. */ public function handle_delete_set() { // 1. Nonce verification. if ( ! check_ajax_referer( 'mi_admin_nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => __( 'Security check failed.', 'maple-icons' ) ), 403 ); } // 2. Capability check. if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to do this.', 'maple-icons' ) ), 403 ); } // 3. Input validation. $slug = isset( $_POST['slug'] ) ? sanitize_key( $_POST['slug'] ) : ''; if ( empty( $slug ) ) { wp_send_json_error( array( 'message' => __( 'Icon set slug is required.', 'maple-icons' ) ) ); } if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) { wp_send_json_error( array( 'message' => __( 'Invalid icon set.', 'maple-icons' ) ) ); } // 4. Delete the set. $registry = MI_Icon_Registry::get_instance(); $result = $registry->delete_set( $slug ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } wp_send_json_success( array( 'message' => __( 'Icon set deleted successfully.', 'maple-icons' ), ) ); } /** * Handle set active request. */ public function handle_set_active() { // 1. Nonce verification. if ( ! check_ajax_referer( 'mi_admin_nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => __( 'Security check failed.', 'maple-icons' ) ), 403 ); } // 2. Capability check. if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to do this.', 'maple-icons' ) ), 403 ); } // 3. Input validation. $slug = isset( $_POST['slug'] ) ? sanitize_key( $_POST['slug'] ) : ''; // Empty slug is allowed (to deactivate). if ( ! empty( $slug ) && ! MI_Icon_Sets::is_valid_slug( $slug ) ) { wp_send_json_error( array( 'message' => __( 'Invalid icon set.', 'maple-icons' ) ) ); } // 4. Set active. $registry = MI_Icon_Registry::get_instance(); $result = $registry->set_active( $slug ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } wp_send_json_success( array( 'message' => empty( $slug ) ? __( 'No icon set is now active.', 'maple-icons' ) : __( 'Icon set activated.', 'maple-icons' ), 'active_set' => $slug, ) ); } /** * Handle get progress request. */ public function handle_get_progress() { // 1. Nonce verification. if ( ! check_ajax_referer( 'mi_admin_nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => __( 'Security check failed.', 'maple-icons' ) ), 403 ); } // 2. Capability check. if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to do this.', 'maple-icons' ) ), 403 ); } // 3. Input validation. $slug = isset( $_POST['slug'] ) ? sanitize_key( $_POST['slug'] ) : ''; if ( empty( $slug ) || ! MI_Icon_Sets::is_valid_slug( $slug ) ) { wp_send_json_error( array( 'message' => __( 'Invalid icon set.', 'maple-icons' ) ) ); } // 4. Get progress. $downloader = new MI_Downloader(); $progress = $downloader->get_progress( $slug ); if ( false === $progress ) { wp_send_json_success( array( 'downloading' => false, 'completed' => 0, 'total' => 0, ) ); } wp_send_json_success( array( 'downloading' => true, 'completed' => $progress['completed'], 'total' => $progress['total'], 'percentage' => $progress['total'] > 0 ? round( ( $progress['completed'] / $progress['total'] ) * 100 ) : 0, ) ); } /** * Handle search icons request (for block editor). */ public function handle_search_icons() { // 1. Nonce verification. if ( ! check_ajax_referer( 'mi_block_nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => __( 'Security check failed.', 'maple-icons' ) ), 403 ); } // 2. Capability check (edit_posts for block usage). if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to do this.', 'maple-icons' ) ), 403 ); } // 3. Input validation. $query = isset( $_POST['query'] ) ? sanitize_text_field( $_POST['query'] ) : ''; $style = isset( $_POST['style'] ) ? sanitize_key( $_POST['style'] ) : ''; $limit = isset( $_POST['limit'] ) ? absint( $_POST['limit'] ) : MI_SEARCH_LIMIT; $offset = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 0; // Limit the limit. if ( $limit > 100 ) { $limit = 100; } // 4. Search. $registry = MI_Icon_Registry::get_instance(); $results = $registry->search_icons( $query, $style, $limit, $offset ); wp_send_json_success( $results ); } /** * Handle get icon SVG request (for block editor). */ public function handle_get_icon_svg() { // 1. Nonce verification. if ( ! check_ajax_referer( 'mi_block_nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => __( 'Security check failed.', 'maple-icons' ) ), 403 ); } // 2. Capability check. if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to do this.', 'maple-icons' ) ), 403 ); } // 3. Input validation. $slug = isset( $_POST['set'] ) ? sanitize_key( $_POST['set'] ) : ''; $style = isset( $_POST['style'] ) ? sanitize_key( $_POST['style'] ) : ''; $name = isset( $_POST['name'] ) ? sanitize_file_name( $_POST['name'] ) : ''; if ( empty( $slug ) || empty( $style ) || empty( $name ) ) { wp_send_json_error( array( 'message' => __( 'Missing required parameters.', 'maple-icons' ) ) ); } // 4. Get SVG. $registry = MI_Icon_Registry::get_instance(); $svg = $registry->get_icon_svg( $slug, $style, $name ); if ( is_wp_error( $svg ) ) { wp_send_json_error( array( 'message' => $svg->get_error_message() ) ); } wp_send_json_success( array( 'svg' => $svg, 'set' => $slug, 'style' => $style, 'name' => $name, ) ); } }