Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
53.49% covered (warning)
53.49%
23 / 43
62.50% covered (warning)
62.50%
5 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hybrid_Product
53.66% covered (warning)
53.66%
22 / 41
62.50% covered (warning)
62.50%
5 / 8
106.02
0.00% covered (danger)
0.00%
0 / 1
 is_plugin_installed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 is_plugin_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 is_standalone_plugin_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 activate_plugin
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 do_product_specific_activation
20.00% covered (danger)
20.00%
3 / 15
0.00% covered (danger)
0.00%
0 / 1
50.47
 deactivate
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 install_and_activate_standalone
50.00% covered (danger)
50.00%
4 / 8
0.00% covered (danger)
0.00%
0 / 1
8.12
1<?php
2/**
3 * Base product
4 *
5 * @package my-jetpack
6 */
7
8namespace Automattic\Jetpack\My_Jetpack;
9
10use Automattic\Jetpack\Modules;
11use Automattic\Jetpack\Plugins_Installer;
12use WP_Error;
13
14if ( ! defined( 'ABSPATH' ) ) {
15    exit( 0 );
16}
17
18/**
19 * Class responsible for handling the hybrid products
20 *
21 * Hybrid products are those that may work both as a stand-alone plugin or with the Jetpack plugin.
22 */
23abstract class Hybrid_Product extends Product {
24    /**
25     * All hybrid products have a standalone plugin
26     *
27     * @var bool
28     */
29    public static $has_standalone_plugin = true;
30
31    /**
32     * For Hybrid products, we can use either the standalone or Jetpack plugin
33     *
34     * @return bool
35     */
36    public static function is_plugin_installed() {
37        return parent::is_plugin_installed() || parent::is_jetpack_plugin_installed();
38    }
39
40    /**
41     * Checks whether the Product is active
42     *
43     * @return boolean
44     */
45    public static function is_plugin_active() {
46        return parent::is_plugin_active() || parent::is_jetpack_plugin_active();
47    }
48
49    /**
50     * Checks whether the standalone plugin for this product is active
51     *
52     * @return boolean
53     */
54    public static function is_standalone_plugin_active() {
55        return parent::is_plugin_active();
56    }
57
58    /**
59     * Checks whether the Product is active
60     *
61     * @return boolean
62     */
63    public static function is_active() {
64        return parent::is_active() && static::is_module_active();
65    }
66
67    /**
68     * Activates the plugin
69     *
70     * @return null|WP_Error Null on success, WP_Error on invalid file.
71     */
72    public static function activate_plugin() {
73        /*
74         * Activate self-installed plugin if it's installed.
75         */
76        if ( parent::is_plugin_installed() ) {
77            return activate_plugin( static::get_installed_plugin_filename() );
78        }
79
80        /*
81         * Otherwise, activate Jetpack plugin.
82         */
83        if ( static::is_jetpack_plugin_installed() ) {
84            return activate_plugin( static::get_installed_plugin_filename( 'jetpack' ) );
85        }
86
87        return new WP_Error( 'plugin_not_found', __( 'Activation failed. Plugin is not installed', 'jetpack-my-jetpack' ) );
88    }
89
90    /**
91     * Activates the product. If the Hybrid product has declared a jetpack module name, let's try to activate it if Jetpack plugin is active
92     *
93     * @param bool|WP_Error $product_activation Is the result of the top level activation actions. You probably won't do anything if it is an WP_Error.
94     * @return bool|WP_Error
95     */
96    public static function do_product_specific_activation( $product_activation ) {
97
98        if ( is_wp_error( $product_activation ) ) {
99            // If we failed to install the stand-alone plugin because the package was not found, let's try and install Jetpack plugin instead.
100            // This might happen, for example, while the stand-alone plugin was not released to the WP.org repository yet.
101            if ( 'no_package' === $product_activation->get_error_code() ) {
102                $product_activation = Plugins_Installer::install_plugin( self::JETPACK_PLUGIN_SLUG );
103                if ( ! is_wp_error( $product_activation ) ) {
104                    $product_activation = static::activate_plugin();
105                }
106            }
107            if ( is_wp_error( $product_activation ) ) {
108                return $product_activation;
109            }
110        }
111
112        if ( ! empty( static::$module_name ) ) {
113            // Only activate the module if the plan supports it
114            // We don't want to throw an error for a missing plan here since we try activation before purchase
115            if ( static::$requires_plan && ! static::has_any_plan_for_product() ) {
116                return true;
117            }
118
119            $module_activation = ( new Modules() )->activate( static::$module_name, false, false );
120
121            if ( ! $module_activation ) {
122                return new WP_Error( 'module_activation_failed', __( 'Error activating Jetpack module', 'jetpack-my-jetpack' ) );
123            }
124
125            return $module_activation;
126        }
127
128        return true;
129    }
130
131    /**
132     * Deactivates the product.
133     *
134     * In addition to the parent's plugin deactivation, also deactivates the
135     * matching Jetpack module when `static::$module_name` is set and the
136     * module is currently active — otherwise Hybrid products activated via
137     * the Jetpack-module path stay half-on after "deactivation".
138     *
139     * @return bool|WP_Error True on success (matches Product::deactivate()),
140     *                       WP_Error if the module deactivation failed.
141     */
142    public static function deactivate() {
143        $result  = parent::deactivate();
144        $modules = new Modules();
145
146        if ( ! empty( static::$module_name ) && $modules->is_active( static::$module_name ) ) {
147            if ( ! $modules->deactivate( static::$module_name ) ) {
148                return new WP_Error(
149                    'module_deactivation_failed',
150                    __( 'Error deactivating Jetpack module', 'jetpack-my-jetpack' )
151                );
152            }
153        }
154
155        return $result;
156    }
157
158    /**
159     * Install and activate the standalone plugin in the case it's missing.
160     *
161     * @return boolean|WP_Error
162     */
163    public static function install_and_activate_standalone() {
164        $result = parent::install_and_activate_standalone();
165
166        if ( is_wp_error( $result ) ) {
167            return $result;
168        }
169
170        /**
171         * Activate the module as well, if the user has a plan
172         * or the product does not require a plan to work
173         */
174        if ( static::has_any_plan_for_product() && isset( static::$module_name ) ) {
175            $module_activation = ( new Modules() )->activate( static::$module_name, false, false );
176
177            if ( ! $module_activation ) {
178                return new WP_Error( 'module_activation_failed', __( 'Error activating Jetpack module', 'jetpack-my-jetpack' ) );
179            }
180        }
181
182        return true;
183    }
184}