PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
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 2 years ago class-cookiestate.php 1 month ago class-errors.php 7 months ago class-files.php 2 years ago class-host.php 7 months ago class-modules.php 2 weeks ago class-paths.php 2 years ago class-request.php 7 months ago class-status.php 7 months ago class-visitor.php 2 years ago
class-modules.php
610 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\Current_Plan as Jetpack_Plan;
11 use Automattic\Jetpack\IP\Utils as IP_Utils;
12 use Automattic\Jetpack\Status\Host;
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 * @param bool $available_only Whether to only check among available modules.
26 *
27 * @return bool
28 */
29 public function is_active( $module, $available_only = true ) {
30 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
31 return true;
32 }
33
34 return in_array( $module, self::get_active( $available_only ), true );
35 }
36
37 /**
38 * Load module data from module file. Headers differ from WordPress
39 * plugin headers to avoid them being identified as standalone
40 * plugins on the WordPress plugins page.
41 *
42 * @param string $module The module slug.
43 */
44 public function get( $module ) {
45 static $modules_details;
46
47 // This method relies heavy on auto-generated file found in Jetpack only: module-headings.php
48 // If it doesn't exist, it's safe to assume none of this will be helpful.
49 if ( ! function_exists( 'jetpack_has_no_module_info' ) ) {
50 return false;
51 }
52
53 if ( jetpack_has_no_module_info( $module ) ) {
54 return false;
55 }
56
57 $file = $this->get_path( $this->get_slug( $module ) );
58
59 if ( isset( $modules_details[ $module ] ) ) {
60 $mod = $modules_details[ $module ];
61 } else {
62 $mod = jetpack_get_module_info( $module );
63
64 if ( null === $mod ) {
65 // Try to get the module info from the file as a fallback.
66 $mod = $this->get_file_data( $file, jetpack_get_all_module_header_names() );
67
68 if ( empty( $mod['name'] ) ) {
69 // No info for this module.
70 return false;
71 }
72 }
73
74 $mod['sort'] = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
75 $mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
76 $mod['deactivate'] = empty( $mod['deactivate'] );
77 $mod['free'] = empty( $mod['free'] );
78 $mod['requires_connection'] = empty( $mod['requires_connection'] ) || 'No' !== $mod['requires_connection'];
79 $mod['requires_user_connection'] = ! ( empty( $mod['requires_user_connection'] ) || 'No' === $mod['requires_user_connection'] );
80
81 if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ), true ) ) {
82 $mod['auto_activate'] = 'No';
83 } else {
84 $mod['auto_activate'] = (string) $mod['auto_activate'];
85 }
86
87 if ( $mod['module_tags'] ) {
88 $mod['module_tags'] = explode( ',', $mod['module_tags'] );
89 $mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
90 } else {
91 $mod['module_tags'] = array( 'Other' );
92 }
93
94 if ( $mod['plan_classes'] ) {
95 $mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
96 $mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
97 } else {
98 $mod['plan_classes'] = array( 'free' );
99 }
100
101 if ( $mod['feature'] ) {
102 $mod['feature'] = explode( ',', $mod['feature'] );
103 $mod['feature'] = array_map( 'trim', $mod['feature'] );
104 } else {
105 $mod['feature'] = array( 'Other' );
106 }
107
108 $modules_details[ $module ] = $mod;
109
110 }
111
112 /**
113 * Filters the feature array on a module.
114 *
115 * This filter allows you to control where each module is filtered: Recommended,
116 * and the default "Other" listing.
117 *
118 * @since-jetpack 3.5.0
119 *
120 * @param array $mod['feature'] The areas to feature this module:
121 * 'Recommended' shows on the main Jetpack admin screen.
122 * 'Other' should be the default if no other value is in the array.
123 * @param string $module The slug of the module, e.g. sharedaddy.
124 * @param array $mod All the currently assembled module data.
125 */
126 $mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
127
128 /**
129 * Filter the returned data about a module.
130 *
131 * This filter allows overriding any info about Jetpack modules. It is dangerous,
132 * so please be careful.
133 *
134 * @since-jetpack 3.6.0
135 *
136 * @param array $mod The details of the requested module.
137 * @param string $module The slug of the module, e.g. sharedaddy
138 * @param string $file The path to the module source file.
139 */
140 return apply_filters( 'jetpack_get_module', $mod, $module, $file );
141 }
142
143 /**
144 * Like core's get_file_data implementation, but caches the result.
145 *
146 * @param string $file Absolute path to the file.
147 * @param array $headers List of headers, in the format array( 'HeaderKey' => 'Header Name' ).
148 */
149 public function get_file_data( $file, $headers ) {
150 // Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated.
151 $file_name = basename( $file );
152
153 if ( ! Constants::is_defined( 'JETPACK__VERSION' ) ) {
154 return get_file_data( $file, $headers );
155 }
156
157 $cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
158
159 $file_data_option = get_transient( $cache_key );
160
161 if ( ! is_array( $file_data_option ) ) {
162 delete_transient( $cache_key );
163 $file_data_option = false;
164 }
165
166 if ( false === $file_data_option ) {
167 $file_data_option = array();
168 }
169
170 $key = md5( $file_name . maybe_serialize( $headers ) );
171 $refresh_cache = is_admin() && isset( $_GET['page'] ) && is_string( $_GET['page'] ) && str_starts_with( $_GET['page'], 'jetpack' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
172
173 // If we don't need to refresh the cache, and already have the value, short-circuit!
174 if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
175 return $file_data_option[ $key ];
176 }
177
178 $data = get_file_data( $file, $headers );
179
180 $file_data_option[ $key ] = $data;
181
182 set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
183
184 return $data;
185 }
186
187 /**
188 * Get a list of activated modules as an array of module slugs.
189 *
190 * @param bool $available_only Filter out the unavailable (deleted) modules.
191 *
192 * @return array
193 */
194 public function get_active( $available_only = true ) {
195 $active = \Jetpack_Options::get_option( 'active_modules' );
196
197 if ( ! is_array( $active ) ) {
198 $active = array();
199 }
200
201 if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
202 $active[] = 'vaultpress';
203 } else {
204 $active = array_diff( $active, array( 'vaultpress' ) );
205 }
206
207 // If protect is active on the main site of a multisite, it should be active on all sites. Doesn't apply to WP.com.
208 if ( ! in_array( 'protect', $active, true )
209 && ! ( new Host() )->is_wpcom_simple()
210 && is_multisite()
211 && get_site_option( 'jetpack_protect_active' ) ) {
212 $active[] = 'protect';
213 }
214
215 if ( $available_only ) {
216 // If it's not available, it shouldn't be active.
217 // We don't delete it from the options though, as it will be active again when a plugin gets reactivated.
218 $active = array_intersect( $active, $this->get_available() );
219 }
220
221 /**
222 * Allow filtering of the active modules.
223 *
224 * Gives theme and plugin developers the power to alter the modules that
225 * are activated on the fly.
226 *
227 * @since-jetpack 5.8.0
228 *
229 * @param array $active Array of active module slugs.
230 */
231 $active = apply_filters( 'jetpack_active_modules', $active );
232
233 return array_unique( $active );
234 }
235
236 /**
237 * Extract a module's slug from its full path.
238 *
239 * @param string $file Full path to a file.
240 *
241 * @return string Module slug.
242 */
243 public function get_slug( $file ) {
244 return str_replace( '.php', '', basename( $file ) );
245 }
246
247 /**
248 * List available Jetpack modules. Simply lists .php files in /modules/.
249 * Make sure to tuck away module "library" files in a sub-directory.
250 *
251 * @param bool|string $min_version Only return modules introduced in this version or later. Default is false, do not filter.
252 * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter.
253 * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection.
254 * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection.
255 *
256 * @return array $modules Array of module slugs
257 */
258 public function get_available( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) {
259 static $modules = null;
260
261 if ( ! class_exists( 'Jetpack' ) || ! Constants::is_defined( 'JETPACK__VERSION' ) || ! Constants::is_defined( 'JETPACK__PLUGIN_DIR' ) ) {
262 return array_unique(
263 /**
264 * Stand alone plugins need to use this filter to register the modules they interact with.
265 * This will allow them to activate and deactivate these modules even when Jetpack is not present.
266 * 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.
267 *
268 * @since 1.13.6
269 *
270 * @param array $modules The list of available modules as an array of slugs.
271 * @param bool $requires_connection Whether to list only modules that require a connection to work.
272 * @param bool $requires_user_connection Whether to list only modules that require a user connection to work.
273 */
274 apply_filters( 'jetpack_get_available_standalone_modules', array(), $requires_connection, $requires_user_connection )
275 );
276 }
277
278 if ( ! isset( $modules ) ) {
279 $available_modules_option = \Jetpack_Options::get_option( 'available_modules', array() );
280 // Use the cache if we're on the front-end and it's available...
281 if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
282 $modules = $available_modules_option[ JETPACK__VERSION ];
283 } else {
284 $files = ( new Files() )->glob_php( JETPACK__PLUGIN_DIR . 'modules' );
285
286 $modules = array();
287
288 foreach ( $files as $file ) {
289 $slug = $this->get_slug( $file );
290 $headers = $this->get( $slug );
291
292 if ( ! $headers ) {
293 continue;
294 }
295
296 $modules[ $slug ] = $headers['introduced'];
297 }
298
299 \Jetpack_Options::update_option(
300 'available_modules',
301 array(
302 JETPACK__VERSION => $modules,
303 )
304 );
305 }
306 }
307
308 /**
309 * Filters the array of modules available to be activated.
310 *
311 * @since 2.4.0
312 *
313 * @param array $modules Array of available modules.
314 * @param string $min_version Minimum version number required to use modules.
315 * @param string $max_version Maximum version number required to use modules.
316 * @param bool|null $requires_connection Value of the Requires Connection filter.
317 * @param bool|null $requires_user_connection Value of the Requires User Connection filter.
318 */
319 $mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version, $requires_connection, $requires_user_connection );
320
321 if ( ! $min_version && ! $max_version && $requires_connection === null && $requires_user_connection === null ) {
322 return array_keys( $mods );
323 }
324
325 $r = array();
326 foreach ( $mods as $slug => $introduced ) {
327 if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
328 continue;
329 }
330
331 if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
332 continue;
333 }
334
335 $mod_details = $this->get( $slug );
336
337 if ( null !== $requires_connection && (bool) $requires_connection !== $mod_details['requires_connection'] ) {
338 continue;
339 }
340
341 if ( null !== $requires_user_connection && (bool) $requires_user_connection !== $mod_details['requires_user_connection'] ) {
342 continue;
343 }
344
345 $r[] = $slug;
346 }
347
348 return $r;
349 }
350
351 /**
352 * Is slug a valid module.
353 *
354 * @param string $module Module slug.
355 *
356 * @return bool
357 */
358 public function is_module( $module ) {
359 return ! empty( $module ) && ! validate_file( $module, $this->get_available() );
360 }
361
362 /**
363 * Update module status.
364 *
365 * @param string $module - module slug.
366 * @param boolean $active - true to activate, false to deactivate.
367 * @param bool $exit Should exit be called after deactivation.
368 * @param bool $redirect Should there be a redirection after activation.
369 */
370 public function update_status( $module, $active, $exit = true, $redirect = true ) {
371 return $active ? $this->activate( $module, $exit, $redirect ) : $this->deactivate( $module );
372 }
373
374 /**
375 * Activate a module.
376 *
377 * @param string $module Module slug.
378 * @param bool $exit Should exit be called after deactivation.
379 * @param bool $redirect Should there be a redirection after activation.
380 *
381 * @return bool|void
382 */
383 public function activate( $module, $exit = true, $redirect = true ) {
384 /**
385 * Fires before a module is activated.
386 *
387 * @since 2.6.0
388 *
389 * @param string $module Module slug.
390 * @param bool $exit Should we exit after the module has been activated. Default to true.
391 * @param bool $redirect Should the user be redirected after module activation? Default to true.
392 */
393 do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
394
395 if ( ! strlen( $module ) ) {
396 return false;
397 }
398
399 // If it's already active, then don't do it again.
400 $active = $this->get_active();
401 foreach ( $active as $act ) {
402 if ( $act === $module ) {
403 return true;
404 }
405 }
406
407 if ( ! $this->is_module( $module ) ) {
408 return false;
409 }
410
411 // Jetpack plugin only
412 if ( class_exists( 'Jetpack' ) ) {
413
414 $module_data = $this->get( $module );
415
416 $status = new Status();
417 $state = new CookieState();
418
419 if ( ! \Jetpack::is_connection_ready() ) {
420 if ( ! $status->is_offline_mode() ) {
421 return false;
422 }
423
424 // If we're not connected but in offline mode, make sure the module doesn't require a connection.
425 if ( $status->is_offline_mode() && $module_data['requires_connection'] ) {
426 return false;
427 }
428 }
429
430 if ( class_exists( 'Jetpack_Client_Server' ) ) {
431 $jetpack = \Jetpack::init();
432
433 // Check and see if the old plugin is active.
434 if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
435 // Deactivate the old plugins.
436 $deactivated = array();
437 foreach ( $jetpack->plugins_to_deactivate[ $module ] as $idx => $deactivate_me ) {
438 if ( \Jetpack_Client_Server::deactivate_plugin( $deactivate_me[0], $deactivate_me[1] ) ) {
439 // If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
440 // We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
441 $deactivated[] = "$module:$idx";
442 }
443 }
444 if ( $deactivated ) {
445 $state->state( 'deactivated_plugins', implode( ',', $deactivated ) );
446 wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
447 exit( 0 );
448 }
449 }
450 }
451
452 // Protect won't work with mis-configured IPs.
453 if ( 'protect' === $module ) {
454 if ( ! IP_Utils::get_ip() ) {
455 $state->state( 'message', 'protect_misconfigured_ip' );
456 return false;
457 }
458 }
459
460 if ( ! Jetpack_Plan::supports( $module ) ) {
461 return false;
462 }
463
464 // Check the file for fatal errors, a la wp-admin/plugins.php::activate.
465 $state->state( 'module', $module );
466 $state->state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error.
467
468 ob_start();
469 $module_path = $this->get_path( $module );
470 if ( file_exists( $module_path ) ) {
471 require_once $this->get_path( $module );
472 }
473
474 $active[] = $module;
475 $this->update_active( $active );
476
477 $state->state( 'error', false ); // the override.
478 ob_end_clean();
479 } else { // Not a Jetpack plugin.
480 $active[] = $module;
481 $this->update_active( $active );
482 }
483
484 if ( $redirect ) {
485 wp_safe_redirect( ( new Paths() )->admin_url( 'page=jetpack' ) );
486 }
487 if ( $exit ) {
488 exit( 0 );
489 }
490 return true;
491 }
492
493 /**
494 * Deactivate module.
495 *
496 * @param string $module Module slug.
497 *
498 * @return bool
499 */
500 public function deactivate( $module ) {
501 /**
502 * Fires when a module is deactivated.
503 *
504 * @since 1.9.0
505 *
506 * @param string $module Module slug.
507 */
508 do_action( 'jetpack_pre_deactivate_module', $module );
509
510 $active = $this->get_active();
511 $new = array_filter( array_diff( $active, (array) $module ) );
512
513 return $this->update_active( $new );
514 }
515
516 /**
517 * Generate a module's path from its slug.
518 *
519 * @param string $slug Module slug.
520 */
521 public function get_path( $slug ) {
522 if ( ! Constants::is_defined( 'JETPACK__PLUGIN_DIR' ) ) {
523 return '';
524 }
525 /**
526 * Filters the path of a modules.
527 *
528 * @since 7.4.0
529 *
530 * @param array $return The absolute path to a module's root php file
531 * @param string $slug The module slug
532 */
533 return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug );
534 }
535
536 /**
537 * Saves all the currently active modules to options.
538 * Also fires Action hooks for each newly activated and deactivated module.
539 *
540 * @param array $modules Array of active modules to be saved in options.
541 *
542 * @return bool $success true for success, false for failure.
543 */
544 public function update_active( $modules ) {
545 $current_modules = \Jetpack_Options::get_option( 'active_modules', array() );
546 $active_modules = $this->get_active();
547 $new_active_modules = array_diff( $modules, $current_modules );
548 $new_inactive_modules = array_diff( $active_modules, $modules );
549 $new_current_modules = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
550 $reindexed_modules = array_values( $new_current_modules );
551 $success = \Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
552 // Let's take `pre_update_option_jetpack_active_modules` filter into account
553 // and actually decide for which modules we need to fire hooks by comparing
554 // the 'active_modules' option before and after the update.
555 $current_modules_post_update = \Jetpack_Options::get_option( 'active_modules', array() );
556
557 $new_inactive_modules = array_diff( $current_modules, $current_modules_post_update );
558 $new_inactive_modules = array_unique( $new_inactive_modules );
559 $new_inactive_modules = array_values( $new_inactive_modules );
560
561 $new_active_modules = array_diff( $current_modules_post_update, $current_modules );
562 $new_active_modules = array_unique( $new_active_modules );
563 $new_active_modules = array_values( $new_active_modules );
564
565 foreach ( $new_active_modules as $module ) {
566 /**
567 * Fires when a specific module is activated.
568 *
569 * @since 1.9.0
570 *
571 * @param string $module Module slug.
572 * @param boolean $success whether the module was activated. @since 4.2
573 */
574 do_action( 'jetpack_activate_module', $module, $success );
575 /**
576 * Fires when a module is activated.
577 * The dynamic part of the filter, $module, is the module slug.
578 *
579 * @since 1.9.0
580 *
581 * @param string $module Module slug.
582 */
583 do_action( "jetpack_activate_module_$module", $module );
584 }
585
586 foreach ( $new_inactive_modules as $module ) {
587 /**
588 * Fired after a module has been deactivated.
589 *
590 * @since 4.2.0
591 *
592 * @param string $module Module slug.
593 * @param boolean $success whether the module was deactivated.
594 */
595 do_action( 'jetpack_deactivate_module', $module, $success );
596 /**
597 * Fires when a module is deactivated.
598 * The dynamic part of the filter, $module, is the module slug.
599 *
600 * @since 1.9.0
601 *
602 * @param string $module Module slug.
603 */
604 do_action( "jetpack_deactivate_module_$module", $module );
605 }
606
607 return $success;
608 }
609 }
610