debug( $line, array( 'source' => 'addsynapse-search' ) ); } if ( defined( 'ADDSYNAPSE_SEARCH_DEBUG' ) && ADDSYNAPSE_SEARCH_DEBUG ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- μόνο με ρητό ADDSYNAPSE_SEARCH_DEBUG. error_log( $line ); } } // -------------------------------------------------------------------- // Μεταφράσεις. // -------------------------------------------------------------------- /** * Εφεδρική φόρτωση των μεταφράσεων που συνοδεύουν το πακέτο (`languages/`). * * Σειρά προτεραιότητας (η πρώτη που υπάρχει κερδίζει): * 1. Language pack του translate.wordpress.org (WP_LANG_DIR/plugins/) — * το φορτώνει ο πυρήνας just-in-time· εδώ δεν κάνουμε τίποτα. * 2. Νεότερος πυρήνας δηλώνει μόνος του το `Domain Path` κάθε ενεργού plugin * (wp-settings.php → set_custom_path()), άρα το registry βρίσκει ήδη το * δικό μας .mo και το JIT το φορτώνει· πάλι δεν κάνουμε τίποτα. * 3. Αλλιώς (παλαιότερο WP χωρίς language pack): load_textdomain() στο δικό * μας .mo — ΟΧΙ load_plugin_textdomain(), που το Plugin Check το σημαδεύει * ως περιττό για plugins του wordpress.org. * * Στο `init` (όχι νωρίτερα): από WP 6.7 μετάφραση πριν το init βγάζει * `_load_textdomain_just_in_time` notice. Προτεραιότητα 0, ώστε να * προηγείται κάθε δικού μας string. * * @return void */ function addsynapse_search_load_bundled_translations() { $domain = 'addsynapse-search-filter'; if ( is_textdomain_loaded( $domain ) ) { return; } $locale = determine_locale(); if ( isset( $GLOBALS['wp_textdomain_registry'] ) && $GLOBALS['wp_textdomain_registry'] instanceof WP_Textdomain_Registry && false !== $GLOBALS['wp_textdomain_registry']->get( $domain, $locale ) ) { return; // Περιπτώσεις 1 και 2: το αναλαμβάνει ο πυρήνας. } $mofile = ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'languages/' . $domain . '-' . $locale . '.mo'; if ( is_readable( $mofile ) ) { load_textdomain( $domain, $mofile, $locale ); } } add_action( 'init', 'addsynapse_search_load_bundled_translations', 0 ); // -------------------------------------------------------------------- // Έλεγχος προαπαιτούμενων (WooCommerce, mbstring). // // 🔴 0.8.1 (review wordpress.org 05/10): ΚΑΝΕΝΑ σκληρό μπλοκάρισμα της // ενεργοποίησης. Πριν υπήρχαν ΔΥΟ: το header `Requires Plugins: woocommerce` // (ο πυρήνας ταιριάζει ΜΟΝΟ με όνομα φακέλου, ΜΟΝΟ κανονικά plugins — όχι // mu-plugins — και ελέγχει τη στιγμή της ενεργοποίησης) και ένα wp_die() στο // activation hook. Σε μαζική ενεργοποίηση («Activate» σε πολλά, `wp plugin // activate --all`, blueprint) το `addsynapse-…` ενεργοποιείται ΠΡΙΝ το // `woocommerce` (αλφαβητικά) → «requires WooCommerce» ενώ στο τέλος του ίδιου // αιτήματος το WooCommerce ήταν ενεργό. Τώρα: η ενεργοποίηση πετυχαίνει πάντα, // ο έλεγχος γίνεται σε ΚΑΘΕ φόρτωση (plugins_loaded, όταν έχουν φορτωθεί όλα // τα plugins — και mu/network), και αν κάτι λείπει το plugin μένει αδρανές με // admin notice που εξηγεί τι λείπει. // -------------------------------------------------------------------- /** Ελάχιστη έκδοση WooCommerce (ίδια με το header «WC requires at least»). */ define( 'ADDSYNAPSE_SEARCH_MIN_WC_VERSION', '8.0' ); /** * Αν το WooCommerce είναι ενεργό αυτή τη στιγμή. * * Αξιόπιστο ΜΟΝΟ από το plugins_loaded και μετά (όλα τα plugins έχουν φορτωθεί). * Πριν από αυτό ένα «false» σημαίνει απλώς «δεν έχει φορτωθεί ΑΚΟΜΑ» — π.χ. το * `addsynapse-…` φορτώνεται αλφαβητικά ΠΡΙΝ το `woocommerce`. * * @return bool */ function addsynapse_search_is_woocommerce_active() { return class_exists( 'WooCommerce', false ) || function_exists( 'WC' ); } /** * Τα προαπαιτούμενα που λείπουν, ως κωδικοί (mbstring|woocommerce|woocommerce_version). * * Κενός πίνακας = όλα εντάξει. 🔴 ΚΩΔΙΚΟΙ, όχι κείμενα: καλείται στο * plugins_loaded, και μια __() πριν το `init` βγάζει `_load_textdomain_just_in_time` * notice (WP 6.7+). Τα μηνύματα χτίζονται μόνο στο admin notice. * * 🔴 0.8.2 (review wordpress.org 08/10): ΚΑΝΕΝΑ cache. Το 0.8.1 κρατούσε το * αποτέλεσμα σε `static` και το πρώτο που το ρωτούσε ήταν το * Addsynapse_Search_Admin::init() (plugins_loaded:10, καταχωρημένο ήδη από το * include του αρχείου) — αν το WooCommerce δεν είχε φορτωθεί ακόμα εκείνη τη * στιγμή, το bootstrap (plugins_loaded:20) ξαναχρησιμοποιούσε το «λείπει» και το * plugin έμενε αδρανές. Ο έλεγχος είναι φθηνός (class_exists/function_exists): * υπολογίζεται ΚΑΘΕ φορά, και καλείται μόνο από plugins_loaded και μετά. * * @return string[] */ function addsynapse_search_requirement_errors() { $errors = array(); // mbstring: ο ελληνικός stemmer/μεταγραφή (mb_strtolower, mb_str_split) — // ο πυρήνας δίνει polyfill μόνο για mb_substr/mb_strlen. if ( ! function_exists( 'mb_strtolower' ) || ! function_exists( 'mb_str_split' ) ) { $errors[] = 'mbstring'; } if ( ! addsynapse_search_is_woocommerce_active() ) { $errors[] = 'woocommerce'; } elseif ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, ADDSYNAPSE_SEARCH_MIN_WC_VERSION, '<' ) ) { $errors[] = 'woocommerce_version'; } return $errors; } /** * Αν ικανοποιούνται όλα τα προαπαιτούμενα (βλ. addsynapse_search_requirement_errors()). * * @return bool */ function addsynapse_search_requirements_met() { return array() === addsynapse_search_requirement_errors(); } /** * Το μήνυμα για τον διαχειριστή ενός κωδικού προαπαιτούμενου. * * @param string $code Κωδικός από addsynapse_search_requirement_errors(). * @return string */ function addsynapse_search_requirement_message( $code ) { switch ( $code ) { case 'mbstring': return __( 'AddSynapse – Live Product Search & Filters for WooCommerce needs the PHP "mbstring" extension, which is not enabled on this server. Please ask your hosting provider to enable it; until then this plugin stays inactive.', 'addsynapse-search-filter' ); case 'woocommerce_version': return sprintf( /* translators: 1: required WooCommerce version, 2: installed WooCommerce version */ __( 'AddSynapse – Live Product Search & Filters for WooCommerce needs WooCommerce %1$s or newer (installed: %2$s). Please update WooCommerce; until then this plugin stays inactive.', 'addsynapse-search-filter' ), ADDSYNAPSE_SEARCH_MIN_WC_VERSION, defined( 'WC_VERSION' ) ? WC_VERSION : '?' ); default: return __( 'AddSynapse – Live Product Search & Filters for WooCommerce needs WooCommerce. Please install and activate WooCommerce; until then this plugin stays inactive and changes nothing on your site.', 'addsynapse-search-filter' ); } } /** * Admin notice όταν λείπει κάποιο προαπαιτούμενο. * * @return void */ function addsynapse_search_requirements_notice() { if ( ! current_user_can( 'activate_plugins' ) ) { return; } foreach ( addsynapse_search_requirement_errors() as $addsynapse_search_code ) { printf( '

