'array',
'sanitize_callback' => array( 'I365_PO_Plugin', 'sanitize_settings' ),
'default' => I365_PO_Plugin::defaults(),
)
);
}
/**
* Enqueue admin assets.
*
* @param string $hook Current page hook.
*
* @return void
*/
public static function enqueue_assets( $hook ) {
if ( 'settings_page_i365-po-settings' !== $hook ) {
return;
}
wp_enqueue_style(
'i365-po-admin',
plugin_dir_url( I365_PO_PLUGIN_FILE ) . 'assets/admin.css',
array(),
I365_PO_VERSION
);
wp_enqueue_script(
'i365-po-admin',
plugin_dir_url( I365_PO_PLUGIN_FILE ) . 'assets/admin.js',
array(),
I365_PO_VERSION,
true
);
$hero_guess = '';
$hero_path = get_theme_file_path( 'assets/images/hero.webp' );
if ( $hero_path && file_exists( $hero_path ) ) {
$hero_guess = get_theme_file_uri( 'assets/images/hero.webp' );
}
$font_guess = self::guess_font_url();
wp_localize_script(
'i365-po-admin',
'I365PODetect',
array(
'home' => home_url(),
'stylesheet' => get_stylesheet_uri(),
'hero' => $hero_guess,
'font' => $font_guess,
'fontsCdn' => 'https://fonts.gstatic.com',
'nonce' => wp_create_nonce( 'i365-po-detect' ),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'messages' => array(
'running' => __( 'Detecting from homepage…', '365i-performance-optimizer' ),
'failed' => __( 'Detection failed. Check your homepage is reachable.', '365i-performance-optimizer' ),
'completed'=> __( 'Detection updated fields.', '365i-performance-optimizer' ),
),
)
);
// Utilities nonce and messages for backup/restore, profiles, import/export.
wp_localize_script(
'i365-po-admin',
'I365POUtilities',
array(
'nonce' => wp_create_nonce( 'i365-po-utilities' ),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'messages' => array(
'confirmRestore' => __( 'Restore this backup? Current settings will be backed up first.', '365i-performance-optimizer' ),
'confirmDeleteBackup' => __( 'Delete this backup permanently?', '365i-performance-optimizer' ),
'confirmApplyProfile' => __( 'Apply this profile? Current settings will be backed up first.', '365i-performance-optimizer' ),
'confirmDeleteProfile' => __( 'Delete this custom profile?', '365i-performance-optimizer' ),
'confirmImport' => __( 'Import these settings? Current settings will be backed up first.', '365i-performance-optimizer' ),
'profileNameRequired' => __( 'Please enter a profile name.', '365i-performance-optimizer' ),
'selectFile' => __( 'Please select a JSON file to import.', '365i-performance-optimizer' ),
'invalidFile' => __( 'Please select a valid JSON file.', '365i-performance-optimizer' ),
'processing' => __( 'Processing…', '365i-performance-optimizer' ),
'error' => __( 'An error occurred. Please try again.', '365i-performance-optimizer' ),
),
)
);
}
/**
* Render settings page.
*
* @return void
*/
public static function render_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$settings = I365_PO_Plugin::get_settings();
?>
__( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$response = wp_remote_get(
home_url(),
array(
'timeout' => 10,
)
);
if ( is_wp_error( $response ) ) {
wp_send_json_error( array( 'message' => $response->get_error_message() ) );
}
$code = (int) wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
if ( $code >= 400 || empty( $body ) ) {
wp_send_json_error( array( 'message' => __( 'Homepage could not be fetched.', '365i-performance-optimizer' ) ) );
}
$result = array(
'preconnect' => array_filter( array( home_url(), 'https://fonts.gstatic.com' ) ),
'stylesheet' => '',
'font' => '',
'hero' => '',
);
libxml_use_internal_errors( true );
$dom = new \DOMDocument();
$loaded = $dom->loadHTML( $body );
libxml_clear_errors();
if ( $loaded ) {
$result['stylesheet'] = self::pick_best_stylesheet( $dom );
$font_pick = self::pick_best_font_v2( $dom );
$result['font'] = $font_pick['primary'];
$result['font_list'] = $font_pick['candidates'];
$result['hero'] = self::pick_best_image( $dom );
}
$result['stylesheet'] = self::strip_query( $result['stylesheet'] );
$result['font'] = self::strip_query( $result['font'] );
$result['hero'] = self::strip_query( $result['hero'] );
if ( ! empty( $result['stylesheet'] ) ) {
$response_code = wp_remote_retrieve_response_code( wp_remote_head( $result['stylesheet'], array( 'timeout' => 6, 'redirection' => 1 ) ) );
if ( $response_code >= 400 ) {
$result['stylesheet'] = '';
}
}
wp_send_json_success( $result );
}
/**
* Normalize relative URL to absolute.
*
* @param string $url URL.
*
* @return string
*/
private static function make_absolute_url( $url ) {
if ( empty( $url ) ) {
return '';
}
// Protocol-relative.
if ( 0 === strpos( $url, '//' ) ) {
return set_url_scheme( $url, is_ssl() ? 'https' : 'http' );
}
$parts = wp_parse_url( $url );
if ( ! empty( $parts['scheme'] ) ) {
return $url;
}
if ( ! empty( $parts['path'] ) && '/' === $parts['path'][0] ) {
return home_url( $parts['path'] );
}
return trailingslashit( home_url() ) . ltrim( $url, '/' );
}
/**
* Remove query string for preload targets.
*
* @param string $url URL.
* @return string
*/
private static function strip_query( $url ) {
if ( empty( $url ) ) {
return '';
}
$parts = wp_parse_url( $url );
if ( empty( $parts['query'] ) ) {
return $url;
}
$queryless = $parts['scheme'] . '://' . $parts['host'];
if ( ! empty( $parts['port'] ) ) {
$queryless .= ':' . $parts['port'];
}
if ( ! empty( $parts['path'] ) ) {
$queryless .= $parts['path'];
}
return $queryless;
}
/**
* Choose the most likely primary stylesheet.
*
* @param \DOMDocument $dom DOM.
* @return string
*/
private static function pick_best_stylesheet( \DOMDocument $dom ) {
$links = $dom->getElementsByTagName( 'link' );
$best = '';
$best_s = -INF;
$host = wp_parse_url( home_url(), PHP_URL_HOST );
foreach ( $links as $idx => $link ) {
$rel = strtolower( $link->getAttribute( 'rel' ) );
$href = $link->getAttribute( 'href' );
if ( false === strpos( $rel, 'stylesheet' ) ) {
continue;
}
if ( empty( $href ) ) {
continue;
}
$url = self::make_absolute_url( $href );
$score = 0 - $idx; // earlier is slightly preferred.
$target_host = wp_parse_url( $url, PHP_URL_HOST );
if ( $target_host && $host && $target_host === $host ) {
$score += 20;
}
if ( strpos( $url, 'dashicons' ) !== false || strpos( $url, 'emoji' ) !== false ) {
$score -= 50;
}
if ( strpos( $url, 'googleapis' ) !== false || strpos( $url, 'gstatic' ) !== false ) {
$score -= 10;
}
if ( $score > $best_s ) {
$best_s = $score;
$best = $url;
}
}
return $best;
}
/**
* Choose the most likely primary font preload or font file.
*
* @param \DOMDocument $dom DOM.
* @return string
*/
private static function pick_best_font( \DOMDocument $dom ) {
$links = $dom->getElementsByTagName( 'link' );
$best = '';
$best_s = -INF;
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$google_fonts = '';
$candidates = array();
foreach ( $links as $idx => $link ) {
$rel = strtolower( $link->getAttribute( 'rel' ) );
$href = $link->getAttribute( 'href' );
$as = strtolower( $link->getAttribute( 'as' ) );
if ( empty( $href ) ) {
continue;
}
$url = self::make_absolute_url( $href );
// Track Google Fonts stylesheet as fallback (remote or local Elementor cache).
if ( empty( $google_fonts ) && ( false !== strpos( $url, 'fonts.googleapis.com' ) || false !== strpos( $url, 'google-fonts/css' ) ) ) {
$google_fonts = $url;
}
if ( false === stripos( $rel, 'preload' ) && ! preg_match( '/\\.woff2?$/i', $url ) ) {
continue;
}
if ( stripos( $rel, 'preload' ) !== false && 'font' !== $as ) {
continue;
}
$score = 0 - $idx;
if ( preg_match( '/\\.woff2$/i', $url ) ) {
$score += 20;
} elseif ( preg_match( '/\\.woff$/i', $url ) ) {
$score += 10;
}
$target_host = wp_parse_url( $url, PHP_URL_HOST );
if ( $target_host ) {
// Only allow same-host or fonts.gstatic.com.
if ( $host && $target_host === $host ) {
$score += 10;
} elseif ( false !== strpos( $target_host, 'fonts.gstatic.com' ) ) {
$score += 15;
} else {
continue;
}
}
// Avoid icon fonts.
if ( strpos( $url, 'font-awesome' ) !== false || strpos( $url, 'icomoon' ) !== false || strpos( $url, 'icon' ) !== false ) {
$score -= 50;
}
if ( $score > $best_s ) {
$best_s = $score;
$best = $url;
}
$candidates[] = $url;
}
// Keep only same-host or gstatic candidates.
$candidates = array_values(
array_unique(
array_filter(
$candidates,
function ( $url ) use ( $host ) {
$th = wp_parse_url( $url, PHP_URL_HOST );
return $th && ( $th === $host || false !== strpos( $th, 'fonts.gstatic.com' ) );
}
)
)
);
// Try to fetch first WOFF2 from Google Fonts CSS (prefer variable).
if ( $google_fonts ) {
$gf_fonts = self::font_from_stylesheet( $google_fonts );
if ( ! empty( $gf_fonts ) ) {
$candidates = array_merge( $gf_fonts, $candidates );
}
}
$candidates = array_values( array_unique( $candidates ) );
return array(
'primary' => isset( $candidates[0] ) ? $candidates[0] : '',
'candidates' => $candidates,
);
}
/**
* Choose a hero-ish image: prefer hero/cover/banner keywords or larger dimensions.
*
* @param \DOMDocument $dom DOM.
* @return string
*/
private static function pick_best_image( \DOMDocument $dom ) {
$images = $dom->getElementsByTagName( 'img' );
$best = '';
$best_s = -INF;
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$keywords_positive = array( 'hero', 'cover', 'banner', 'slider', 'header', 'featured', 'main', 'masthead' );
$keywords_negative = array( 'logo', 'icon', 'avatar', 'thumb', 'thumbnail', 'emoji' );
foreach ( $images as $idx => $img ) {
$src = $img->getAttribute( 'src' );
if ( empty( $src ) ) {
continue;
}
$url = self::make_absolute_url( $src );
$score = 0 - $idx;
$cls_id = strtolower( $img->getAttribute( 'class' ) . ' ' . $img->getAttribute( 'id' ) . ' ' . $url );
foreach ( $keywords_positive as $word ) {
if ( false !== strpos( $cls_id, $word ) ) {
$score += 25;
}
}
foreach ( $keywords_negative as $word ) {
if ( false !== strpos( $cls_id, $word ) ) {
$score -= 30;
}
}
$w = (int) $img->getAttribute( 'width' );
$h = (int) $img->getAttribute( 'height' );
if ( $w >= 1200 || $h >= 800 ) {
$score += 30;
} elseif ( $w >= 800 || $h >= 600 ) {
$score += 20;
} elseif ( $w >= 400 || $h >= 300 ) {
$score += 10;
} else {
$score -= 5;
}
$target_host = wp_parse_url( $url, PHP_URL_HOST );
if ( $target_host && $host && $target_host === $host ) {
$score += 5;
}
if ( $score > $best_s ) {
$best_s = $score;
$best = $url;
}
}
return $best;
}
/**
* Safe, single-font detector following conservative rules.
*
* @param \DOMDocument $dom DOM.
* @return array {primary, candidates}
*/
private static function pick_best_font_v2( \DOMDocument $dom ) {
$links = $dom->getElementsByTagName( 'link' );
$styles = $dom->getElementsByTagName( 'style' );
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$google_fonts = array();
$primary_family = '';
$priority_selectors = array( 'body', 'html', 'h1', '.entry-title', '.site-title', '.elementor-heading-title' );
foreach ( $styles as $style ) {
$css = $style->textContent;
foreach ( $priority_selectors as $selector ) {
if ( preg_match( '/' . preg_quote( $selector, '/' ) . '\\s*\\{[^}]*font-family\\s*:\\s*([^;}]+)[;}]/i', $css, $m ) ) {
$families = explode( ',', $m[1] );
if ( ! empty( $families[0] ) ) {
$family = trim( $families[0], "\"' \t\r\n" );
if ( ! in_array( strtolower( $family ), array( 'sans-serif', 'serif', 'monospace', 'inherit' ), true ) ) {
$primary_family = $family;
break 2;
}
}
}
}
}
foreach ( $links as $link ) {
$rel = strtolower( $link->getAttribute( 'rel' ) );
$href = $link->getAttribute( 'href' );
if ( false === strpos( $rel, 'stylesheet' ) || empty( $href ) ) {
continue;
}
$url = self::make_absolute_url( $href );
if ( false !== strpos( $url, 'fonts.googleapis.com' ) || false !== strpos( $url, 'google-fonts/css' ) ) {
$google_fonts[] = $url;
if ( empty( $primary_family ) && preg_match( '/family=([^:&]+)/', $url, $m ) ) {
$family = urldecode( str_replace( '+', ' ', $m[1] ) );
$primary_family = $family;
}
}
}
if ( empty( $primary_family ) ) {
return array(
'primary' => '',
'candidates' => array(),
);
}
$candidates = array();
foreach ( $google_fonts as $css_url ) {
$fonts = self::font_from_stylesheet( $css_url, $primary_family );
if ( ! empty( $fonts ) ) {
foreach ( $fonts as $f ) {
$th = wp_parse_url( $f, PHP_URL_HOST );
if ( $th && ( $th === $host || false !== strpos( $th, 'fonts.gstatic.com' ) ) ) {
$candidates[] = $f;
}
}
}
}
$candidates = array_values( array_unique( $candidates ) );
if ( count( $candidates ) > 1 ) {
foreach ( $candidates as $cand ) {
if ( false !== stripos( $cand, 'var' ) ) {
$candidates = array( $cand );
break;
}
}
}
$primary = ( count( $candidates ) === 1 ) ? $candidates[0] : '';
return array(
'primary' => $primary,
'candidates' => $candidates,
);
}
/**
* Fetch a stylesheet and return the first usable woff2 from fonts.gstatic.com (prefer variable).
*
* @param string $url Stylesheet URL.
* @param string $family Target family (unused currently, kept for clarity).
* @return string
*/
private static function font_from_stylesheet( $url, $family = '' ) {
$response = wp_remote_get(
$url,
array(
'timeout' => 6,
'redirection' => 2,
)
);
if ( is_wp_error( $response ) ) {
return '';
}
$code = (int) wp_remote_retrieve_response_code( $response );
if ( $code >= 400 ) {
return '';
}
$body = wp_remote_retrieve_body( $response );
if ( empty( $body ) ) {
return '';
}
$urls = array();
preg_match_all( '/url\\(([^)]+)\\)/i', $body, $matches );
if ( ! empty( $matches[1] ) ) {
foreach ( $matches[1] as $candidate ) {
$candidate = trim( $candidate, "\"' \t\n\r\0\x0B" );
if ( preg_match( '/\\.woff2(\\?|$)/i', $candidate ) ) {
$target_host = wp_parse_url( $candidate, PHP_URL_HOST );
if ( $target_host && false !== strpos( $target_host, 'fonts.gstatic.com' ) ) {
$urls[] = $candidate;
}
}
}
}
if ( empty( $urls ) ) {
return array();
}
// Prefer variable font if present.
foreach ( $urls as $candidate ) {
if ( false !== stripos( $candidate, 'var' ) ) {
return array( $candidate );
}
}
return array( $urls[0] );
}
/**
* Add contextual help tab.
*
* @return void
*/
public static function add_help_tab() {
$screen = get_current_screen();
if ( ! $screen || 'settings_page_i365-po-settings' !== $screen->id ) {
return;
}
$screen->add_help_tab(
array(
'id' => 'i365-po-help',
'title' => __( 'Performance Optimizer Help', '365i-performance-optimizer' ),
'content' => '' . esc_html__( 'Toggle the options to match your site: enable speculation for faster navigation, set preconnect/preload URLs to your theme assets, defer non-core scripts, and refine image loading.', '365i-performance-optimizer' ) . '
' .
'' . esc_html__( 'Use Auto-detect to fill fields from your active theme. Exclude checkout/cart paths from speculation. Save to apply changes; Restore defaults to undo.', '365i-performance-optimizer' ) . '
',
)
);
}
/*
|--------------------------------------------------------------------------
| Backup & Restore AJAX Handlers
|--------------------------------------------------------------------------
*/
/**
* AJAX: Restore settings from a backup.
*
* @return void
*/
public static function ajax_restore_backup() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$timestamp = isset( $_POST['timestamp'] ) ? absint( $_POST['timestamp'] ) : 0;
if ( empty( $timestamp ) ) {
wp_send_json_error( array( 'message' => __( 'Invalid backup timestamp.', '365i-performance-optimizer' ) ) );
}
$result = I365_PO_Plugin::restore_backup( $timestamp );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'message' => __( 'Settings restored successfully. Page will reload.', '365i-performance-optimizer' ),
'reload' => true,
) );
}
/**
* AJAX: Delete a specific backup.
*
* @return void
*/
public static function ajax_delete_backup() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$timestamp = isset( $_POST['timestamp'] ) ? absint( $_POST['timestamp'] ) : 0;
if ( empty( $timestamp ) ) {
wp_send_json_error( array( 'message' => __( 'Invalid backup timestamp.', '365i-performance-optimizer' ) ) );
}
$result = I365_PO_Plugin::delete_backup( $timestamp );
if ( ! $result ) {
wp_send_json_error( array( 'message' => __( 'Failed to delete backup.', '365i-performance-optimizer' ) ) );
}
wp_send_json_success( array(
'message' => __( 'Backup deleted.', '365i-performance-optimizer' ),
'backups' => self::render_backups_list(),
) );
}
/*
|--------------------------------------------------------------------------
| Profile AJAX Handlers
|--------------------------------------------------------------------------
*/
/**
* AJAX: Apply a profile.
*
* @return void
*/
public static function ajax_apply_profile() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$slug = isset( $_POST['profile'] ) ? sanitize_text_field( wp_unslash( $_POST['profile'] ) ) : '';
if ( empty( $slug ) ) {
wp_send_json_error( array( 'message' => __( 'Invalid profile.', '365i-performance-optimizer' ) ) );
}
$result = I365_PO_Plugin::apply_profile( $slug );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
$profiles = I365_PO_Plugin::get_all_profiles();
$profile_name = isset( $profiles[ $slug ]['name'] ) ? $profiles[ $slug ]['name'] : $slug;
wp_send_json_success( array(
'message' => sprintf(
/* translators: %s: profile name */
__( 'Profile "%s" applied. Page will reload.', '365i-performance-optimizer' ),
$profile_name
),
'reload' => true,
) );
}
/**
* AJAX: Save current settings as a custom profile.
*
* @return void
*/
public static function ajax_save_profile() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
$description = isset( $_POST['description'] ) ? sanitize_text_field( wp_unslash( $_POST['description'] ) ) : '';
if ( empty( $name ) ) {
wp_send_json_error( array( 'message' => __( 'Profile name is required.', '365i-performance-optimizer' ) ) );
}
$result = I365_PO_Plugin::save_profile( $name, $description );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'message' => sprintf(
/* translators: %s: profile name */
__( 'Profile "%s" saved.', '365i-performance-optimizer' ),
$name
),
'profiles' => self::render_profiles_list(),
) );
}
/**
* AJAX: Delete a custom profile.
*
* @return void
*/
public static function ajax_delete_profile() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$slug = isset( $_POST['profile'] ) ? sanitize_text_field( wp_unslash( $_POST['profile'] ) ) : '';
if ( empty( $slug ) ) {
wp_send_json_error( array( 'message' => __( 'Invalid profile.', '365i-performance-optimizer' ) ) );
}
$result = I365_PO_Plugin::delete_profile( $slug );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'message' => __( 'Profile deleted.', '365i-performance-optimizer' ),
'profiles' => self::render_profiles_list(),
) );
}
/*
|--------------------------------------------------------------------------
| Import/Export AJAX Handlers
|--------------------------------------------------------------------------
*/
/**
* AJAX: Export settings as JSON.
*
* @return void
*/
public static function ajax_export() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$json = I365_PO_Plugin::export_settings();
wp_send_json_success( array(
'json' => $json,
'filename' => 'performance-optimizer-settings-' . gmdate( 'Y-m-d' ) . '.json',
) );
}
/**
* AJAX: Import settings from JSON.
*
* @return void
*/
public static function ajax_import() {
check_ajax_referer( 'i365-po-utilities', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Unauthorized', '365i-performance-optimizer' ) ) );
}
$json = isset( $_POST['json'] ) ? sanitize_textarea_field( wp_unslash( $_POST['json'] ) ) : '';
if ( empty( $json ) ) {
wp_send_json_error( array( 'message' => __( 'No settings data provided.', '365i-performance-optimizer' ) ) );
}
$result = I365_PO_Plugin::import_settings( $json );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'message' => __( 'Settings imported successfully. Page will reload.', '365i-performance-optimizer' ),
'reload' => true,
) );
}
/*
|--------------------------------------------------------------------------
| Helper Render Methods
|--------------------------------------------------------------------------
*/
/**
* Render backups list HTML for AJAX response.
*
* @return string HTML.
*/
private static function render_backups_list() {
$backups = I365_PO_Plugin::get_backups();
if ( empty( $backups ) ) {
return '' . esc_html__( 'No backups available.', '365i-performance-optimizer' ) . '
';
}
$html = '';
foreach ( $backups as $timestamp => $backup ) {
$date = wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp );
$user = ! empty( $backup['user'] ) ? $backup['user'] : __( 'Unknown', '365i-performance-optimizer' );
$version = ! empty( $backup['version'] ) ? $backup['version'] : '?';
$html .= '';
$html .= '' . esc_html( $date ) . ' ';
/* translators: %1$s: user name or ID, %2$s: plugin version */
$html .= '' . esc_html( sprintf( __( 'by %1$s (v%2$s)', '365i-performance-optimizer' ), $user, $version ) ) . ' ';
$html .= '';
$html .= '' . esc_html__( 'Restore', '365i-performance-optimizer' ) . ' ';
$html .= '' . esc_html__( 'Delete', '365i-performance-optimizer' ) . ' ';
$html .= ' ';
$html .= ' ';
}
$html .= ' ';
return $html;
}
/**
* Render profiles list HTML for AJAX response.
*
* @return string HTML.
*/
private static function render_profiles_list() {
$profiles = I365_PO_Plugin::get_all_profiles();
$html = '';
foreach ( $profiles as $slug => $profile ) {
$is_builtin = ! empty( $profile['builtin'] );
$html .= '
';
$html .= '
' . esc_html( $profile['name'] ) . ' ';
$html .= '
' . esc_html( $profile['description'] ) . '
';
$html .= '
';
$html .= '' . esc_html__( 'Apply', '365i-performance-optimizer' ) . ' ';
if ( ! $is_builtin ) {
$html .= '' . esc_html__( 'Delete', '365i-performance-optimizer' ) . ' ';
}
$html .= '
';
$html .= '
';
}
$html .= '
';
return $html;
}
}