PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.3.2
Jetpack – WP Security, Backup, Speed, & Growth v12.3.2
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-status / src / class-modules.php
jetpack / jetpack_vendor / automattic / jetpack-status / src Last commit date
class-cache.php 4 years ago class-cookiestate.php 4 years ago class-errors.php 3 years ago class-files.php 4 years ago class-host.php 3 years ago class-modules.php 3 years ago class-paths.php 4 years ago class-status.php 3 years ago class-visitor.php 4 years ago
class-modules.php
598 lines
1 <?php
2 /**
3 * A modules class for Jetpack.
4 *
5 * @package automattic/jetpack-status
6 */
7
8 namespace Automattic\Jetpack;
9
10 use Automattic\Jetpack\Constants as Constants;
11 use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
12 use Automattic\Jetpack\IP\Utils as IP_Utils;
13
14 /**
15 * Class Automattic\Jetpack\Modules
16 *
17 * Used to retrieve information about the current status of Jetpack modules.
18 */
19 class Modules {
20
21 /**
22 * Check whether or not a Jetpack module is active.
23 *
24 * @param string $module The slug of a Jetpack module.
25 * @return bool
26 */
27 public function is_active( $module ) {
28 return in_array( $module, self::get_active(), true );
29 }
30
31 /**
32 * Load module data from module file. Headers differ from WordPress
33 * plugin headers to avoid them being identified as standalone
34 * plugins on the WordPress plugins page.
35 *
36 * @param string $module The module slug.
37 */
38 public function get( $module ) {
39 static $modules_details;
40
41 // This method relies heavy on auto-generated file found in Jetpack only: module-headings.php
42 // If it doesn't exist, it's safe to assume none of this will be helpful.
43 if ( ! function_exists( 'jetpack_has_no_module_info' ) ) {
44 return false;
45 }
46
47 if ( jetpack_has_no_module_info( $module ) ) {
48 return false;
49 }
50
51 $file = $this->get_path( $this->get_slug( $module ) );
52
53 if ( isset( $modules_details[ $module ] ) ) {
54 $mod = $modules_details[ $module ];
55 } else {
56 $mod = jetpack_get_module_info( $module );
57
58 if ( null === $mod ) {
59 // Try to get the module info from the file as a fallback.
60 $mod = $this->get_file_data( $file, jetpack_get_all_module_header_names() );
61
62 if ( empty( $mod['name'] ) ) {
63 // No info for this module.
64 return false;
65 }
66 }
67
68 $mod['sort'] = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
69 $mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
70 $mod['deactivate'] = empty( $mod['deactivate'] );
71 $mod['free'] = empty( $mod['free'] );
72 $mod['requires_connection'] = ( ! empty( $mod['requires_connection'] ) && 'No' === $mod['requires_connection'] ) ? false : true;
73 $mod['requires_user_connection'] = ( empty( $mod['requires_user_connection'] ) || 'No' === $mod['requires_user_connection'] ) ? false : true;
74
75 if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ), true ) ) {
76 $mod['auto_activate'] = 'No';
77 } else {
78 $mod['auto_activate'] = (string) $mod['auto_activate'];
79 }
80
81 if ( $mod['module_tags'] ) {
82 $mod['module_tags'] = explode( ',', $mod['module_tags'] );
83 $mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
84 } else {
85 $mod['module_tags'] = array( 'Other' );
86 }
87
88 if ( $mod['plan_classes'] ) {
89 $mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
90 $mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
91 } else {
92 $mod['plan_classes'] = array( 'free' );
93 }
94
95 if ( $mod['feature'] ) {
96 $mod['feature'] = explode( ',', $mod['feature'] );
97 $mod['feature'] = array_map( 'trim', $mod['feature'] );
98 } else {
99 $mod['feature'] = array( 'Other' );
100 }
101
102 $modules_details[ $module ] = $mod;
103
104 }
105
106 /**
107 * Filters the feature array on a module.
108 *
109 * This filter allows you to control where each module is filtered: Recommended,
110 * and the default "Other" listing.
111 *
112 * @since-jetpack 3.5.0
113 *
114 * @param array $mod['feature'] The areas to feature this module:
115 * 'Recommended' shows on the main Jetpack admin screen.
116 * 'Other' should be the default if no other value is in the array.
117 * @param string $module The slug of the module, e.g. sharedaddy.
118 * @param array $mod All the currently assembled module data.
119 */
120 $mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
121
122 /**
123 * Filter the returned data about a module.
124 *
125 * This filter allows overriding any info about Jetpack modules. It is dangerous,
126 * so please be careful.
127 *
128 * @since-jetpack 3.6.0
129 *
130 * @param array $mod The details of the requested module.
131 * @param string $module The slug of the module, e.g. sharedaddy
132 * @param string $file The path to the module source file.
133 */
134 return apply_filters( 'jetpack_get_module', $mod, $module, $file );
135 }
136
137 /**
138 * Like core's get_file_data implementation, but caches the result.
139 *
140 * @param string $file Absolute path to the file.
141 * @param array $headers List of headers, in the format array( 'HeaderKey' => 'Header Name' ).
142 */
143 public function get_file_data( $file, $headers ) {
144 // Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated.
145 $file_name = basename( $file );
146
147 if ( ! Constants::is_defined( 'JETPACK__VERSION' ) ) {
148 return get_file_data( $file, $headers );
149 }
150
151 $cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
152
153 $file_data_option = get_transient( $cache_key );
154
155 if ( ! is_array( $file_data_option ) ) {
156 delete_transient( $cache_key );
157 $file_data_option = false;
158 }
159
160 if ( false === $file_data_option ) {
161 $file_data_option = array();
162 }
163
164 $key = md5( $file_name . maybe_serialize( $headers ) );
165 $refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
166
167 // If we don't need to refresh the cache, and already have the value, short-circuit!
168 if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
169 return $file_data_option[ $key ];
170 }
171
172 $data = get_file_data( $file, $headers );
173
174 $file_data_option[ $key ] = $data;
175
176 set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
177
178 return $data;
179 }
180
181 /**
182 * Get a list of activated modules as an array of module slugs.
183 */
184 public function get_active() {
185 $active = \Jetpack_Options::get_option( 'active_modules' );
186
187 if ( ! is_array( $active ) ) {
188 $active = array();
189 }
190
191 if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
192 $active[] = 'vaultpress';
193 } else {
194 $active = array_diff( $active, array( 'vaultpress' ) );
195 }
196
197 // If protect is active on the main site of a multisite, it should be active on all sites.
198 if ( ! in_array( 'protect', $active, true ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
199 $active[] = 'protect';
200 }
201
202 // If it's not available, it shouldn't be active.
203 // We don't delete it from the options though, as it will be active again when a plugin gets reactivated.
204 $active = array_intersect( $active, $this->get_available() );
205
206 /**
207 * Allow filtering of the active modules.
208 *
209 * Gives theme and plugin developers the power to alter the modules that
210 * are activated on the fly.
211 *
212 * @since-jetpack 5.8.0
213 *
214 * @param array $active Array of active module slugs.
215 */
216 $active = apply_filters( 'jetpack_active_modules', $active );
217
218 return array_unique( $active );
219 }
220
221 /**
222 * Extract a module's slug from its full path.
223 *
224 * @param string $file Full path to a file.
225 *
226 * @return string Module slug.
227 */
228 public function get_slug( $file ) {
229 return str_replace( '.php', '', basename( $file ) );
230 }
231
232 /**
233 * List available Jetpack modules. Simply lists .php files in /modules/.
234 * Make sure to tuck away module "library" files in a sub-directory.
235 *
236 * @param bool|string $min_version Only return modules introduced in this version or later. Default is false, do not filter.
237 * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter.
238 * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection.
239 * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection.
240 *
241 * @return array $modules Array of module slugs
242 */
243 public function get_available( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) {
244 static $modules = null;
245
246 if ( ! class_exists( 'Jetpack' ) || ! Constants::is_defined( 'JETPACK__VERSION' ) || ! Constants::is_defined( 'JETPACK__PLUGIN_DIR' ) ) {
247 return array_unique(
248 /**
249 * Stand alone plugins need to use this filter to register the modules they interact with.
250 * This will allow them to activate and deactivate these modules even when Jetpack is not present.
251 * Note: Standalone plugins can only interact with modules that also exist in the Jetpack plugin, otherwise they'll lose the ability to control it if Jetpack is activated.
252 *
253 * @since 1.13.6
254 *
255 * @param array $modules The list of available modules as an array of slugs.
256 * @param bool $requires_connection Whether to list only modules that require a connection to work.
257 * @param bool $requires_user_connection Whether to list only modules that require a user connection to work.
258 */
259 apply_filters( 'jetpack_get_available_standalone_modules', array(), $requires_connection, $requires_user_connection )
260 );
261 }
262
263 if ( ! isset( $modules ) ) {
264 $available_modules_option = \Jetpack_Options::get_option( 'available_modules', array() );
265 // Use the cache if we're on the front-end and it's available...
266 if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
267 $modules = $available_modules_option[ JETPACK__VERSION ];
268 } else {
269 $files = ( new Files() )->glob_php( JETPACK__PLUGIN_DIR . 'modules' );
270
271 $modules = array();
272
273 foreach ( $files as $file ) {
274 $slug = $this->get_slug( $file );
275 $headers = $this->get( $slug );
276
277 if ( ! $headers ) {
278 continue;
279 }
280
281 $modules[ $slug ] = $headers['introduced'];
282 }
283
284 \Jetpack_Options::update_option(
285 'available_modules',
286 array(
287 JETPACK__VERSION => $modules,
288 )
289 );
290 }
291 }
292
293 /**
294 * Filters the array of modules available to be activated.
295 *
296 * @since 2.4.0
297 *
298 * @param array $modules Array of available modules.
299 * @param string $min_version Minimum version number required to use modules.
300 * @param string $max_version Maximum version number required to use modules.
301 * @param bool|null $requires_connection Value of the Requires Connection filter.
302 * @param bool|null $requires_user_connection Value of the Requires User Connection filter.
303 */
304 $mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version, $requires_connection, $requires_user_connection );
305
306 if ( ! $min_version && ! $max_version && $requires_connection === null && $requires_user_connection === null ) {
307 return array_keys( $mods );
308 }
309
310 $r = array();
311 foreach ( $mods as $slug => $introduced ) {
312 if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
313 continue;
314 }
315
316 if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
317 continue;
318 }
319
320 $mod_details = $this->get( $slug );
321
322 if ( null !== $requires_connection && (bool) $requires_connection !== $mod_details['requires_connection'] ) {
323 continue;
324 }
325
326 if ( null !== $requires_user_connection && (bool) $requires_user_connection !== $mod_details['requires_user_connection'] ) {
327 continue;
328 }
329
330 $r[] = $slug;
331 }
332
333 return $r;
334 }
335
336 /**
337 * Is slug a valid module.
338 *
339 * @param string $module Module slug.
340 *
341 * @return bool
342 */
343 public function is_module( $module ) {
344 return ! empty( $module ) && ! validate_file( $module, $this->get_available() );
345 }
346
347 /**
348 * Update module status.
349 *
350 * @param string $module - module slug.
351 * @param boolean $active - true to activate, false to deactivate.
352 * @param bool $exit Should exit be called after deactivation.
353 * @param bool $redirect Should there be a redirection after activation.
354 */
355 public function update_status( $module, $active, $exit = true, $redirect = true ) {
356 return $active ? $this->activate( $module, $exit, $redirect ) : $this->deactivate( $module );
357 }
358
359 /**
360 * Activate a module.
361 *
362 * @param string $module Module slug.
363 * @param bool $exit Should exit be called after deactivation.
364 * @param bool $redirect Should there be a redirection after activation.
365 *
366 * @return bool|void
367 */
368 public function activate( $module, $exit = true, $redirect = true ) {
369 /**
370 * Fires before a module is activated.
371 *
372 * @since 2.6.0
373 *
374 * @param string $module Module slug.
375 * @param bool $exit Should we exit after the module has been activated. Default to true.
376 * @param bool $redirect Should the user be redirected after module activation? Default to true.
377 */
378 do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
379
380 if ( ! strlen( $module ) ) {
381 return false;
382 }
383
384 // If it's already active, then don't do it again.
385 $active = $this->get_active();
386 foreach ( $active as $act ) {
387 if ( $act === $module ) {
388 return true;
389 }
390 }
391
392 if ( ! $this->is_module( $module ) ) {
393 return false;
394 }
395
396 // Jetpack plugin only
397 if ( class_exists( 'Jetpack' ) ) {
398
399 $module_data = $this->get( $module );
400
401 $status = new Status();
402 $state = new CookieState();
403
404 if ( ! \Jetpack::is_connection_ready() ) {
405 if ( ! $status->is_offline_mode() && ! $status->is_onboarding() ) {
406 return false;
407 }
408
409 // If we're not connected but in offline mode, make sure the module doesn't require a connection.
410 if ( $status->is_offline_mode() && $module_data['requires_connection'] ) {
411 return false;
412 }
413 }
414
415 if ( class_exists( 'Jetpack_Client_Server' ) ) {
416 $jetpack = \Jetpack::init();
417
418 // Check and see if the old plugin is active.
419 if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
420 // Deactivate the old plugins.
421 $deactivated = array();
422 foreach ( $jetpack->plugins_to_deactivate[ $module ] as $idx => $deactivate_me ) {
423 if ( \Jetpack_Client_Server::deactivate_plugin( $deactivate_me[0], $deactivate_me[1] ) ) {
424 // If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
425 // We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
426 $deactivated[] = "$module:$idx";
427 }
428 }
429 if ( $deactivated ) {
430 $state->state( 'deactivated_plugins', implode( ',', $deactivated ) );
431 wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
432 exit;
433 }
434 }
435 }
436
437 // Protect won't work with mis-configured IPs.
438 if ( 'protect' === $module ) {
439 if ( ! IP_Utils::get_ip() ) {
440 $state->state( 'message', 'protect_misconfigured_ip' );
441 return false;
442 }
443 }
444
445 if ( ! Jetpack_Plan::supports( $module ) ) {
446 return false;
447 }
448
449 // Check the file for fatal errors, a la wp-admin/plugins.php::activate.
450 $errors = new Errors();
451 $state->state( 'module', $module );
452 $state->state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error.
453 $errors->catch_errors( true );
454
455 ob_start();
456 $module_path = $this->get_path( $module );
457 if ( file_exists( $module_path ) ) {
458 require $this->get_path( $module ); // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.NotAbsolutePath
459 }
460
461 $active[] = $module;
462 $this->update_active( $active );
463
464 $state->state( 'error', false ); // the override.
465 ob_end_clean();
466 $errors->catch_errors( false );
467 } else { // Not a Jetpack plugin.
468 $active[] = $module;
469 $this->update_active( $active );
470 }
471
472 if ( $redirect ) {
473 wp_safe_redirect( ( new Paths() )->admin_url( 'page=jetpack' ) );
474 }
475 if ( $exit ) {
476 exit;
477 }
478 return true;
479 }
480
481 /**
482 * Deactivate module.
483 *
484 * @param string $module Module slug.
485 *
486 * @return bool
487 */
488 public function deactivate( $module ) {
489 /**
490 * Fires when a module is deactivated.
491 *
492 * @since 1.9.0
493 *
494 * @param string $module Module slug.
495 */
496 do_action( 'jetpack_pre_deactivate_module', $module );
497
498 $active = $this->get_active();
499 $new = array_filter( array_diff( $active, (array) $module ) );
500
501 return $this->update_active( $new );
502 }
503
504 /**
505 * Generate a module's path from its slug.
506 *
507 * @param string $slug Module slug.
508 */
509 public function get_path( $slug ) {
510 if ( ! Constants::is_defined( 'JETPACK__PLUGIN_DIR' ) ) {
511 return '';
512 }
513 /**
514 * Filters the path of a modules.
515 *
516 * @since 7.4.0
517 *
518 * @param array $return The absolute path to a module's root php file
519 * @param string $slug The module slug
520 */
521 return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug );
522 }
523
524 /**
525 * Saves all the currently active modules to options.
526 * Also fires Action hooks for each newly activated and deactivated module.
527 *
528 * @param array $modules Array of active modules to be saved in options.
529 *
530 * @return $success bool true for success, false for failure.
531 */
532 public function update_active( $modules ) {
533 $current_modules = \Jetpack_Options::get_option( 'active_modules', array() );
534 $active_modules = $this->get_active();
535 $new_active_modules = array_diff( $modules, $current_modules );
536 $new_inactive_modules = array_diff( $active_modules, $modules );
537 $new_current_modules = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
538 $reindexed_modules = array_values( $new_current_modules );
539 $success = \Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
540 // Let's take `pre_update_option_jetpack_active_modules` filter into account
541 // and actually decide for which modules we need to fire hooks by comparing
542 // the 'active_modules' option before and after the update.
543 $current_modules_post_update = \Jetpack_Options::get_option( 'active_modules', array() );
544
545 $new_inactive_modules = array_diff( $current_modules, $current_modules_post_update );
546 $new_inactive_modules = array_unique( $new_inactive_modules );
547 $new_inactive_modules = array_values( $new_inactive_modules );
548
549 $new_active_modules = array_diff( $current_modules_post_update, $current_modules );
550 $new_active_modules = array_unique( $new_active_modules );
551 $new_active_modules = array_values( $new_active_modules );
552
553 foreach ( $new_active_modules as $module ) {
554 /**
555 * Fires when a specific module is activated.
556 *
557 * @since 1.9.0
558 *
559 * @param string $module Module slug.
560 * @param boolean $success whether the module was activated. @since 4.2
561 */
562 do_action( 'jetpack_activate_module', $module, $success );
563 /**
564 * Fires when a module is activated.
565 * The dynamic part of the filter, $module, is the module slug.
566 *
567 * @since 1.9.0
568 *
569 * @param string $module Module slug.
570 */
571 do_action( "jetpack_activate_module_$module", $module );
572 }
573
574 foreach ( $new_inactive_modules as $module ) {
575 /**
576 * Fired after a module has been deactivated.
577 *
578 * @since 4.2.0
579 *
580 * @param string $module Module slug.
581 * @param boolean $success whether the module was deactivated.
582 */
583 do_action( 'jetpack_deactivate_module', $module, $success );
584 /**
585 * Fires when a module is deactivated.
586 * The dynamic part of the filter, $module, is the module slug.
587 *
588 * @since 1.9.0
589 *
590 * @param string $module Module slug.
591 */
592 do_action( "jetpack_deactivate_module_$module", $module );
593 }
594
595 return $success;
596 }
597 }
598