%s

', esc_html( addsynapse_search_requirement_message( $addsynapse_search_code ) ) ); } } // -------------------------------------------------------------------- // Φόρτωση includes. // // Φορτώνονται πάντα (ανεξάρτητα από το αν το WooCommerce είναι ενεργό αυτή τη // στιγμή), ώστε οι κλάσεις να υπάρχουν· το πραγματικό «άναμμα» των hooks γίνεται // μόνο μέσα από addsynapse_search_bootstrap(), αφού επιβεβαιωθεί ότι υπάρχει WooCommerce. // -------------------------------------------------------------------- require_once ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'includes/class-addsynapse-search-settings.php'; require_once ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'includes/class-addsynapse-search-api-client.php'; require_once ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'includes/class-addsynapse-search-license.php'; require_once ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'includes/class-addsynapse-search-indexer.php'; require_once ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'includes/class-addsynapse-search-plugin.php'; // Οποιαδήποτε άλλη class-addsynapse-search-*.php στο includes/ (π.χ. transliteration helper, // frontend attach, shortcode, block, analytics) φορτώνεται αυτόματα εδώ — // require_once προστατεύει από διπλή φόρτωση των παραπάνω. // glob() δίνει false σε σφάλμα (π.χ. open_basedir) — (array) false θα έδινε array( false ). $addsynapse_search_files = glob( ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'includes/class-addsynapse-search-*.php' ); foreach ( is_array( $addsynapse_search_files ) ? $addsynapse_search_files : array() as $addsynapse_search_include_file ) { require_once $addsynapse_search_include_file; } unset( $addsynapse_search_include_file, $addsynapse_search_files ); // Το admin UI ζει σε ξεχωριστό φάκελο (admin/)· ΔΕΝ αυτο-εκκινείται (0.8.2) — το // ξεκινά το addsynapse_search_bootstrap() στο plugins_loaded, μόνο σε admin. // glob() δίνει false σε σφάλμα (π.χ. open_basedir) — (array) false θα έδινε array( false ). $addsynapse_search_files = glob( ADDSYNAPSE_SEARCH_PLUGIN_DIR . 'admin/class-addsynapse-search-*.php' ); foreach ( is_array( $addsynapse_search_files ) ? $addsynapse_search_files : array() as $addsynapse_search_admin_file ) { require_once $addsynapse_search_admin_file; } unset( $addsynapse_search_admin_file, $addsynapse_search_files ); // -------------------------------------------------------------------- // HPOS (High-Performance Order Storage) compatibility. // -------------------------------------------------------------------- add_action( 'before_woocommerce_init', function () { if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', ADDSYNAPSE_SEARCH_PLUGIN_FILE, true ); } } ); // -------------------------------------------------------------------- // Bootstrap. // -------------------------------------------------------------------- /** * Ξεκινά το plugin, μόνο αν ικανοποιούνται τα προαπαιτούμενα. * * Αλλιώς: κανένα hook, κανένα asset, καμία κλήση δικτύου — μόνο ένα admin * notice που λέει τι λείπει (και στο network admin, για network activation). * * 🔴 0.8.2: το ΜΟΝΑΔΙΚΟ σημείο εκκίνησης — και του admin UI (πριν αυτο-εκκινούνταν * από το include του αρχείου του, στο plugins_loaded:10, και «κλείδωνε» νωρίς το * «λείπει το WooCommerce»). Τρέχει ΜΟΝΟ από το plugins_loaded και μετά· στο include * του αρχείου γίνονται μόνο define/require/καταχωρήσεις hooks. * * @return void */ function addsynapse_search_bootstrap() { static $started = false; // Μόνο «ξεκίνησε ήδη» — ΠΟΤΕ cache αποτυχίας. if ( $started || ! did_action( 'plugins_loaded' ) ) { return; } if ( ! addsynapse_search_requirements_met() ) { // Το notice ξαναελέγχει τη στιγμή που τυπώνεται (χωρίς cache)· αν το // WooCommerce φορτωθεί αργότερα στο ίδιο αίτημα, δεν τυπώνει τίποτα. if ( ! has_action( 'admin_notices', 'addsynapse_search_requirements_notice' ) ) { add_action( 'admin_notices', 'addsynapse_search_requirements_notice' ); add_action( 'network_admin_notices', 'addsynapse_search_requirements_notice' ); } return; } $started = true; Addsynapse_Search_Plugin::instance(); if ( is_admin() ) { Addsynapse_Search_Admin::init(); } } add_action( 'plugins_loaded', 'addsynapse_search_bootstrap', 20 ); /** * Δίχτυ ασφαλείας: WooCommerce που φορτώνεται ΑΡΓΑ (από loader μέσα στο * plugins_loaded, μετά τη δική μας προτεραιότητα 20) — ξαναδοκιμάζει τη στιγμή * που το WooCommerce δηλώνει ότι φορτώθηκε, αρκεί να μην έχει περάσει το `init` * (εκεί καταχωρούνται τα υπόλοιπα hooks μας). Σε κανονική σειρά το * `woocommerce_loaded` τρέχει πριν το plugins_loaded και αυτό δεν κάνει τίποτα. * * @return void */ function addsynapse_search_bootstrap_late() { if ( ! did_action( 'init' ) ) { addsynapse_search_bootstrap(); } } add_action( 'woocommerce_loaded', 'addsynapse_search_bootstrap_late', 99 ); // Αναβάθμιση σχήματος ρυθμίσεων (0.4.1: παλιές εγκαταστάσεις κρατούν simple). add_action( 'plugins_loaded', array( 'Addsynapse_Search_Settings', 'maybe_upgrade' ), 6 ); // -------------------------------------------------------------------- // Activation / deactivation. // -------------------------------------------------------------------- /** * Activation hook: ΜΟΝΟ τοπική προετοιμασία — δεν μπλοκάρει ποτέ, δεν τυπώνει * τίποτα, δεν επικοινωνεί με κανέναν server (Guideline 7). * * 🔴 Χωρίς έλεγχο WooCommerce εδώ (βλ. «Έλεγχος προαπαιτούμενων» πιο πάνω): σε * μαζική ενεργοποίηση το WooCommerce μπορεί να μην έχει φορτωθεί ακόμα στο * ίδιο αίτημα. Ο έλεγχος γίνεται σε κάθε φόρτωση (addsynapse_search_bootstrap()). * Τίποτα από τα παρακάτω δεν χρειάζεται WooCommerce: ρυθμίσεις = options, και * το νυχτερινό προγραμματίζεται μόνο αν υπάρχει Action Scheduler ΚΑΙ σύνδεση. * * @return void */ function addsynapse_search_activate_plugin() { Addsynapse_Search_Settings::maybe_upgrade(); Addsynapse_Search_Plugin::activate(); } register_activation_hook( __FILE__, 'addsynapse_search_activate_plugin' ); /** * Deactivation hook. * * @return void */ function addsynapse_search_deactivate_plugin() { Addsynapse_Search_Plugin::deactivate(); } register_deactivation_hook( __FILE__, 'addsynapse_search_deactivate_plugin' );