'page', 'post_status' => array('publish', 'draft', 'private'), 'meta_key' => self::META_KEY, 'meta_value' => '1', 'posts_per_page' => 1, 'fields' => 'ids', 'no_found_rows' => true, )); if (!empty($pages) && is_array($pages)) { return (int) $pages[0]; } return 0; } /** * Check whether the system page exists and is published. * * @return bool */ public function systemPageExists(): bool { $pageId = $this->getSystemPageId(); if ($pageId <= 0) { return false; } return get_post_status($pageId) === 'publish'; } /** * Get or create the system page. If the page already exists and is published, * return its ID. Otherwise, create a new one. * * @return int The page ID, or 0 on failure. */ public function getOrCreateSystemPage(): int { $existingId = $this->getSystemPageId(); if ($existingId > 0 && get_post_status($existingId) === 'publish') { return $existingId; } // If the page exists but is trashed/drafted, try to republish it if ($existingId > 0) { $result = wp_update_post(array( 'ID' => $existingId, 'post_status' => 'publish', ), true); if (!is_wp_error($result)) { return $existingId; } } return $this->createSystemPage(); } /** * Create a fresh system page with the shortcode. * * @return int The new page ID, or 0 on failure. */ public function createSystemPage(): int { $title = __('Page Not Found', '404-solution'); $pageId = wp_insert_post(array( 'post_title' => $title, 'post_content' => '[abj404_solution_page_suggestions]', 'post_status' => 'publish', 'post_type' => 'page', 'post_author' => get_current_user_id() ?: 1, 'comment_status' => 'closed', 'ping_status' => 'closed', ), true); if (is_wp_error($pageId)) { return 0; } // Tag with system page meta update_post_meta($pageId, self::META_KEY, '1'); // Exclude from sitemaps update_post_meta($pageId, '_yoast_wpseo_meta-robots-noindex', '1'); update_post_meta($pageId, 'rank_math_robots', array('noindex')); return (int) $pageId; } /** * Delete the system page permanently. * * @return bool True if deleted, false otherwise. */ public function deleteSystemPage(): bool { $pageId = $this->getSystemPageId(); if ($pageId <= 0) { return false; } $result = wp_delete_post($pageId, true); return $result !== false && $result !== null; } /** * Check if a given post ID is the system page. * * @param int $postId * @return bool */ public static function isSystemPage(int $postId): bool { if ($postId <= 0) { return false; } return get_post_meta($postId, self::META_KEY, true) === '1'; } /** * When the system page is deleted or trashed externally, flip the behavior * setting to 'theme_default' and set a transient for the admin notice. * * @return void */ public function handleSystemPageDeleted(): void { $logic = abj_service('plugin_logic'); $options = $logic->getOptions(true); if (isset($options['dest404_behavior']) && $options['dest404_behavior'] === 'suggest') { $options['dest404_behavior'] = 'theme_default'; $options['dest404page'] = '0|' . ABJ404_TYPE_404_DISPLAYED; $logic->updateOptions($options); set_transient('abj404_system_page_deleted', '1', DAY_IN_SECONDS); } } /** * On each 404 hit, verify that the system page still exists when behavior is 'suggest'. * This catches bulk deletes, DB restores, cleanup plugins, etc. * * @return void */ public function verifySystemPageOnRequest(): void { $logic = abj_service('plugin_logic'); $options = $logic->getOptions(true); if (!isset($options['dest404_behavior']) || $options['dest404_behavior'] !== 'suggest') { return; } if (!$this->systemPageExists()) { $this->handleSystemPageDeleted(); } } /** * Hook: before_delete_post / wp_trash_post — detect when system page is trashed/deleted. * * @param int $postId * @return void */ public static function onPostDeleteOrTrash(int $postId): void { if (!self::isSystemPage($postId)) { return; } $instance = self::getInstance(); $instance->handleSystemPageDeleted(); } /** * Hook: admin_notices on plugin settings page — show notice if system page was deleted. * * @return void */ public static function maybeShowDeletedPageNotice(): void { if (get_transient('abj404_system_page_deleted') !== '1') { return; } // Only show on our plugin's settings page $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : ''; if ($page !== ABJ404_PP) { return; } $settingsUrl = admin_url('options-general.php?page=' . ABJ404_PP . '&subpage=abj404_options'); $recreateUrl = wp_nonce_url( add_query_arg('abj404_recreate_system_page', '1', $settingsUrl), 'abj404_recreate_system_page' ); echo '
'; echo esc_html__('Your 404 suggestion page was deleted.', '404-solution'); echo ' ' . esc_html__('Recreate it', '404-solution') . ''; echo ' ' . esc_html__('or choose a different option below.', '404-solution'); echo '
'; echo ''; echo esc_html__('This page is used by 404 Solution to display suggested pages to visitors.', '404-solution'); echo ' ' . esc_html__('Learn more', '404-solution') . ''; echo '
'; echo '