get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s LIMIT 1", 'rh_testimonial' ) ); if ( ! $exists ) { return; } $enabled = get_option( 'rahkar_wp_enabled_modules' ); $enabled = is_array( $enabled ) ? array_values( array_filter( $enabled, 'is_string' ) ) : []; if ( in_array( 'testimonials', $enabled, true ) ) { return; } $enabled[] = 'testimonials'; update_option( 'rahkar_wp_enabled_modules', $enabled, false ); } /** * 0.18.0: turn the courier status on for stores that are already using it. * * The «پیک موتوری» order status used to ship inside `rahkartheme`, where it * was always on. It is a module here, and a module is off until an admin * enables it — so upgrading without this step would leave every order sitting * in `wc-motor-courier` with a status nothing has registered: gone from the * status filter, missing from reports, and an empty dropdown on the edit * screen. * * The trigger is the data, not the theme. Asking "was rahkartheme active" * would be both unknowable from here and wrong — a store could have switched * themes last week and still have the orders. Asking "does an order carry * this status" is the only question whose answer decides whether anything * actually breaks, and it is the same question on a store that never used the * theme at all: no such orders, nothing enabled, no surprise module. * * Safe to repeat: enabling an already-enabled slug is a no-op, and a store * that switches the module off afterwards is not re-enabled, because the * step only runs for versions older than 0.18.0. */ private static function adoptCourierStatus(): void { if ( ! function_exists( 'wc_get_orders' ) ) { return; } // One id is all the evidence needed, and `wc_get_orders` reads whichever // store the site is on — HPOS or the legacy post table — so this does not // have to know which. $existing = wc_get_orders( [ 'status' => [ 'wc-motor-courier' ], 'limit' => 1, 'return' => 'ids', ] ); if ( empty( $existing ) ) { return; } $enabled = get_option( 'rahkar_wp_enabled_modules' ); $enabled = is_array( $enabled ) ? array_values( array_filter( $enabled, 'is_string' ) ) : []; if ( in_array( 'courier_status', $enabled, true ) ) { return; } $enabled[] = 'courier_status'; // Rebuilt by `Lifecycle::maybeUpgrade()` right after the migrations run. update_option( 'rahkar_wp_enabled_modules', $enabled, false ); } /** * 0.17.0: the checkout gate comes back from the add-on as a login setting. * * It was a module of its own in `rahkar-wp-pro`, so a store that had it on * carries `checkout_gate` in the enabled-module list. The slug now belongs to * nothing: the add-on has dropped the module, so leaving it there would mean * a store that paid for the feature quietly losing it on upgrade. The switch * it was is turned on in its new home instead, and the dead slug removed. * * Ordering between the two plugins does not matter. If the add-on is still on * its old version, its module is skipped anyway — the slug is gone from the * enabled list, which is gate one — so the gate runs once either way. */ private static function adoptCheckoutGate(): void { $enabled = get_option( 'rahkar_wp_enabled_modules' ); if ( ! is_array( $enabled ) || ! in_array( 'checkout_gate', $enabled, true ) ) { return; } $login = get_option( Settings::PREFIX . 'otp_auth' ); $login = is_array( $login ) ? $login : []; // Never overwrite a value already set: that would mean this step ran // before and the admin has since switched the gate off. if ( ! array_key_exists( 'checkout_gate', $login ) ) { $login['checkout_gate'] = true; update_option( Settings::PREFIX . 'otp_auth', $login, false ); Settings::flush(); } // Rebuilt by `Lifecycle::maybeUpgrade()` right after the migrations run, // so the manifest picks up both the dropped slug and the new switch. update_option( 'rahkar_wp_enabled_modules', array_values( array_diff( $enabled, [ 'checkout_gate' ] ) ), false ); } /** * 0.10.0: the login code's pattern moves from the gateway to the login module. * * It used to be an IPPanel setting, shown wherever IPPanel was, including on a * store that had only the notifications module on. It belongs to the login * module, and per gateway, since a pattern code means nothing to another * provider. The order pattern that sat beside it was a duplicate of the * events screen's per-event field, which already overrode it, and is dropped. */ private static function moveLoginPatterns(): void { $notifications = get_option( Settings::PREFIX . 'notifications' ); if ( ! is_array( $notifications ) ) { return; } $login = get_option( Settings::PREFIX . 'otp_auth' ); $login = is_array( $login ) ? $login : []; $pattern = trim( (string) ( $notifications['ippanel_pattern_otp_login'] ?? '' ) ); $variable = trim( (string) ( $notifications['ippanel_otp_variable'] ?? '' ) ); // Never overwrite a value already set in the new place — that would mean // this step ran before and the admin has since changed it. if ( '' !== $pattern && '' === (string) ( $login['pattern_ippanel'] ?? '' ) ) { $login['pattern_ippanel'] = $pattern; } if ( '' !== $variable && '' === (string) ( $login['pattern_var_ippanel'] ?? '' ) ) { $login['pattern_var_ippanel'] = $variable; } unset( $notifications['ippanel_pattern_otp_login'], $notifications['ippanel_otp_variable'], $notifications['ippanel_pattern_order_new'] ); update_option( Settings::PREFIX . 'otp_auth', $login, false ); update_option( Settings::PREFIX . 'notifications', $notifications, false ); Settings::flush(); } }