| 1 |
<?php |
| 2 |
|
| 3 |
|
| 4 |
if (!defined('ABSPATH')) { |
| 5 |
exit; |
| 6 |
} |
| 7 |
|
| 8 |
// Constants |
| 9 |
if (!defined('ABJ404_AUTHOR_EMAIL')) { define( 'ABJ404_AUTHOR_EMAIL', '404solution@ajexperience.com' ); } |
| 10 |
// Default endpoint for the FeedbackTransport HTTP reports POST. Site owners |
| 11 |
// and tests can override via the 'abj404_report_endpoint' filter. |
| 12 |
if (!defined('ABJ404_REPORT_ENDPOINT')) { define( 'ABJ404_REPORT_ENDPOINT', 'https://404solution.ajexperience.com/api/v1/reports' ); } |
| 13 |
/* plugin_dir_url( __FILE__ ) */ |
| 14 |
if (!defined('ABJ404_URL')) { define( 'ABJ404_URL', plugin_dir_url(ABJ404_FILE)); } // https://site-url/wp-content/plugins/404-solution/ |
| 15 |
|
| 16 |
/** plugin_dir_path( __FILE__ ) */ |
| 17 |
if (!defined('ABJ404_NAME')) { define( 'ABJ404_NAME', plugin_basename(ABJ404_FILE)); } // wp-content/plugins/404-solution/404-solution.php |
| 18 |
if (!defined('ABJ404_SOLUTION_BASENAME')) { |
| 19 |
define('ABJ404_SOLUTION_BASENAME', function_exists('plugin_basename') ? plugin_basename(ABJ404_FILE) : |
| 20 |
basename(dirname(ABJ404_FILE)) . '/' . basename(ABJ404_FILE)); |
| 21 |
} |
| 22 |
|
| 23 |
// Read version from main plugin file header (single source of truth) |
| 24 |
$abj404_plugin_data = get_file_data(ABJ404_FILE, array('Version' => 'Version')); |
| 25 |
if (!defined('ABJ404_VERSION')) { define('ABJ404_VERSION', $abj404_plugin_data['Version']); } |
| 26 |
if (!defined('URL_TRACKING_SUFFIX')) { define( 'URL_TRACKING_SUFFIX', '?utm_source=404SolutionPlugin&utm_medium=WordPress'); } |
| 27 |
if (!defined('ABJ404_HOME_URL')) { define( 'ABJ404_HOME_URL', 'https://www.ajexperience.com/404-solution/' . URL_TRACKING_SUFFIX); } |
| 28 |
if (!defined('ABJ404_FC_URL')) { define( 'ABJ404_FC_URL', 'https://www.ajexperience.com/' . URL_TRACKING_SUFFIX); } |
| 29 |
if (!defined('PLUGIN_NAME')) { define( 'PLUGIN_NAME', '404 Solution'); } |
| 30 |
|
| 31 |
// STATUS types |
| 32 |
if (!defined('ABJ404_TRASH_FILTER')) { define( 'ABJ404_TRASH_FILTER', -1 ); } |
| 33 |
if (!defined('ABJ404_HANDLED_FILTER')) { define( 'ABJ404_HANDLED_FILTER', -2 ); } |
| 34 |
if (!defined('ABJ404_STATUS_MANUAL')) { define( 'ABJ404_STATUS_MANUAL', 1 ); } |
| 35 |
if (!defined('ABJ404_STATUS_AUTO')) { define( 'ABJ404_STATUS_AUTO', 2 ); } |
| 36 |
if (!defined('ABJ404_STATUS_CAPTURED')) { define( 'ABJ404_STATUS_CAPTURED', 3 ); } |
| 37 |
if (!defined('ABJ404_STATUS_IGNORED')) { define( 'ABJ404_STATUS_IGNORED', 4 ); } |
| 38 |
if (!defined('ABJ404_STATUS_LATER')) { define( 'ABJ404_STATUS_LATER', 5 ); } |
| 39 |
if (!defined('ABJ404_STATUS_REGEX')) { define( 'ABJ404_STATUS_REGEX', 6 ); } |
| 40 |
// Note: TRASH is handled via the 'disabled' column, not status. This constant exists for |
| 41 |
// backward compatibility with UninstallModal.php. Value 0 ensures 'status != 0' returns all rows. |
| 42 |
if (!defined('ABJ404_STATUS_TRASH')) { define( 'ABJ404_STATUS_TRASH', 0 ); } |
| 43 |
|
| 44 |
// Redirect types |
| 45 |
if (!defined('ABJ404_TYPE_404_DISPLAYED')) { define( 'ABJ404_TYPE_404_DISPLAYED', 0 ); } |
| 46 |
if (!defined('ABJ404_TYPE_POST')) { define( 'ABJ404_TYPE_POST', 1 ); } |
| 47 |
if (!defined('ABJ404_TYPE_CAT')) { define( 'ABJ404_TYPE_CAT', 2 ); } |
| 48 |
if (!defined('ABJ404_TYPE_TAG')) { define( 'ABJ404_TYPE_TAG', 3 ); } |
| 49 |
if (!defined('ABJ404_TYPE_EXTERNAL')) { define( 'ABJ404_TYPE_EXTERNAL', 4 ); } |
| 50 |
if (!defined('ABJ404_TYPE_HOME')) { define( 'ABJ404_TYPE_HOME', 5 ); } |
| 51 |
|
| 52 |
// Use $GLOBALS so these are in the true PHP global scope even when Loader.php |
| 53 |
// is require_once'd from inside a function or closure (e.g. the rest_api_init callback). |
| 54 |
$GLOBALS['abj404_redirect_types'] = array(ABJ404_STATUS_MANUAL, ABJ404_STATUS_AUTO, ABJ404_STATUS_REGEX); |
| 55 |
$GLOBALS['abj404_captured_types'] = array(ABJ404_STATUS_CAPTURED, ABJ404_STATUS_IGNORED, ABJ404_STATUS_LATER); |
| 56 |
// Also set as local-scope variables for backward compatibility when included at file scope. |
| 57 |
$abj404_redirect_types = $GLOBALS['abj404_redirect_types']; |
| 58 |
$abj404_captured_types = $GLOBALS['abj404_captured_types']; |
| 59 |
|
| 60 |
// other |
| 61 |
if (!defined("ABJ404_OPTION_DEFAULT_PERPAGE")) { define("ABJ404_OPTION_DEFAULT_PERPAGE", 25); } |
| 62 |
if (!defined("ABJ404_OPTION_MIN_PERPAGE")) { define("ABJ404_OPTION_MIN_PERPAGE", 3); } |
| 63 |
if (!defined("ABJ404_OPTION_MAX_PERPAGE")) { define("ABJ404_OPTION_MAX_PERPAGE", 500); } |
| 64 |
if (!defined("ABJ404_MAX_AJAX_DROPDOWN_SIZE")) { define("ABJ404_MAX_AJAX_DROPDOWN_SIZE", 500); } |
| 65 |
if (!defined("ABJ404_MAX_URL_LENGTH")) { define("ABJ404_MAX_URL_LENGTH", 4096); } |
| 66 |
|
| 67 |
// Load the bootstrap file which contains the service initialization function |
| 68 |
require_once(__DIR__ . '/bootstrap.php'); |
| 69 |
|
| 70 |
// Optional runtime query-budget instrumentation. No-op unless |
| 71 |
// ABJ404_QUERY_BUDGET_LOG env var or constant points to a writable |
| 72 |
// directory. Implements the deliverable of technique 4 in |
| 73 |
// docs/PROACTIVE_BUG_DISCOVERY.md ("reverse-proxy timeout budget audit"). |
| 74 |
require_once(__DIR__ . '/diagnostics/QueryBudgetInstrumentation.php'); |
| 75 |
|
| 76 |
// Initialize the service container |
| 77 |
// This sets up dependency injection for all core services |
| 78 |
abj_404_solution_init_services(); |
| 79 |
|
| 80 |
// always include |
| 81 |
ABJ_404_Solution_ErrorHandler::init(); |
| 82 |
|
| 83 |
// SlugChangeHandler registers the save_post hook which must fire whenever a post |
| 84 |
// is saved — including via WP-CLI (wp post update) and REST API, not only in admin. |
| 85 |
// Moving it outside is_admin() ensures auto-redirects are created in all contexts. |
| 86 |
ABJ_404_Solution_SlugChangeHandler::init(); |
| 87 |
|
| 88 |
// Keep the redirects denorm display columns (dest_for_view / published_status) |
| 89 |
// fresh when a targeted post or term changes. Registered in all contexts |
| 90 |
// (admin, WP-CLI, REST) because content changes outside wp-admin too. (Step 3c.) |
| 91 |
ABJ_404_Solution_RedirectsDenormContentHooks::init(); |
| 92 |
|
| 93 |
// System page hooks must fire on both admin and frontend |
| 94 |
ABJ_404_Solution_SystemPage::registerHooks(); |
| 95 |
|
| 96 |
if (is_admin()) { |
| 97 |
ABJ_404_Solution_PermalinkCache::init(); |
| 98 |
ABJ_404_Solution_SpellChecker::init(); |
| 99 |
ABJ_404_Solution_PostEditorIntegration::init(); |
| 100 |
|
| 101 |
// Get services from the container instead of using getInstance() |
| 102 |
// Keeping the global variables for backward compatibility during migration |
| 103 |
$abj404view = abj_service('view'); |
| 104 |
$abj404viewSuggestions = abj_service('view_suggestions'); |
| 105 |
} |
| 106 |
|