Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.52% covered (warning)
88.52%
54 / 61
54.55% covered (warning)
54.55%
6 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Module_Product
89.83% covered (warning)
89.83%
53 / 59
54.55% covered (warning)
54.55%
6 / 11
30.95
0.00% covered (danger)
0.00%
0 / 1
 get_plugin_slug
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_plugin_filename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 check_for_module_name
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 is_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 is_activated
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 is_module_active
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 get_feature_status
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
6.05
 get_status
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
 do_product_specific_activation
88.89% covered (warning)
88.89%
16 / 18
0.00% covered (danger)
0.00%
0 / 1
5.03
 deactivate
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
3.00
 get_module_title
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Base Module product
4 *
5 * @package my-jetpack
6 */
7
8namespace Automattic\Jetpack\My_Jetpack;
9
10use Automattic\Jetpack\Connection\Manager as Connection_Manager;
11use Jetpack;
12use WP_Error;
13
14if ( ! defined( 'ABSPATH' ) ) {
15    exit( 0 );
16}
17
18/**
19 * Class responsible for handling the Module products
20 *
21 * Module products are those that are a Jetpack module behind the scenes.
22 *
23 * They require Jetpack plugin and will then activate/deactivate a module.
24 */
25abstract class Module_Product extends Product {
26
27    /**
28     * The Jetpack module name associated with this product
29     *
30     * @var string|null
31     */
32    public static $module_name = null;
33
34    /**
35     * Get the plugin slug - ovewrite it ans return Jetpack's
36     *
37     * @return ?string
38     */
39    public static function get_plugin_slug() {
40        return self::JETPACK_PLUGIN_SLUG;
41    }
42
43    /**
44     * Get the plugin filename - ovewrite it ans return Jetpack's
45     *
46     * @return ?string
47     */
48    public static function get_plugin_filename() {
49        return self::JETPACK_PLUGIN_FILENAME;
50    }
51
52    /**
53     * Ensure that child classes define $module_name attribute
54     *
55     * @throws \Exception If required attribute is not declared in the child class.
56     * @return void
57     */
58    private static function check_for_module_name() {
59        if ( empty( static::$module_name ) ) {
60            throw new \Exception( 'Module Product classes must declare the $module_name attribute.' );
61        }
62    }
63
64    /**
65     * Checks whether the Product is active
66     *
67     * @return boolean
68     */
69    public static function is_active() {
70        return static::is_jetpack_plugin_active() && static::is_module_active();
71    }
72
73    /**
74     * Checks whether the site has switched the product on, whether or not it has a plan for it.
75     *
76     * Deliberately not is_active(), which some products override with a plan check that ignores the module.
77     *
78     * @return boolean
79     */
80    public static function is_activated() {
81        return static::is_jetpack_plugin_active() && static::is_module_active();
82    }
83
84    /**
85     * Checks whether the Jetpack module is active
86     *
87     * @return bool
88     */
89    public static function is_module_active() {
90        self::check_for_module_name();
91        if ( ! class_exists( 'Jetpack' ) ) {
92            return false;
93        }
94
95        return Jetpack::is_module_active( static::$module_name );
96    }
97
98    /**
99     * Get the product status.
100     * We don't use parent::get_status() to avoid complexity.
101     *
102     * @return string Product status.
103     */
104    private static function get_feature_status() {
105        if ( ! static::is_plugin_installed() ) {
106            return Products::STATUS_PLUGIN_ABSENT;
107        }
108
109        if ( ! static::is_plugin_active() ) {
110            return Products::STATUS_INACTIVE;
111        }
112
113        if ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
114            return Products::STATUS_USER_CONNECTION_ERROR;
115        }
116
117        if ( ! static::is_module_active() ) {
118            return Products::STATUS_MODULE_DISABLED;
119        }
120
121        return Products::STATUS_ACTIVE;
122    }
123
124    /**
125     * Gets the current status of the product
126     *
127     * @return string
128     */
129    public static function get_status() {
130        if ( static::$is_feature ) {
131            return static::get_feature_status();
132        }
133
134        $status = parent::get_status();
135        if ( Products::STATUS_INACTIVE === $status && ! static::is_module_active() ) {
136            $status = Products::STATUS_MODULE_DISABLED;
137        }
138        return $status;
139    }
140
141    /**
142     * Activates the product by installing and activating its plugin
143     *
144     * @param bool|WP_Error $plugin_activation Is the result of the top level activation actions. You probably won't do anything if it is an WP_Error.
145     * @return boolean|\WP_Error
146     */
147    public static function do_product_specific_activation( $plugin_activation ) {
148        self::check_for_module_name();
149
150        if ( is_wp_error( $plugin_activation ) ) {
151            return $plugin_activation;
152        }
153
154        if ( ! class_exists( 'Jetpack' ) ) {
155            return new WP_Error( 'plugin_activation_failed', __( 'Error activating Jetpack plugin', 'jetpack-my-jetpack' ) );
156        }
157
158        $module_activation = Jetpack::activate_module( static::$module_name, false, false );
159
160        if ( ! $module_activation ) {
161            return new WP_Error( 'module_activation_failed', __( 'Error activating Jetpack module', 'jetpack-my-jetpack' ) );
162        }
163
164        // Saved as active, but a jetpack_active_modules callback can still keep it off.
165        if ( ! static::is_module_active() ) {
166            return new WP_Error(
167                'module_forced',
168                sprintf(
169                    /* translators: %s is a Jetpack module name, such as "Stats". */
170                    __( '%s is disabled by your host or site administrator, so it stays off.', 'jetpack-my-jetpack' ),
171                    static::get_module_title()
172                ),
173                array( 'status' => 409 )
174            );
175        }
176
177        return $module_activation;
178    }
179
180    /**
181     * Deactivate the module
182     *
183     * @return boolean|\WP_Error WP_Error when a host keeps the module on.
184     */
185    public static function deactivate() {
186        self::check_for_module_name();
187        if ( ! class_exists( 'Jetpack' ) ) {
188            return true;
189        }
190
191        $result = Jetpack::deactivate_module( static::$module_name );
192
193        // A module that was never saved as active leaves nothing to change, so check the outcome.
194        if ( static::is_module_active() ) {
195            return new WP_Error(
196                'module_forced',
197                sprintf(
198                    /* translators: %s is a Jetpack module name, such as "Stats". */
199                    __( '%s is enabled by your host or site administrator, so it stays on.', 'jetpack-my-jetpack' ),
200                    static::get_module_title()
201                ),
202                array( 'status' => 409 )
203            );
204        }
205
206        return $result;
207    }
208
209    /**
210     * Get the module's display name, falling back to its slug.
211     *
212     * @return string
213     */
214    private static function get_module_title() {
215        $module = Jetpack::get_module( static::$module_name );
216        return ! empty( $module['name'] ) ? $module['name'] : static::$module_name;
217    }
218}