| @@ -44,9 +44,8 @@ | ||
| 44 | 44 | require_once plugin_dir_path( __FILE__ ) . '/Otto_pixel_class.php'; |
| 45 | 45 | require_once plugin_dir_path( __FILE__ ) . '/metasync-otto-seo-functions.php'; |
| 46 | 46 | require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-transient-cache.php'; |
| 47 | 47 | require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-job-status.php'; |
| 48 | -require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-webhook-audit.php'; | |
| 49 | 48 | require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-render-strategy.php'; |
| 50 | 49 | require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-config.php'; |
| 51 | 50 | require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-bot-detector.php'; |
| 52 | 51 | require_once plugin_dir_path( __FILE__ ) . '/class-metasync-otto-bot-statistics-database.php'; |
| @@ -114,9 +113,8 @@ | ||
| 114 | 113 | # OPTIMIZED: use cached uuid |
| 115 | 114 | $otto_tag = '<meta name="otto" content="uuid='.esc_attr(Metasync_Otto_Config::get_otto_uuid()).'; type=wordpress; enabled='.esc_attr($string_enabled).'; version='.esc_attr($plugin_version).'">'; |
| 116 | 115 | |
| 117 | 116 | # out the otto tag |
| 118 | - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- meta tag assembled on the line above with esc_attr() on every interpolated value | |
| 119 | 117 | echo $otto_tag; |
| 120 | 118 | }, 1); # Priority 1 to output early in head |
| 121 | 119 | |
| 122 | 120 | /** |
| @@ -167,13 +165,8 @@ | ||
| 167 | 165 | |
| 168 | 166 | # function to register the route |
| 169 | 167 | function metasync_otto_crawl_notify($request){ |
| 170 | 168 | |
| 171 | - # receipt time is captured before validation and scheduling, so the audit | |
| 172 | - # record describes when WordPress received the delivery, not when its work | |
| 173 | - # happened to finish. | |
| 174 | - $received_at = time(); | |
| 175 | - | |
| 176 | 169 | # get request data params |
| 177 | 170 | $data = $request->get_json_params(); |
| 178 | 171 | |
| 179 | 172 | # fields |
| @@ -193,29 +186,14 @@ | ||
| 193 | 186 | } |
| 194 | 187 | |
| 195 | 188 | } |
| 196 | 189 | |
| 197 | - # The audit trail is deliberately after payload validation: invalid requests | |
| 198 | - # receive the existing 400 response and are not recorded as deliveries. | |
| 199 | - # It is also before any scheduling so a valid delivery is captured even if | |
| 200 | - # later work is deferred or rejected. The failure guard is intentionally | |
| 201 | - # broader than Exception: a broken option adapter or stale plugin copy must | |
| 202 | - # never turn an otherwise acknowledged webhook into a 500 response. | |
| 203 | - try { | |
| 204 | - if (metasync_otto_class_provides('Metasync_Otto_Webhook_Audit', 'record')) { | |
| 205 | - Metasync_Otto_Webhook_Audit::record( | |
| 206 | - $received_at, | |
| 207 | - $data['domain'], | |
| 208 | - $data['urls'] | |
| 209 | - ); | |
| 210 | - } | |
| 211 | - } catch (Throwable $metasync_otto_audit_failure) { | |
| 212 | - error_log( | |
| 213 | - 'MetaSync OTTO: failed to record crawl-notify webhook audit entry: ' | |
| 214 | - . $metasync_otto_audit_failure->getMessage() | |
| 215 | - ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- audit failure must not break webhook acknowledgement | |
| 216 | - } | |
| 190 | + # load otto pixel | |
| 191 | + $otto_pixel = new Metasync_otto_pixel(false); | |
| 217 | 192 | |
| 193 | + # save the otto data first (cheap local DB write — kept synchronous) | |
| 194 | + $otto_pixel->save_crawl_data($data); | |
| 195 | + | |
| 218 | 196 | # Defer the expensive per-URL work (OTTO API fetch, post meta sync, |
| 219 | 197 | # per-URL cache purge) to background jobs. The webhook caller enforces a |
| 220 | 198 | # strict response-time budget, so the response acknowledges SCHEDULING, |
| 221 | 199 | # never execution. The per-URL status store records what actually |
| @@ -446,32 +424,10 @@ | ||
| 446 | 424 | } |
| 447 | 425 | |
| 448 | 426 | Metasync_Otto_Job_Status::record($route, Metasync_Otto_Job_Status::STATE_COMPLETED, 'job', $retry_count, $outcome); |
| 449 | 427 | |
| 450 | - } catch (\Throwable $e) { | |
| 451 | - # Cron must survive both Exception and PHP 7+ Error failures. Always use | |
| 452 | - # the same bounded retry path, and emit structured context without the URL | |
| 453 | - # payload or credentials. The retry is booked BEFORE the log attempt so a | |
| 454 | - # failing logger can never swallow the retry; logging is best-effort. | |
| 455 | - $schedule_retry('throwable:' . get_class($e)); | |
| 456 | - if (class_exists('Metasync_Error_Logger')) { | |
| 457 | - try { | |
| 458 | - Metasync_Error_Logger::log( | |
| 459 | - Metasync_Error_Logger::CATEGORY_OTTO_RENDER, | |
| 460 | - Metasync_Error_Logger::SEVERITY_ERROR, | |
| 461 | - 'OTTO crawl-url job threw; retry policy applied', | |
| 462 | - [ | |
| 463 | - 'operation' => 'crawl_url_job', | |
| 464 | - 'outcome' => 'throwable', | |
| 465 | - 'retry_count' => (int) $retry_count, | |
| 466 | - 'exception' => get_class($e), | |
| 467 | - ] | |
| 468 | - ); | |
| 469 | - } catch (\Throwable $logger_error) { | |
| 470 | - # Logging must never kill the handler after the retry is booked. | |
| 471 | - error_log('MetaSync OTTO: crawl-url job error log failed: ' . $logger_error->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets | |
| 472 | - } | |
| 473 | - } | |
| 428 | + } catch (Exception $e) { | |
| 429 | + $schedule_retry('exception:' . $e->getMessage()); | |
| 474 | 430 | } |
| 475 | 431 | } |
| 476 | 432 | |
| 477 | 433 | /** |
| @@ -933,9 +889,9 @@ | ||
| 933 | 889 | return; |
| 934 | 890 | } |
| 935 | 891 | |
| 936 | 892 | # OPTIMIZED: Check if Otto should be disabled for WP Rocket compatibility |
| 937 | - if (defined('WP_ROCKET_VERSION') || class_exists('WP_Rocket')) { | |
| 893 | + if (class_exists('WP_Rocket')) { | |
| 938 | 894 | $wp_rocket_compat_mode = Metasync_Otto_Config::get_wp_rocket_compat_mode(); |
| 939 | 895 | |
| 940 | 896 | if ($wp_rocket_compat_mode === 'disable_otto') { |
| 941 | 897 | return; # Exit early, Otto is disabled when WP Rocket is active |
| @@ -941,25 +897,14 @@ | ||
| 941 | 897 | return; # Exit early, Otto is disabled when WP Rocket is active |
| 942 | 898 | } |
| 943 | 899 | } |
| 944 | 900 | |
| 945 | - # Skip OTTO for Divi AJAX pagination, and for paginated archive requests | |
| 946 | - # on Divi-built sites. | |
| 947 | - # ?et_blog = Divi AJAX pagination callback — always skipped, on every | |
| 948 | - # theme, because Divi fires it regardless of the site layout. | |
| 901 | + # Skip OTTO for Divi AJAX pagination and paginated archive requests. | |
| 902 | + # ?et_blog = Divi AJAX pagination callback | |
| 949 | 903 | # /page/N/ = paginated blog/archive pages — OTTO's buffer/HTTP render causes |
| 950 | 904 | # module numbering mismatch between page 1 (with TB template) and page N |
| 951 | 905 | # (without TB template), breaking Divi's JS pagination selector matching. |
| 952 | - # That mismatch is a Divi rendering bug, so the /page/N/ skip is gated on | |
| 953 | - # Divi detection: non-Divi sites (Elementor + the7, ...) keep OTTO on real | |
| 954 | - # paginated archives. Detection covers the Divi theme and its child themes | |
| 955 | - # (get_template() returns the PARENT template dir, "Divi" either way), the | |
| 956 | - # Extra theme and the Divi Builder plugin (both define ET_BUILDER_VERSION | |
| 957 | - # / et_setup_theme — same TB machinery, same bug). | |
| 958 | - $is_divi = defined('ET_BUILDER_VERSION') | |
| 959 | - || function_exists('et_setup_theme') | |
| 960 | - || wp_get_theme()->get_template() === 'Divi'; | |
| 961 | - if (isset($_GET['et_blog']) || ($is_divi && is_paged() && !is_singular())) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only front-end check that skips OTTO | |
| 906 | + if (isset($_GET['et_blog']) || (is_paged() && !is_singular())) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only front-end check that skips OTTO | |
| 962 | 907 | return; |
| 963 | 908 | } |
| 964 | 909 | |
| 965 | 910 | # Skip OTTO on search-results pages. A search request carries its |
| @@ -1102,9 +1047,9 @@ | ||
| 1102 | 1047 | # Detect active plugins. SG Optimizer detection now lives in |
| 1103 | 1048 | # metasync_otto_disable_sg_page_cache(), which is invoked later |
| 1104 | 1049 | # once OTTO confirms suggestions for the URL. |
| 1105 | 1050 | $brizy_active = class_exists('Brizy_Editor') || defined('BRIZY_VERSION'); |
| 1106 | - $wp_rocket_active = defined('WP_ROCKET_VERSION') || class_exists('WP_Rocket'); | |
| 1051 | + $wp_rocket_active = class_exists('WP_Rocket'); | |
| 1107 | 1052 | |
| 1108 | 1053 | # OPTIMIZED: Get configuration option using cached config |
| 1109 | 1054 | $wp_rocket_compat_mode = Metasync_Otto_Config::get_wp_rocket_compat_mode(); |
| 1110 | 1055 | |
| @@ -1177,9 +1122,9 @@ | ||
| 1177 | 1122 | * Only meaningful on SiteGround sites without WP Rocket; a no-op otherwise. |
| 1178 | 1123 | */ |
| 1179 | 1124 | function metasync_otto_disable_sg_page_cache() { |
| 1180 | 1125 | # Only relevant when SG Optimizer is active and WP Rocket is not present. |
| 1181 | - if (defined('WP_ROCKET_VERSION') || class_exists('WP_Rocket')) { | |
| 1126 | + if (class_exists('WP_Rocket')) { | |
| 1182 | 1127 | return; |
| 1183 | 1128 | } |
| 1184 | 1129 | if (!is_plugin_active('sg-cachepress/sg-cachepress.php')) { |
| 1185 | 1130 | return; |
| @@ -1904,8 +1849,24 @@ | ||
| 1904 | 1849 | metasync_record_failed_action( 'metasync_process_seo_job' ); |
| 1905 | 1850 | return Metasync_Otto_Job_Status::OUTCOME_RETRYABLE; |
| 1906 | 1851 | } |
| 1907 | 1852 | } |
| 1853 | + | |
| 1854 | + # Mark this URL as crawled by OTTO for SSR | |
| 1855 | + # Extract domain and path from route | |
| 1856 | + $parsed_url = wp_parse_url($route); | |
| 1857 | + $domain_with_scheme = ($parsed_url['scheme'] ?? 'https') . '://' . ($parsed_url['host'] ?? ''); | |
| 1858 | + $url_path = ($parsed_url['path'] ?? '/'); | |
| 1859 | + | |
| 1860 | + # Create crawl data structure | |
| 1861 | + $crawl_data = array( | |
| 1862 | + 'domain' => $domain_with_scheme, | |
| 1863 | + 'urls' => array($url_path) | |
| 1864 | + ); | |
| 1865 | + | |
| 1866 | + # Load Otto pixel class and save crawl data | |
| 1867 | + $otto_pixel = new Metasync_otto_pixel($otto_uuid); | |
| 1868 | + $otto_pixel->save_crawl_data($crawl_data); | |
| 1908 | 1869 | |
| 1909 | 1870 | # Get WordPress post ID from URL |
| 1910 | 1871 | $post_id = url_to_postid($route); |
| 1911 | 1872 | |