set_basename( false, __FILE__ ); return; } // This condition looks redundant after the return above, and it must stay: without it the // function below is unconditional and PHP declares it while compiling the file, before the // return runs, which is exactly the "Cannot redeclare" fatal of the second copy. // @phpstan-ignore booleanNot.alwaysTrue if ( ! function_exists( 'cloakpilot_fs' ) ) { // Create a helper function for easy SDK access. function cloakpilot_fs() { global $cloakpilot_fs; if ( ! isset( $cloakpilot_fs ) ) { // Include Freemius SDK. require_once dirname( __FILE__ ) . '/vendor/freemius/start.php'; $cloakpilot_fs = fs_dynamic_init( array( 'id' => '34258', // 'slug' is Freemius' own identifier for this product (used for its update // mechanism and internal URLs) - it is NOT required to match the plugin's // folder name (cloakpilot) or its text domain. 'cloakpilot' here was copied // verbatim from the real ready-to-paste snippet Freemius generated when the // live product (id 34258) was created on 29.8.2026, so it almost certainly // matches what is already saved as this product's slug in the Freemius // dashboard. Left unchanged on purpose: overwriting it here without also // checking/changing the dashboard value first would risk creating a NEW // mismatch instead of fixing one, and there is no way to read the dashboard's // current value from this offline environment tonight. Verify in the morning // (Freemius dashboard -> product settings -> slug) before the first real // update ships; only change this line if the dashboard disagrees. 'slug' => 'cloakpilot', // Was 'linkpilot-premium' - a leftover from the product's previous name. Not // tied to the live account (no separate premium zip is ever built or uploaded; // this codebase uses a single file gated at runtime, see includes/class-cloakpilot-plans.php), // so renaming it is a safe, purely cosmetic cleanup with no dependency on the // dashboard's current configuration. 'premium_slug' => 'cloakpilot-premium', 'type' => 'plugin', 'public_key' => 'pk_9a9a7c304331035406c3210f82f50', 'is_premium' => false, 'has_premium_version' => true, 'has_addons' => false, 'has_paid_plans' => true, // Deliberate, not a leftover: CloakPilot is sold independently today, but a // free tier on wp.org remains a possible future distribution channel (see // BRIEF.md, "ערוץ הפצה"). Keeping this compliant now avoids a rework later. 'is_org_compliant' => true, 'trial' => array( 'days' => 14, 'is_require_payment' => false, ), // Without 'menu.slug', Freemius does not register a menu at all (its account/ // pricing/contact pages become unreachable from the admin, not just hidden - // verified by reading vendor/freemius/includes/managers/class-fs-admin-menu-manager.php, // init(): $_menu_exists is only true when 'slug' is set). Without 'menu.parent', // a menu IS created but as a brand-new top-level entry - and the CPT registration // in class-cloakpilot-cpt.php already creates a top-level menu literally named // "CloakPilot" (edit.php?post_type=cloakpilot_link), so the result would be two // identically-named top-level menu items, which is confusing. Nesting Freemius' // pages as extra submenu entries under the SAME existing "CloakPilot" menu (the // one that already holds Links/Reports/Settings/Import) is the supported pattern // for a plugin whose own admin UI is not Freemius-generated. Not visually verified // on a live site tonight - see BUILD-LICENSE-LAYER.md. 'menu' => array( 'slug' => 'cloakpilot', // 'cloakpilot_link' is the CPT slug (CloakPilot_CPT::POST_TYPE) hardcoded here on purpose: // this array is built before includes/class-cloakpilot-cpt.php is loaded, so the // constant does not exist yet at this point in the file. 'parent' => array( 'slug' => 'edit.php?post_type=cloakpilot_link' ), 'support' => false, // 19.9.2026 (owner: "it added items to the menu"): the SDK's own submenu // entries stay out of the CloakPilot menu. A page whose flag is false is // registered without a parent (add_submenu_items() in // vendor/freemius/includes/class-freemius.php), and under a CPT parent // WordPress then answers "Sorry, you are not allowed to access this page" // (measured live on motiongraphics.co.il, 19.9.2026, page=cloakpilot-contact). // So the flag is false only for pages nothing links to: 'contact' is Freemius' // hosted contact form (the plugin has its own Send feedback dialog). 'pricing' was false // for a few hours on 19.9.2026 while the plans were unreleased in the Freemius // dashboard (the in-dashboard pricing app rendered an empty page then); the // plans were released the same day, so the page has content again and the // License tab links to it. Its menu entry ("Upgrade") is hidden by CSS in // CloakPilot_License::hide_menu_item(), like 'account': a page whose flag is // false is registered without a parent and WordPress answers "not allowed" // for it under a CPT parent (measured live), so pages the tab links to must // stay registered. 'contact' => false, ), ) ); } return $cloakpilot_fs; } // Init Freemius. cloakpilot_fs(); // Signal that SDK was initiated. do_action( 'cloakpilot_fs_loaded' ); // WordPress.org build only (1.0.5, guideline 8): no Freemius trial promotion there. That build // has no paid code, so a trial started from it would lead to installing the paid copy from inside // the plugin; its screens point to the switch steps instead. tools/wporg/strip-premium.php turns // the marker below into the Freemius "show_trial" filter returning false, in the free build only. // The full build keeps the promotion (the SDK hides it there anyway). cloakpilot_fs()->add_filter( 'show_trial', '__return_false' ); // ‏ניקוי ההסרה (1.0.0-rc.1, ממצא REL-01): היה uninstall.php, ש-Freemius דוחה בחבילה. עכשיו // ‏פונקציה שרצה בפעולה after_uninstall של Freemius. השומר: אם ה-SDK לא עלה, אין רישום ואין // ‏שגיאה קטלנית (וההסרה עצמה של וורדפרס ממשיכה כרגיל). require_once dirname( __FILE__ ) . '/includes/uninstall-cleanup.php'; if ( is_object( cloakpilot_fs() ) && method_exists( cloakpilot_fs(), 'add_action' ) ) { cloakpilot_fs()->add_action( 'after_uninstall', 'cloakpilot_uninstall_cleanup' ); } } /* * CRITICAL FIX (2026-08-30, licensing-layer build): this used to be * `if ( function_exists( 'cloakpilot_fs' ) ) { set_basename(...); } else { ...load the entire * plugin... }`. That looked like a fallback ("use Freemius if available, otherwise load * our own code"), but it never behaved that way: `cloakpilot_fs` is a plain function declared a * few lines above, unconditionally, the moment this file runs at all - so * `function_exists( 'cloakpilot_fs' )` is always true from this point on, regardless of whether * `fs_dynamic_init()` actually succeeded. The `else` branch, which contained every * `require_once` for the plugin's real functionality (CPT, redirects, REST, the settings * screen - all of it), was therefore dead code: with the Freemius SDK wired in, CloakPilot * itself never loaded at all. Verified by reading this file's own control flow, not * inferred. Fixed by de-coupling the two, and the plugin's own bootstrap below always runs. * * P1 (8.11.2026): the set_basename( true, __FILE__ ) call that stood here ran on every load, * including the WordPress.org build, where it told Freemius that the free copy itself is the * paid version (premium_plugin_basename() returned cloakpilot/cloakpilot.php, measured on * Playground). Freemius' upgrade notice then offered to "activate" the copy that was already * active instead of explaining how to get the paid one. The call now lives only in the * second-copy branch at the top of this file, where Freemius expects it. */ if ( ! defined( 'ABSPATH' ) ) { exit; } define( 'CLOAKPILOT_VERSION', '1.0.5' ); // Release history: CHANGELOG.md (kept in the repository, not in the zip). define( 'CLOAKPILOT_FILE', __FILE__ ); define( 'CLOAKPILOT_DIR', plugin_dir_path( __FILE__ ) ); define( 'CLOAKPILOT_URL', plugin_dir_url( __FILE__ ) ); define( 'CLOAKPILOT_BASENAME', plugin_basename( __FILE__ ) ); // ברירת מחדל לתחילית ה-URL. חייבת להישאר "go" לתאימות לאחור עם קישורי ThirstyAffiliates. if ( ! defined( 'CLOAKPILOT_LINK_PREFIX' ) ) { define( 'CLOAKPILOT_LINK_PREFIX', 'go' ); } // ‏פיד העדכונים של התוסף עצמו - רק בבנייה המלאה (cloakpilot.net / Freemius). בבנייה ל-WordPress.org // ‏הבלוק הזה, מחלקת בודק העדכונים והכותרת Update URI אינם קיימים: WordPress.org מגיש את העדכונים // ‏שם, וההנחיות שלו (סעיף 8) אוסרות עדכון משרת אחר. // ‏פיד העדכונים של התוסף עצמו (הודלק ב-1.0.0-rc.1, 18.10.2026, הכרעת מוטי על גישה מוקדמת פתוחה). // ‏למה לא רק Freemius: Freemius מוסר עדכון של חבילת premium רק להתקנה מחוברת לרישיון, ובגישה // ‏המוקדמת רוב ההתקנות אינן מחוברות - בלי הפיד הזה הן לא היו מקבלות תיקונים. מה קורה כששני // ‏המנגנונים פועלים יחד: ראו ההערה ב-CloakPilot_Update_Checker::__construct(). // ‏המארח של קובץ ה-zip עצמו חייב להופיע ב-ALLOWED_PACKAGE_HOSTS, אחרת כל הפיד נדחה. // ‏גיבויים ל-mbstring לפני כל מחלקה (ראו הקובץ). באתר רגיל אינו משנה דבר. require_once CLOAKPILOT_DIR . 'includes/compat.php'; // ‏הגירה חד-פעמית של נתונים שנשמרו תחת שם הקוד הישן (mg-links / mgl_). רצה לפני // ‏שהתוסף קורא הגדרה כלשהי, ורק כשיש נתונים ישנים - אחרת קריאה אחת ל-get_option. require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-legacy.php'; if ( 'busy' === CloakPilot_Legacy::maybe_migrate() ) { // ‏בקשה אחרת מריצה עכשיו את ההגירה: לא טוענים קוד חדש מעל נתונים חצי-מומרים. return; } require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-plugin.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-cpt.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-vendors.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-redirect.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-clicks.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-autolink.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-reports.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-rest.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-import.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-vendor-desc.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-credentials.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-positions.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-links-screen.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-categories-screen.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-gone.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-wizard.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-ui.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-locale.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-cli.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-cleanup.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-feedback.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-send-feedback.php'; // ‏היכולות בתשלום: רק בבנייה המלאה. הבנייה ל-WordPress.org (build.sh --free) מוחקת את הבלוק הזה ואת // ‏הקבצים עצמם (רשימת @fs_premium_only בכותרת), כדי שהעותק שם לא יכיל קוד נעול (סעיף 5 בהנחיות). // ‏הסדר בתוך הבלוק חשוב: ממשק הספקים והמחלקה הבסיסית לפני הספקים, והמרשם אחריהם. // שכבת הרישוי (30.8.2026): mapping יכולת->מסלול + cloakpilot_can_use(), ומסך "License". // CloakPilot_License נטען בלי תלות ב-CloakPilot_Plans כדי שהמסך יוכל להציג מצב גם אם המיפוי // עצמו עוד לא הוכרע - ראה BUILD-LICENSE-LAYER.md. require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-plans.php'; require_once CLOAKPILOT_DIR . 'includes/class-cloakpilot-license.php'; // ‏אכיפת המסלולים (1.0.0-rc.2, 19.10.2026). שלושת הקבועים הם ההחלטה היחידה שנשארת לקוד, // ‏ואין להם ממשק: לקוח אינו יכול לכבות את האכיפה. CLOAKPILOT_PLANS_ENFORCED=false קיים // ‏רק לבניית פיתוח. תקופת החסד חלה על אתר שעודכן מגרסת הגישה המוקדמת (ראו // ‏CloakPilot_Plans::maybe_schedule_enforcement); התאריך הקשיח (ריק = אין) סוגר אותה בכל מקרה. if ( ! defined( 'CLOAKPILOT_PLANS_ENFORCED' ) ) { define( 'CLOAKPILOT_PLANS_ENFORCED', true ); } if ( ! defined( 'CLOAKPILOT_PLANS_GRACE_DAYS' ) ) { define( 'CLOAKPILOT_PLANS_GRACE_DAYS', 14 ); } if ( ! defined( 'CLOAKPILOT_PLANS_ENFORCE_BY' ) ) { define( 'CLOAKPILOT_PLANS_ENFORCE_BY', '' ); } add_action( 'plugins_loaded', function () { CloakPilot_Plans::maybe_schedule_enforcement( CloakPilot_Plugin::DB_VERSION_KEY ); }, 5 ); CloakPilot_Plans::register_hooks(); if ( ! function_exists( 'cloakpilot' ) ) { /** * נקודת כניסה יחידה. Declared inside a condition (P1, 8.11.2026): see the second-copy note * at the top of this file. */ function cloakpilot() { return CloakPilot_Plugin::instance(); } } cloakpilot(); // הפעלה / כיבוי. register_activation_hook( __FILE__, array( 'CloakPilot_Plugin', 'activate' ) ); register_deactivation_hook( __FILE__, array( 'CloakPilot_Plugin', 'deactivate' ) );