diff --git a/admin/class-nginx-helper-admin.php b/admin/class-nginx-helper-admin.php index aeac55d..3c99f18 100644 --- a/admin/class-nginx-helper-admin.php +++ b/admin/class-nginx-helper-admin.php @@ -20,7 +20,7 @@ * @author rtCamp */ class Nginx_Helper_Admin { - + /** * The ID of this plugin. * @@ -29,7 +29,7 @@ class Nginx_Helper_Admin { * @var string $plugin_name The ID of this plugin. */ private $plugin_name; - + /** * The version of this plugin. * @@ -38,7 +38,7 @@ class Nginx_Helper_Admin { * @var string $version The current version of this plugin. */ private $version; - + /** * Various settings tabs. * @@ -47,7 +47,7 @@ class Nginx_Helper_Admin { * @var string $settings_tabs Various settings tabs. */ private $settings_tabs; - + /** * Purge options. * @@ -56,7 +56,7 @@ class Nginx_Helper_Admin { * @var string[] $options Purge options. */ public $options; - + /** * WP-CLI Command. * @@ -65,7 +65,7 @@ class Nginx_Helper_Admin { * @var string $options WP-CLI Command. */ const WP_CLI_COMMAND = 'nginx-helper'; - + /** * Initialize the class and set its properties. * @@ -74,10 +74,10 @@ class Nginx_Helper_Admin { * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { - + $this->plugin_name = $plugin_name; $this->version = $version; - + /** * Define settings tabs */ @@ -94,11 +94,10 @@ public function __construct( $plugin_name, $version ) { ), ) ); - + $this->options = $this->nginx_helper_settings(); - } - + /** * Register the stylesheets for the admin area. * @@ -107,7 +106,7 @@ public function __construct( $plugin_name, $version ) { * @param string $hook The current admin page. */ public function enqueue_styles( $hook ) { - + /** * This function is provided for demonstration purposes only. * @@ -119,16 +118,16 @@ public function enqueue_styles( $hook ) { * between the defined hooks and the functions defined in this * class. */ - + if ( 'settings_page_nginx' !== $hook ) { return; } - + wp_enqueue_style( $this->plugin_name . '-icons', plugin_dir_url( __FILE__ ) . 'icons/css/nginx-fontello.css', array(), $this->version, 'all' ); wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/nginx-helper-admin.css', array(), $this->version, 'all' ); - + } - + /** * Register the JavaScript for the admin area. * @@ -137,7 +136,7 @@ public function enqueue_styles( $hook ) { * @param string $hook The current admin page. */ public function enqueue_scripts( $hook ) { - + /** * This function is provided for demonstration purposes only. * @@ -149,29 +148,29 @@ public function enqueue_scripts( $hook ) { * between the defined hooks and the functions defined in this * class. */ - + if ( 'settings_page_nginx' !== $hook ) { return; } - + wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/nginx-helper-admin.js', array( 'jquery' ), $this->version, false ); - + $do_localize = array( 'purge_confirm_string' => esc_html__( 'Purging entire cache is not recommended. Would you like to continue?', 'nginx-helper' ), ); wp_localize_script( $this->plugin_name, 'nginx_helper', $do_localize ); - + } - + /** * Add admin menu. * * @since 2.0.0 */ public function nginx_helper_admin_menu() { - + if ( is_multisite() ) { - + add_submenu_page( 'settings.php', __( 'Nginx Helper', 'nginx-helper' ), @@ -180,9 +179,9 @@ public function nginx_helper_admin_menu() { 'nginx', array( &$this, 'nginx_helper_setting_page' ) ); - + } else { - + add_submenu_page( 'options-general.php', __( 'Nginx Helper', 'nginx-helper' ), @@ -191,22 +190,22 @@ public function nginx_helper_admin_menu() { 'nginx', array( &$this, 'nginx_helper_setting_page' ) ); - + } - + } - + /** * Function to add toolbar purge link. * * @param object $wp_admin_bar Admin bar object. */ public function nginx_helper_toolbar_purge_link( $wp_admin_bar ) { - + if ( ! current_user_can( 'manage_options' ) ) { return; } - + if ( is_admin() ) { $nginx_helper_urls = 'all'; $link_title = __( 'Purge Cache', 'nginx-helper' ); @@ -214,16 +213,16 @@ public function nginx_helper_toolbar_purge_link( $wp_admin_bar ) { $nginx_helper_urls = 'current-url'; $link_title = __( 'Purge Current Page', 'nginx-helper' ); } - + $purge_url = add_query_arg( array( 'nginx_helper_action' => 'purge', 'nginx_helper_urls' => $nginx_helper_urls, ) ); - + $nonced_url = wp_nonce_url( $purge_url, 'nginx_helper-purge_all' ); - + $wp_admin_bar->add_menu( array( 'id' => 'nginx-helper-purge-all', @@ -232,9 +231,9 @@ public function nginx_helper_toolbar_purge_link( $wp_admin_bar ) { 'meta' => array( 'title' => $link_title ), ) ); - + } - + /** * Display settings. * @@ -245,7 +244,7 @@ public function nginx_helper_toolbar_purge_link( $wp_admin_bar ) { public function nginx_helper_setting_page() { include plugin_dir_path( __FILE__ ) . 'partials/nginx-helper-admin-display.php'; } - + /** * Default settings. * @@ -253,7 +252,7 @@ public function nginx_helper_setting_page() { * @return array */ public function nginx_helper_default_settings() { - + return array( 'enable_purge' => 0, 'cache_method' => 'enable_fastcgi', @@ -278,8 +277,10 @@ public function nginx_helper_default_settings() { 'redis_prefix' => 'nginx-cache:', 'purge_url' => '', 'redis_enabled_by_constant' => 0, + 'preload_cache' => 0, + 'is_cache_preloaded' => 0 ); - + } public function store_default_options() { @@ -310,7 +311,7 @@ public function store_default_options() { * @since 2.0.0 */ public function nginx_helper_settings() { - + $options = get_site_option( 'rt_wp_nginx_helper_options', array( @@ -319,33 +320,33 @@ public function nginx_helper_settings() { 'redis_prefix' => 'nginx-cache:', ) ); - + $data = wp_parse_args( $options, $this->nginx_helper_default_settings() ); - + $is_redis_enabled = ( defined( 'RT_WP_NGINX_HELPER_REDIS_HOSTNAME' ) && defined( 'RT_WP_NGINX_HELPER_REDIS_PORT' ) && defined( 'RT_WP_NGINX_HELPER_REDIS_PREFIX' ) ); - + if ( ! $is_redis_enabled ) { return $data; } - + $data['redis_enabled_by_constant'] = $is_redis_enabled; $data['enable_purge'] = $is_redis_enabled; $data['cache_method'] = 'enable_redis'; $data['redis_hostname'] = RT_WP_NGINX_HELPER_REDIS_HOSTNAME; $data['redis_port'] = RT_WP_NGINX_HELPER_REDIS_PORT; - $data['redis_prefix'] = RT_WP_NGINX_HELPER_REDIS_PREFIX; - + $data['redis_prefix'] = RT_WP_NGINX_HELPER_REDIS_PREFIX; + return $data; - + } - + /** * Nginx helper setting link function. * @@ -354,20 +355,20 @@ public function nginx_helper_settings() { * @return mixed */ public function nginx_helper_settings_link( $links ) { - + if ( is_network_admin() ) { $setting_page = 'settings.php'; } else { $setting_page = 'options-general.php'; } - + $settings_link = '' . __( 'Settings', 'nginx-helper' ) . ''; array_unshift( $links, $settings_link ); - + return $links; - + } - + /** * Check if the nginx log is enabled. * @@ -375,20 +376,20 @@ public function nginx_helper_settings_link( $links ) { * @return boolean */ public function is_nginx_log_enabled() { - + $options = get_site_option( 'rt_wp_nginx_helper_options', array() ); - + if ( ! empty( $options['enable_log'] ) && 1 === (int) $options['enable_log'] ) { return true; } - + if ( defined( 'NGINX_HELPER_LOG' ) && true === NGINX_HELPER_LOG ) { return true; } - + return false; } - + /** * Retrieve the asset path. * @@ -396,13 +397,13 @@ public function is_nginx_log_enabled() { * @return string asset path of the plugin. */ public function functional_asset_path() { - + $log_path = WP_CONTENT_DIR . '/uploads/nginx-helper/'; - + return apply_filters( 'nginx_asset_path', $log_path ); - + } - + /** * Retrieve the asset url. * @@ -410,82 +411,82 @@ public function functional_asset_path() { * @return string asset url of the plugin. */ public function functional_asset_url() { - + $log_url = WP_CONTENT_URL . '/uploads/nginx-helper/'; - + return apply_filters( 'nginx_asset_url', $log_url ); - + } - + /** * Get latest news. * * @since 2.0.0 */ public function nginx_helper_get_feeds() { - + // Get RSS Feed(s). require_once ABSPATH . WPINC . '/feed.php'; - + $maxitems = 0; $rss_items = array(); - + // Get a SimplePie feed object from the specified feed source. $rss = fetch_feed( 'https://rtcamp.com/blog/feed/' ); - + if ( ! is_wp_error( $rss ) ) { // Checks that the object is created correctly. - + // Figure out how many total items there are, but limit it to 5. $maxitems = $rss->get_item_quantity( 5 ); // Build an array of all the items, starting with element 0 (first element). $rss_items = $rss->get_items( 0, $maxitems ); - + } ?> - options['enable_purge'] || 1 !== (int) $this->options['enable_stamp'] ) { return; } - + if ( ! empty( $pagenow ) && 'wp-login.php' === $pagenow ) { return; } - + foreach ( headers_list() as $header ) { list( $key, $value ) = explode( ':', $header, 2 ); $key = strtolower( $key ); @@ -496,32 +497,32 @@ public function add_timestamps() { break; } } - + /** * Don't add timestamp if run from ajax, cron or wpcli. */ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { return; } - + if ( defined( 'DOING_CRON' ) && DOING_CRON ) { return; } - + if ( defined( 'WP_CLI' ) && WP_CLI ) { return; } - + $timestamps = "\n\n" . - ''; - + 'Cached using Nginx-Helper on ' . current_time( 'mysql' ) . '. ' . + 'It took ' . get_num_queries() . ' queries executed in ' . timer_stop() . ' seconds.' . + "-->\n" . + ''; + echo wp_kses( $timestamps, array() ); - + } - + /** * Get map * @@ -530,83 +531,83 @@ public function add_timestamps() { * @return string */ public function get_map() { - + if ( ! $this->options['enable_map'] ) { return; } - + if ( is_multisite() ) { - + global $wpdb; - + $rt_all_blogs = $wpdb->get_results( $wpdb->prepare( 'SELECT blog_id, domain, path FROM ' . $wpdb->blogs . " WHERE site_id = %d AND archived = '0' AND mature = '0' AND spam = '0' AND deleted = '0'", $wpdb->siteid ) ); - + $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping'; - + $rt_domain_map_sites = ''; - + if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->dmtable}'" ) === $wpdb->dmtable ) { // phpcs:ignore $rt_domain_map_sites = $wpdb->get_results( "SELECT blog_id, domain FROM {$wpdb->dmtable} ORDER BY id DESC" ); } - + $rt_nginx_map = ''; $rt_nginx_map_array = array(); - + if ( $rt_all_blogs ) { - + foreach ( $rt_all_blogs as $blog ) { - + if ( true === SUBDOMAIN_INSTALL ) { $rt_nginx_map_array[ $blog->domain ] = $blog->blog_id; } else { - + if ( 1 !== $blog->blog_id ) { $rt_nginx_map_array[ $blog->path ] = $blog->blog_id; } } } } - + if ( $rt_domain_map_sites ) { - + foreach ( $rt_domain_map_sites as $site ) { $rt_nginx_map_array[ $site->domain ] = $site->blog_id; } } - + foreach ( $rt_nginx_map_array as $domain => $domain_id ) { $rt_nginx_map .= "\t" . $domain . "\t" . $domain_id . ";\n"; } - + return $rt_nginx_map; - + } - + } - + /** * Update map */ public function update_map() { - + if ( is_multisite() ) { - + $rt_nginx_map = $this->get_map(); - + $fp = fopen( $this->functional_asset_path() . 'map.conf', 'w+' ); if ( $fp ) { fwrite( $fp, $rt_nginx_map ); fclose( $fp ); } } - + } - + /** * Purge url when post status is changed. * @@ -618,28 +619,28 @@ public function update_map() { * @param object $post Post object. */ public function set_future_post_option_on_future_status( $new_status, $old_status, $post ) { - + global $blog_id, $nginx_purger; - + $exclude_post_types = array( 'nav_menu_item' ); - + if ( in_array( $post->post_type, $exclude_post_types, true ) ) { return; } - - if ( ! $this->options['enable_purge'] ) { + + if ( ! $this->options['enable_purge'] || $this->is_import_request() ) { return; } - + $purge_status = array( 'publish', 'future' ); - + if ( in_array( $old_status, $purge_status, true ) || in_array( $new_status, $purge_status, true ) ) { - + $nginx_purger->log( 'Purge post on transition post STATUS from ' . $old_status . ' to ' . $new_status ); $nginx_purger->purge_post( $post->ID ); - + } - + if ( 'future' === $new_status && $post && 'future' === $post->post_status && ( @@ -650,15 +651,15 @@ public function set_future_post_option_on_future_status( $new_status, $old_statu ) ) ) { - + $nginx_purger->log( 'Set/update future_posts option ( post id = ' . $post->ID . ' and blog id = ' . $blog_id . ' )' ); $this->options['future_posts'][ $blog_id ][ $post->ID ] = strtotime( $post->post_date_gmt ) + 60; update_site_option( 'rt_wp_nginx_helper_options', $this->options ); - + } - + } - + /** * Unset future post option on delete * @@ -668,9 +669,9 @@ public function set_future_post_option_on_future_status( $new_status, $old_statu * @param int $post_id Post id. */ public function unset_future_post_option_on_delete( $post_id ) { - + global $blog_id, $nginx_purger; - + if ( ! $this->options['enable_purge'] || empty( $this->options['future_posts'] ) || @@ -680,18 +681,18 @@ public function unset_future_post_option_on_delete( $post_id ) { ) { return; } - + $nginx_purger->log( 'Unset future_posts option ( post id = ' . $post_id . ' and blog id = ' . $blog_id . ' )' ); - + unset( $this->options['future_posts'][ $blog_id ][ $post_id ] ); - + if ( ! count( $this->options['future_posts'][ $blog_id ] ) ) { unset( $this->options['future_posts'][ $blog_id ] ); } - + update_site_option( 'rt_wp_nginx_helper_options', $this->options ); } - + /** * Update map when new blog added in multisite. * @@ -700,18 +701,18 @@ public function unset_future_post_option_on_delete( $post_id ) { * @param string $blog_id blog id. */ public function update_new_blog_options( $blog_id ) { - + global $nginx_purger; - + $nginx_purger->log( "New site added ( id $blog_id )" ); $this->update_map(); $nginx_purger->log( "New site added to nginx map ( id $blog_id )" ); $helper_options = $this->nginx_helper_default_settings(); update_blog_option( $blog_id, 'rt_wp_nginx_helper_options', $helper_options ); $nginx_purger->log( "Default options updated for the new blog ( id $blog_id )" ); - + } - + /** * Purge all urls. * Purge current page cache when purging is requested from front @@ -720,14 +721,18 @@ public function update_new_blog_options( $blog_id ) { * @global object $nginx_purger */ public function purge_all() { - + + if ( $this->is_import_request() ) { + return; + } + global $nginx_purger, $wp; - + $method = null; if ( isset( $_SERVER['REQUEST_METHOD'] ) ) { $method = wp_strip_all_tags( $_SERVER['REQUEST_METHOD'] ); } - + $action = ''; if ( 'POST' === $method ) { if ( isset( $_POST['nginx_helper_action'] ) ) { @@ -738,34 +743,34 @@ public function purge_all() { $action = wp_strip_all_tags( $_GET['nginx_helper_action'] ); } } - + if ( empty( $action ) ) { return; } - + if ( ! current_user_can( 'manage_options' ) ) { wp_die( 'Sorry, you do not have the necessary privileges to edit these options.' ); } - + if ( 'done' === $action ) { - + add_action( 'admin_notices', array( &$this, 'display_notices' ) ); add_action( 'network_admin_notices', array( &$this, 'display_notices' ) ); return; - + } - + check_admin_referer( 'nginx_helper-purge_all' ); - + $current_url = user_trailingslashit( home_url( $wp->request ) ); - + if ( ! is_admin() ) { $action = 'purge_current_page'; $redirect_url = $current_url; } else { $redirect_url = add_query_arg( array( 'nginx_helper_action' => 'done' ) ); } - + switch ( $action ) { case 'purge': $nginx_purger->purge_all(); @@ -774,28 +779,138 @@ public function purge_all() { $nginx_purger->purge_url( $current_url ); break; } - + if ( 'purge' === $action ) { - + /** * Fire an action after the entire cache has been purged whatever caching type is used. * * @since 2.2.2 */ do_action( 'rt_nginx_helper_after_purge_all' ); - + } - + wp_redirect( esc_url_raw( $redirect_url ) ); exit(); - + } - + /** * Dispay plugin notices. */ public function display_notices() { echo '

' . esc_html__( 'Purge initiated', 'nginx-helper' ) . '

'; } - + + /** + * Preloads the cache for the website. + * + * @return void + */ + public function preload_cache() { + $is_cache_preloaded = $this->options['is_cache_preloaded']; + $preload_cache_enabled = $this->options['preload_cache']; + + if ( $preload_cache_enabled && false === boolval( $is_cache_preloaded ) ) { + $this->options['is_cache_preloaded'] = true; + + update_site_option( 'rt_wp_nginx_helper_options', $this->options ); + $this->preload_cache_from_sitemap(); + } + } + + /** + * This function preloads the cache from sitemap url. + * + * @return void + */ + private function preload_cache_from_sitemap() { + + $sitemap_urls = $this->get_index_sitemap_urls(); + $all_urls = array(); + + foreach ( $sitemap_urls as $sitemap_url ) { + $urls = $this->extract_sitemap_urls( $sitemap_url ); + $all_urls = array_merge( $all_urls, $urls ); + } + + $args = array( + 'timeout' => 1, + 'blocking' => false, + 'sslverify' => false, + ); + + foreach ( $all_urls as $url ) { + wp_remote_get( esc_url_raw( $url ), $args ); + } + + } + + /** + * Fetches all the sitemap urls for the site. + * + * @return array + */ + private function get_index_sitemap_urls() { + $sitemaps = wp_sitemaps_get_server()->index->get_sitemap_list(); + $urls = array(); + foreach ( $sitemaps as $sitemap ) { + $urls[] = $sitemap['loc']; + } + return $urls; + } + + /** + * Parse sitemap content and extract all URLs. + * + * @param string $sitemap_url The URL of the sitemap. + * @return array|WP_Error An array of URLs or WP_Error on failure. + */ + private function extract_sitemap_urls( $sitemap_url ) { + $response = wp_remote_get( $sitemap_url ); + + $urls = array(); + + if ( is_wp_error( $response ) ) { + return $urls; + } + + $sitemap_content = wp_remote_retrieve_body( $response ); + + libxml_use_internal_errors( true ); + $xml = simplexml_load_string( $sitemap_content ); + + if ( false === $xml ) { + return new WP_Error( 'sitemap_parse_error', esc_html__( 'Failed to parse the sitemap XML', 'nginx-helper' ) ); + } + + $urls = array(); + + if ( $xml === false ) { + return $urls; + } + + foreach ( $xml->url as $url ) { + $urls[] = (string) $url->loc; + } + + return $urls; + } + + /** + * Determines if the current request is for importing Posts/ WordPress content. + * + * @return bool True if the request is for importing, false otherwise. + */ + public function is_import_request() { + $import_query_var = sanitize_text_field( wp_unslash( $_GET['import'] ?? '' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is already in the admin dashboard. + $has_import_started = did_action( 'import_start' ); + + return ( defined( 'WP_IMPORTING' ) && true === WP_IMPORTING ) + || 0 !== $has_import_started + || ! empty( $import_query_var ); + } + + }