Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
21.24% covered (danger)
21.24%
48 / 226
15.79% covered (danger)
15.79%
3 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
ECommerce
21.24% covered (danger)
21.24%
48 / 226
15.79% covered (danger)
15.79%
3 / 19
903.85
0.00% covered (danger)
0.00%
0 / 1
 __construct
90.00% covered (success)
90.00%
36 / 40
0.00% covered (danger)
0.00%
0 / 1
2.00
 add_filters
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 load_php_textdomain
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 add_to_runtime
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 maybe_do_dash_redirect
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 register_routes
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 register_settings
0.00% covered (danger)
0.00%
0 / 91
0.00% covered (danger)
0.00%
0 / 1
12
 load_script_translation_file
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 register_textdomains
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 hide_woocommerce_set_up
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 custom_payment_gateways_order
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 dismiss_woo_payments_cta
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 disable_creative_mail_banner
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 detect_plugin_activation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 remove_woocommerce_ssl_notice
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 check_url_match
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 show_store_setup
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 hide_wp_pointer_with_css
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 disable_modern_payments_settings
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace NewfoldLabs\WP\Module\ECommerce;
4
5use NewfoldLabs\WP\Module\ECommerce\Data\Brands;
6use NewfoldLabs\WP\Module\ECommerce\Partials\CaptiveFlow;
7use NewfoldLabs\WP\Module\ECommerce\Partials\WooCommerceBacklink;
8use NewfoldLabs\WP\Module\Installer\Services\PluginInstaller;
9use NewfoldLabs\WP\ModuleLoader\Container;
10use NewfoldLabs\WP\Module\Data\SiteCapabilities;
11
12/**
13 * Class ECommerce
14 *
15 * @package NewfoldLabs\WP\Module\ECommerce
16 */
17class ECommerce {
18    /**
19     * Main Ecommerce file
20     *
21     * Entry point via bootstrap.php
22     */
23
24    /**
25     * Container loaded from the brand plugin.
26     *
27     * @var Container
28     */
29    protected $container;
30
31    /**
32     * Identifier for script handle.
33     *
34     * @var string
35     */
36    public static $handle_i18n = 'nfd-ecommerce-i18n';
37
38    /**
39     * Array map of API controllers.
40     *
41     * @var array
42     */
43    protected $controllers = array(
44        'NewfoldLabs\\WP\\Module\\ECommerce\\RestApi\\IntegrationsController',
45        'NewfoldLabs\\WP\\Module\\ECommerce\\RestApi\\PluginsController',
46    );
47
48    /**
49     * Option settings
50     *
51     * @var array
52     */
53    protected $options = array(
54        'nfd-ecommerce-captive-flow-paypal',
55        'nfd-ecommerce-captive-flow-shippo',
56        'nfd-ecommerce-captive-flow-stripe',
57        'nfd-ecommerce-captive-flow-razorpay',
58        'nfd-ecommerce-onboarding-check',
59        'nfd-ecommerce-counter',
60        'woocommerce_store_address',
61        'woocommerce_store_address_2',
62        'woocommerce_store_city',
63        'woocommerce_store_postcode',
64        'woocommerce_default_country',
65        'wc_connect_taxes_enabled',
66        'woocommerce_calc_taxes',
67        'woocommerce_currency',
68        'woocommerce_email_from_address',
69        'woocommerce_bacs_settings',
70        'woocommerce_cod_settings',
71        'woocommerce_cheque_settings',
72        'onboarding_experience_level',
73        'yoast_seo_signup_status',
74        'nfd_show_migration_steps',
75        'update_site_server_clicked',
76    );
77
78    /**
79     * ECommerce constructor.
80     *
81     * @param Container $container Container loaded from the brand plugin.
82     */
83    public function __construct( Container $container ) {
84
85        $this->container = $container;
86        // Module functionality goes here
87        add_action( 'init', array( $this, 'load_php_textdomain' ) );
88        add_action( 'admin_init', array( $this, 'maybe_do_dash_redirect' ) );
89        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
90        add_action( 'load-toplevel_page_' . $container->plugin()->id, array( $this, 'register_textdomains' ) );
91        add_action( 'before_woocommerce_init', array( $this, 'hide_woocommerce_set_up' ) );
92        add_action( 'before_woocommerce_init', array( $this, 'custom_payment_gateways_order' ) );
93        add_action( 'before_woocommerce_init', array( $this, 'dismiss_woo_payments_cta' ) );
94        add_action( 'load-toplevel_page_' . $container->plugin()->id, array( $this, 'disable_creative_mail_banner' ) );
95        // add_action( 'activated_plugin', array( $this, 'detect_plugin_activation' ), 10, 1 );
96        add_action( 'wp_login', array( $this, 'show_store_setup' ) );
97        add_action( 'auth_cookie_expired', array( $this, 'show_store_setup' ) );
98        add_action( 'admin_head', array( $this, 'hide_wp_pointer_with_css' ) );
99        add_action( 'admin_footer', array( $this, 'remove_woocommerce_ssl_notice' ), 20 );
100        \add_filter( 'load_script_translation_file', array( $this, 'load_script_translation_file' ), 10, 3 );
101        add_filter( 'woocommerce_admin_get_feature_config', array( $this, 'disable_modern_payments_settings' ), 999 );
102
103        CaptiveFlow::init();
104        WooCommerceBacklink::init( $container );
105        register_meta(
106            'post',
107            'nf_dc_page',
108            array(
109                'type'         => 'string',
110                'description'  => __( 'Reference to page category', 'wp-module-ecommerce' ),
111                'show_in_rest' => true,
112                'single'       => true,
113            )
114        );
115
116        $this->add_filters(
117            array( 'postbox_classes_page_wpseo_meta', 'postbox_classes_post_wpseo_meta', 'postbox_classes_product_wpseo_meta' ),
118            function ( $classes ) {
119                $classes[] = 'closed';
120                return $classes;
121            }
122        );
123
124        // Handle WonderCart Integrations
125        if ( is_plugin_active( 'wonder-cart/init.php' ) ) {
126            $wonder_cart = new WonderCart( $container );
127            $wonder_cart->init();
128        }
129
130        // Load Quick Add Product feature.
131        ( new QuickAddProduct( $container ) )->init();
132        // Load Store Quick Start feature.
133        ( new StoreInfo( $container ) )->init();
134
135        add_filter( 'newfold_runtime', array( $this, 'add_to_runtime' ) );
136    }
137
138    /**
139     * Add multiple filters to a closure
140     *
141     * @param string|array $tags The filter name or array of filter names
142     * @param callable     $function_to_add The closure to add to the filter
143     * @param int|array    $priority The priority at which the closure should be added
144     * @param int|array    $accepted_args The number of arguments the closure accepts
145     *
146     * @return bool true
147     */
148    public static function add_filters( $tags, $function_to_add, $priority = 10, $accepted_args = 1 ) {
149        // If the filter names are not an array, create an array containing one item
150        if ( ! is_array( $tags ) ) {
151            $tags = array( $tags );
152        }
153            // For each filter name
154        foreach ( $tags as $index => $tag ) {
155            add_filter( $tag, $function_to_add, (int) ( is_array( $priority ) ? $priority[ $index ] : $priority ), (int) ( is_array( $accepted_args ) ? $accepted_args[ $index ] : $accepted_args ) );
156        }
157
158        return true;
159    }
160
161    /**
162     * Loads the textdomain for the module. This applies only to PHP strings.
163     *
164     * @return boolean
165     */
166    public static function load_php_textdomain() {
167        return I18nService::load_php_translations(
168            'wp-module-ecommerce',
169            NFD_ECOMMERCE_PLUGIN_DIRNAME . '/vendor/newfold-labs/wp-module-ecommerce/languages'
170        );
171    }
172
173    /**
174     * Add values to the runtime object.
175     *
176     * @param array $sdk The runtime object.
177     *
178     * @return array
179     */
180    public function add_to_runtime( $sdk ) {
181        $values = array(
182            'brand_settings'         => Brands::get_config( $this->container ),
183            'nonces'                 => array(
184                'gateway_toggle' => \wp_create_nonce( 'woocommerce-toggle-payment-gateway-enabled' ),
185            ),
186            'install_token'          => PluginInstaller::rest_get_plugin_install_hash(),
187        );
188        return array_merge( $sdk, array( 'ecommerce' => $values ) );
189    }
190
191    /**
192     * Redirect to the dashboard after WooCommerce activation.
193     */
194    public function maybe_do_dash_redirect() {
195        $show_dash = get_option( 'nfd_show_dash_after_woo_activation', false );
196        if ( $show_dash && ! wp_doing_ajax() ) {
197            update_option( 'nfd_show_dash_after_woo_activation', false );
198            wp_safe_redirect( apply_filters( 'nfd_build_url', admin_url( 'admin.php?page=' . $this->container->plugin()->id . '#/home' ) ) );
199        }
200    }
201
202    /**
203     * Register API routes.
204     */
205    public function register_routes() {
206        foreach ( $this->controllers as $Controller ) {
207            /**
208             * Get an instance of the WP_REST_Controller.
209             *
210             * @var $instance \WP_REST_Controller
211             */
212            $instance = new $Controller( $this->container );
213            $instance->register_routes();
214        }
215        $this->register_settings();
216    }
217
218    /**
219     * Register settings.
220     */
221    public function register_settings() {
222        $option_settings = array(
223            'show_in_rest' => true,
224            'type'         => 'string',
225            'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
226        );
227        foreach ( $this->options as $option ) {
228            \register_setting( 'general', $option, $option_settings );
229        }
230        \register_setting(
231            'general',
232            'woocommerce_no_sales_tax',
233            array(
234                'show_in_rest' => true,
235                'type'         => 'boolean',
236                'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
237            )
238        );
239        \register_setting(
240            'general',
241            'bluehost_academy_signup_clicked',
242            array(
243                'show_in_rest' => true,
244                'type'         => 'boolean',
245                'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
246            )
247        );
248        \register_setting(
249            'general',
250            'yoast_seo_signup_status',
251            array(
252                'show_in_rest' => true,
253                'type'         => 'boolean',
254                'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
255            )
256        );
257        \register_setting(
258            'general',
259            'update_site_server_clicked',
260            array(
261                'show_in_rest' => true,
262                'type'         => 'boolean',
263                'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
264            )
265        );
266        \register_setting(
267            'general',
268            'nfd_show_migration_steps',
269            array(
270                'show_in_rest' => true,
271                'type'         => 'boolean',
272                'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
273            )
274        );
275        $payments                    = array(
276            'woocommerce_bacs_settings',
277            'woocommerce_cod_settings',
278            'woocommerce_cheque_settings',
279        );
280        $schema_for_offline_payments = array(
281            'show_in_rest' => array(
282                'schema' => array(
283                    'type'       => 'object',
284                    'properties' => array(
285                        'gateway_id' => array(
286                            'type' => 'string',
287                        ),
288                        'enabled'    => array(
289                            'type' => 'string',
290                        ),
291                        'action'     => array(
292                            'type' => 'string',
293                        ),
294                        'security'   => array(
295                            'type' => 'string',
296                        ),
297                    ),
298                ),
299            ),
300            'type'         => 'object',
301            'description'  => __( 'NFD eCommerce Options', 'wp-module-ecommerce' ),
302        );
303        foreach ( $payments as $payment ) {
304            \register_setting( 'general', $payment, $schema_for_offline_payments );
305        }
306
307        register_setting(
308            'general',
309            'is_fse_theme',
310            array(
311                'type'         => 'boolean',
312                'show_in_rest' => true,
313                'default'      => wp_is_block_theme(), // Set default value based on current theme
314            )
315        );
316    }
317
318    /**
319     * Filters the file path for the JS translation JSON.
320     *
321     * If the script handle matches the module's handle, builds a custom path using
322     * the languages directory, current locale, text domain, and a hash of the script.
323     *
324     * @param string $file   Default translation file path.
325     * @param string $handle_i18n Script handle.
326     * @param string $domain Text domain.
327     * @return string Modified file path for the translation JSON.
328     */
329    public function load_script_translation_file( $file, $handle_i18n, $domain ) {
330
331        if ( $handle_i18n === self::$handle_i18n ) {
332            $path   = NFD_ECOMMERCE_DIR . '/languages/';
333            $locale = determine_locale();
334
335            $file_base = 'default' === $domain
336                ? $locale
337                : $domain . '-' . $locale;
338            $file      = $path . $file_base . '-' . md5( 'build/index.js' ) . '.json';
339
340        }
341        return $file;
342    }
343
344    /**
345     * Load the textdomains for the module.
346     */
347    public function register_textdomains() {
348        $MODULE_LANG_DIR  = $this->container->plugin()->dir . 'vendor/newfold-labs/wp-module-ecommerce/languages';
349
350        \load_textdomain(
351            'wp-module-ecommerce',
352            $MODULE_LANG_DIR
353        );
354        // load textdomain for scripts
355        \load_script_textdomain(
356            self::$handle_i18n,
357            'wp-module-ecommerce',
358            $MODULE_LANG_DIR
359        );
360    }
361
362    /**
363     * Hide the WooCommerce set up task list
364     */
365    public function hide_woocommerce_set_up() {
366        $hidden_list = get_option( 'woocommerce_task_list_hidden_lists', array() );
367        if ( ! in_array( 'setup', $hidden_list, true ) ) {
368            $woocommerce_list = array_merge(
369                get_option( 'woocommerce_task_list_hidden_lists', array() ),
370                array(
371                    'setup',
372                )
373            );
374            update_option( 'woocommerce_task_list_hidden_lists', $woocommerce_list );
375        }
376    }
377
378    /**
379     * Change the order of payment gateways
380     */
381    public function custom_payment_gateways_order() {
382        $array_data = array(
383            'pre_install_woocommerce_payments_promotion' => 2,
384            'yith_paypal_payments'                       => 0,
385            'element'                                    => 1,
386        );
387        update_option( 'woocommerce_gateway_order', $array_data );
388    }
389
390    /**
391     * Dismisses the WooCommerce Payments CTA
392     */
393    public function dismiss_woo_payments_cta() {
394        $is_dismissed = get_option( 'wcpay_welcome_page_incentives_dismissed' );
395        if ( ! is_array( $is_dismissed ) || empty( $is_dismissed ) ) {
396            update_option( 'wcpay_welcome_page_incentives_dismissed', array( 'wcpay-promo-2023-action-discount' ) );
397        }
398    }
399
400    /**
401     * Disables the creative mail banner
402     */
403    public function disable_creative_mail_banner() {
404        $is_dismissed = get_option( 'ce4wp_ignore_review_notice' );
405        if ( ! is_array( $is_dismissed ) || empty( $is_dismissed ) ) {
406            update_option( 'ce4wp_ignore_review_notice', true );
407        }
408    }
409
410    /**
411     *  Activates yith payment plugins (PayPal, Stripe) when woocommerce is activated
412     *
413     * @param string $plugin Path to the plugin file relative
414     *
415     * @return void
416     */
417    public function detect_plugin_activation( $plugin ) {
418        /*
419        * Commented out to avoid installing the plugins again when the module is activated.
420        * TODO - Reinstate with update to new Payments & Shipping plugin
421
422        $plugin_slugs = array(
423            'nfd_slug_yith_paypal_payments_for_woocommerce',
424            'nfd_slug_yith_stripe_payments_for_woocommerce',
425        );
426        if ( 'woocommerce/woocommerce.php' === $plugin ) {
427            foreach ( $plugin_slugs as $plugin ) {
428                PluginInstaller::install( $plugin, true );
429            }
430        }
431        */
432        // do nothing for now
433        return;
434    }
435
436    /**
437     * Hide WooCommerce SSL notice
438     *
439     * @return void
440     */
441    public function remove_woocommerce_ssl_notice() {
442
443        // Check if WooCommerce is active.
444        if ( ! class_exists( 'WooCommerce' ) ) {
445            return;
446        }
447
448        if ( ! is_ssl() ) {
449            // Check if there are any WooCommerce admin notices, find the one with ssl notice link and hide it.
450            ?>
451                <script type="text/javascript">
452                    jQuery(document).ready(function($) {
453                        if ($('.updated.woocommerce-message').length) {
454                            $('.updated.woocommerce-message').each(function() {
455                                var $message = $(this);
456                                var $link = $message.find('a[href="https://woocommerce.com/document/ssl-and-https/"]');
457
458                                if ($link.length > 0) {
459                                    $message.hide();
460                                }
461                            });
462                        }
463                    });
464                </script>
465            <?php
466        }
467    }
468
469    /**
470     * Verifies if the url is matching with the regex
471     *
472     * @param string $brand_name id of the brand
473     *
474     * @param string $site_url siteurl
475     */
476    public function check_url_match( $brand_name, $site_url ) {
477        switch ( $brand_name ) {
478            case 'bluehost':
479                return ! preg_match( '/\b\w+(\.\w+)*\.mybluehost\.me\b/', $site_url );
480            case 'hostgator':
481                return ! preg_match( '/\b\w+(\.\w+)*\.temporary\.site\b/', $site_url );
482            default:
483                return true;
484        }
485    }
486
487    /**
488     * On login, it checks whether to show the migration steps, post migration to user
489     */
490    public function show_store_setup() {
491        $site_url = get_option( 'siteurl', false );
492        $brand    = $this->container->plugin()->id;
493
494        if ( $this->check_url_match( $brand, $site_url ) ) {
495            update_option( 'nfd_show_migration_steps', false );
496        }
497    }
498
499    /**
500     * Suppress WP-Form Notice using css
501     *
502     * @return void
503     */
504    public function hide_wp_pointer_with_css() {
505        echo '<style>
506            .wp-pointer { display: none !important; }
507        </style>';
508    }
509
510    /**
511     * Force WooCommerce to use the old Payments settings page.
512     *
513     * WooCommerce 9.7+ introduces a new Payments settings page.
514     * This function disables it and keeps the classic version.
515     *
516     * @param array $features Existing WooCommerce feature configurations.
517     * @return array Modified feature configuration with modern Payments settings disabled.
518     */
519    public function disable_modern_payments_settings( $features ) {
520        $features['reactify-classic-payments-settings'] = false;
521        return $features;
522    }
523}