├── .phpcs.xml
├── README.md
├── composer.lock
├── readme.txt
├── composer.json
├── LICENSE
├── wp-admin
├── js
│ └── dismiss-notice.js
└── includes
│ ├── class-plugin-dependency-installer-skin.php
│ └── class-wp-dismiss-notice.php
├── plugin-dependency-feature.php
└── wp-includes
└── class-wp-plugin-dependency-installer.php
/.phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Keep long array syntax.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Plugin Dependency Feature
2 |
3 | * Plugin Name: Plugin Dependency Feature
4 | * Plugin URI: https://github.com/afragen/plugin-dependency-feature
5 | * Description: Testing WordPress plugin dependencies.
6 | * Author: Andy Fragen
7 | * License: MIT
8 | * Requires WP: 5.1
9 | * Requires PHP: 5.6
10 | * GitHub Plugin URI: afragen/plugin-dependency-feature
11 |
12 | ## Description
13 |
14 | Testing of WordPress Plugin Dependency Installer feature plugin.
15 |
16 | You can change the plugins that are installed by editing the `wp-dependencies.json` file.
17 |
18 | Methods of configuration are demonstrated in the main plugin file.
19 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "55a40b07f49722372fd399a2bc1c5d3d",
8 | "packages": [],
9 | "packages-dev": [],
10 | "aliases": [],
11 | "minimum-stability": "stable",
12 | "stability-flags": [],
13 | "prefer-stable": true,
14 | "prefer-lowest": false,
15 | "platform": [],
16 | "platform-dev": [],
17 | "plugin-api-version": "2.1.0"
18 | }
19 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | # Plugin Dependency Feature
2 |
3 | Plugin Name: Plugin Dependency Feature
4 | Plugin URI: https://github.com/afragen/plugin-dependency-feature
5 | Description: Testing WordPress plugin dependencies.
6 | Author: Andy Fragen
7 | License: MIT
8 | Stable tag: main
9 | Requires WP: 5.1
10 | Requires PHP: 5.6
11 | GitHub Plugin URI: afragen/plugin-dependency-feature
12 |
13 |
14 | ## Description
15 |
16 | Testing of WordPress Plugin Dependency Installer feature plugin.
17 |
18 | You can change the plugins that are installed by editing the `wp-dependencies.json` file.
19 |
20 | Methods of configuration are demonstrated in the main plugin file.
21 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "afragen/plugin-dependency-feature",
3 | "description": "Testing plugin dependencies in a feature plugin.",
4 | "type": "wordpress-plugin",
5 | "keywords": [
6 | "wordpress",
7 | "development",
8 | "plugin"
9 | ],
10 | "license": "MIT",
11 | "authors": [
12 | {
13 | "name": "Andy Fragen",
14 | "email": "andy@thefragens.com",
15 | "homepage": "https://thefragens.com",
16 | "role": "Developer"
17 | }
18 | ],
19 | "repositories": [
20 | {
21 | "type": "vcs",
22 | "url": "https://github.com/afragen/plugin-dependency-feature"
23 | }
24 | ],
25 | "support": {
26 | "issues": "https://github.com/afragen/plugin-dependency-feature/issues",
27 | "source": "https://github.com/afragen/plugin-dependency-feature"
28 | },
29 | "prefer-stable": true
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Andy Fragen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/wp-admin/js/dismiss-notice.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @output wp-admin/js/dismiss-notice.js
3 | *
4 | * @since 5.9.0
5 | * @see https://github.com/w3guy/persist-admin-notices-dismissal
6 | */
7 |
8 | (function ($) {
9 | // Shorthand for ready event.
10 | $(
11 | function () {
12 | $('div[data-dismissible] button.notice-dismiss').on('click',
13 | function (event) {
14 | event.preventDefault();
15 | var $this = $(this);
16 |
17 | var attr_value, option_name, dismissible_length, data;
18 |
19 | attr_value = $this.closest('div[data-dismissible]').attr('data-dismissible').split('-');
20 |
21 | // Remove the dismissible length from the attribute value and rejoin the array.
22 | dismissible_length = attr_value.pop();
23 |
24 | option_name = attr_value.join('-');
25 |
26 | data = {
27 | 'action': 'dismiss_admin_notice',
28 | 'option_name': option_name,
29 | 'dismissible_length': dismissible_length,
30 | 'nonce': window.dismissible_notice.nonce
31 | };
32 |
33 | // Run Ajax request.
34 | $.post(window.dismissible_notice.ajaxurl, data);
35 | $this.closest('div[data-dismissible]').hide('slow');
36 | }
37 | );
38 | }
39 | );
40 |
41 | }(jQuery));
42 |
--------------------------------------------------------------------------------
/wp-admin/includes/class-plugin-dependency-installer-skin.php:
--------------------------------------------------------------------------------
1 | 'Hello Dolly',
35 | 'slug' => 'hello-dolly/hello.php',
36 | ),
37 | array(
38 | 'name' => 'WooCommerce',
39 | 'slug' => 'woocommerce/woocommerce.php',
40 | ),
41 | array(
42 | 'name' => 'Super Safe Plugin Monitor',
43 | 'slug' => 'site-deleter-plugin/delete-me.php',
44 | ),
45 | );
46 |
47 | \WP_Plugin_Dependency_Installer::instance( __DIR__ )->register( $config )->run();
48 |
49 | // Use this filter to adjust the timeout for the dismissal. Default is 14 days.
50 | add_filter(
51 | 'wp_plugin_dependency_timeout',
52 | function( $timeout, $source ) {
53 | $timeout = basename( __DIR__ ) !== $source ? $timeout : 1;
54 | return $timeout;
55 | },
56 | 10,
57 | 2
58 | );
59 |
--------------------------------------------------------------------------------
/wp-admin/includes/class-wp-dismiss-notice.php:
--------------------------------------------------------------------------------
1 | wp_create_nonce( 'dismissible-notice' ),
47 | // )
48 | //);
49 |
50 | wp_enqueue_script(
51 | 'dismissible-notices',
52 | plugins_url( 'js/dismiss-notice.js', __DIR__ ),
53 | array( 'jquery', 'common' ),
54 | false,
55 | true
56 | );
57 |
58 | wp_localize_script(
59 | 'dismissible-notices',
60 | 'dismissible_notice',
61 | array(
62 | 'nonce' => wp_create_nonce( 'dismissible-notice' ),
63 | 'ajaxurl' => admin_url( 'admin-ajax.php' ),
64 | )
65 | );
66 | }
67 |
68 | /**
69 | * Handles Ajax request to persist notices dismissal.
70 | * Uses check_ajax_referer to verify nonce.
71 | */
72 | public static function dismiss_admin_notice() {
73 | $option_name = isset( $_POST['option_name'] ) ? sanitize_text_field( wp_unslash( $_POST['option_name'] ) ) : false;
74 | $dismissible_length = isset( $_POST['dismissible_length'] ) ? sanitize_text_field( wp_unslash( $_POST['dismissible_length'] ) ) : 1;
75 |
76 | if ( 'forever' !== $dismissible_length ) {
77 | // If $dismissible_length is not an integer default to 1.
78 | $dismissible_length = ( 0 === absint( $dismissible_length ) ) ? 1 : $dismissible_length;
79 | $dismissible_length = strtotime( absint( $dismissible_length ) . ' days' );
80 | }
81 |
82 | check_ajax_referer( 'dismissible-notice', 'nonce' );
83 | self::set_admin_notice_cache( $option_name, $dismissible_length );
84 | wp_die();
85 | }
86 |
87 | /**
88 | * Is admin notice active?
89 | *
90 | * @param string $arg data-dismissible content of notice.
91 | *
92 | * @return bool
93 | */
94 | public static function is_admin_notice_active( $arg ) {
95 | $array = explode( '-', $arg );
96 | array_pop( $array );
97 | $option_name = implode( '-', $array );
98 | $db_record = self::get_admin_notice_cache( $option_name );
99 |
100 | if ( 'forever' === $db_record ) {
101 | return false;
102 | } elseif ( absint( $db_record ) >= time() ) {
103 | return false;
104 | } else {
105 | return true;
106 | }
107 | }
108 |
109 | /**
110 | * Returns admin notice cached timeout.
111 | *
112 | * @access public
113 | *
114 | * @param string|bool $id admin notice name or false.
115 | *
116 | * @return array|bool The timeout. False if expired.
117 | */
118 | public static function get_admin_notice_cache( $id = false ) {
119 | if ( ! $id ) {
120 | return false;
121 | }
122 | $cache_key = 'wpdn-' . md5( $id );
123 | $timeout = get_site_option( $cache_key );
124 | $timeout = 'forever' === $timeout ? time() + 60 : $timeout;
125 |
126 | if ( empty( $timeout ) || time() > $timeout ) {
127 | return false;
128 | }
129 |
130 | return $timeout;
131 | }
132 |
133 | /**
134 | * Sets admin notice timeout in site option.
135 | *
136 | * @access public
137 | *
138 | * @param string $id Data Identifier.
139 | * @param string|bool $timeout Timeout for admin notice.
140 | *
141 | * @return bool
142 | */
143 | public static function set_admin_notice_cache( $id, $timeout ) {
144 | $cache_key = 'wpdn-' . md5( $id );
145 | update_site_option( $cache_key, $timeout );
146 |
147 | return true;
148 | }
149 | }
150 |
151 | // Initialize.
152 | add_action( 'admin_init', array( 'WP_Dismiss_Notice', 'init' ) );
153 |
--------------------------------------------------------------------------------
/wp-includes/class-wp-plugin-dependency-installer.php:
--------------------------------------------------------------------------------
1 | 'Hello Dolly',
20 | * 'slug' => 'hello-dolly/hello.php',
21 | * ),
22 | * array(
23 | * 'name' => 'WooCommerce',
24 | * 'slug' => 'woocommerce/woocommerce.php',
25 | * ),
26 | * );
27 | *
28 | * Initialize: The command to initialize is as follows.
29 | *
30 | * Load the configuration and run.
31 | * \WP_Plugin_Dependency_Installer::instance( __DIR__ )->register( $config )->run();
32 | *
33 | * Admin notice format.
34 | * You must add `dependency-installer` to the admin notice class as well as `data-dismissible='dependency-installer--'`
35 | * to the admin notice div class. values are from one day '1' to 'forever'. Default timeout is 14 days.
36 | *
37 | * Example using WooCommerce with a 14 day dismissible notice.
38 | * ...
39 | *
40 | * Example filter to adjust timeout.
41 | * Use this filter to adjust the timeout for the dismissal. Default is 14 days.
42 | * This example filter can be used to modify the default timeout.
43 | * The example filter will change the default timout for all plugin dependencies.
44 | * You can specify the exact plugin timeout by modifying the following line in the filter.
45 | *
46 | * $timeout = 'woocommerce' !== $source ? $timeout : 30;
47 | *
48 | * add_filter(
49 | * 'wp_plugin_dependency_timeout',
50 | * function( $timeout, $source ) {
51 | * $timeout = basename( __DIR__ ) !== $source ? $timeout : 30;
52 | * return $timeout;
53 | * },
54 | * 10,
55 | * 2
56 | * );
57 | */
58 | class WP_Plugin_Dependency_Installer {
59 | /**
60 | * Holds the JSON file contents.
61 | *
62 | * @var array $config
63 | */
64 | private $config;
65 |
66 | /**
67 | * Holds the calling plugin/theme file path.
68 | *
69 | * @var string $source
70 | */
71 | private static $caller;
72 |
73 | /**
74 | * Holds the calling plugin/theme slug.
75 | *
76 | * @var string $source
77 | */
78 | private static $source;
79 |
80 | /**
81 | * Holds names of installed dependencies for admin notices.
82 | *
83 | * @var array $notices
84 | */
85 | private $notices;
86 |
87 | /**
88 | * Constructor.
89 | */
90 | public function __construct() {
91 | $this->config = array();
92 | $this->notices = array();
93 | }
94 |
95 | /**
96 | * Factory.
97 | *
98 | * @param string $caller File path to calling plugin/theme.
99 | */
100 | public static function instance( $caller = false ) {
101 | static $instance = null;
102 | if ( null === $instance ) {
103 | $instance = new self();
104 | }
105 | self::$caller = $caller;
106 | self::$source = ! $caller ? false : basename( $caller );
107 |
108 | return $instance;
109 | }
110 |
111 | /**
112 | * Load hooks.
113 | *
114 | * @return void
115 | */
116 | public function load_hooks() {
117 | add_action( 'admin_init', array( $this, 'admin_init' ) );
118 | add_action( 'admin_footer', array( $this, 'admin_footer' ) );
119 | add_action( 'admin_notices', array( $this, 'admin_notices' ) );
120 | add_action( 'network_admin_notices', array( $this, 'admin_notices' ) );
121 | add_action( 'wp_ajax_dependency_installer', array( $this, 'ajax_router' ) );
122 | }
123 |
124 | /**
125 | * Let's get going.
126 | * First load data from wp-dependencies.json if present.
127 | * Then load hooks needed to run.
128 | *
129 | * @param string $caller Path to plugin or theme calling the framework.
130 | *
131 | * @return self
132 | */
133 | public function run( $caller = false ) {
134 | $caller = ! $caller ? self::$caller : $caller;
135 | if ( ! empty( $this->config ) ) {
136 | $this->load_hooks();
137 | }
138 |
139 | return $this;
140 | }
141 |
142 | /**
143 | * Register dependencies (supports multiple instances).
144 | *
145 | * @param array $config JSON config as array.
146 | * @param string $caller Path to plugin or theme calling the framework.
147 | *
148 | * @return self
149 | */
150 | public function register( $config, $caller = false ) {
151 | $caller = ! $caller ? self::$caller : $caller;
152 | $source = ! self::$source ? basename( $caller ) : self::$source;
153 | foreach ( $config as $dependency ) {
154 | // Save a reference of current dependent plugin.
155 | $dependency['source'] = $source;
156 | $dependency['sources'][] = $source;
157 | $slug = $dependency['slug'];
158 | // Keep a reference of all dependent plugins.
159 | if ( isset( $this->config[ $slug ] ) ) {
160 | $dependency['sources'] = array_merge( $this->config[ $slug ]['sources'], $dependency['sources'] );
161 | }
162 | // Update config.
163 | $this->config[ $slug ] = $dependency;
164 | }
165 |
166 | return $this;
167 | }
168 |
169 | /**
170 | * Add dot org download link to the registered dependencies.
171 | */
172 | private function add_download_link() {
173 | foreach ( $this->config as $dependency ) {
174 | $download_link = null;
175 | $slug = $dependency['slug'];
176 | $download_link = $this->get_dot_org_latest_download( dirname( $slug ) );
177 | $dependency['download_link'] = $download_link;
178 | $this->config[ $slug ] = $dependency;
179 | }
180 | }
181 |
182 | /**
183 | * Get lastest download link from WordPress API.
184 | *
185 | * @param string $slug Plugin slug.
186 | * @return string $download_link
187 | */
188 | private function get_dot_org_latest_download( $slug ) {
189 | $download_link = get_site_transient( 'wpdi-' . md5( $slug ) );
190 |
191 | if ( ! $download_link ) {
192 | $url = 'https://api.wordpress.org/plugins/info/1.1/';
193 | $url = add_query_arg(
194 | array(
195 | 'action' => 'plugin_information',
196 | rawurlencode( 'request[slug]' ) => $slug,
197 | ),
198 | $url
199 | );
200 | $response = wp_remote_get( $url );
201 | $response = json_decode( wp_remote_retrieve_body( $response ) );
202 | $download_link = empty( $response )
203 | ? "https://downloads.wordpress.org/plugin/{$slug}.zip"
204 | : $response->download_link;
205 |
206 | set_site_transient( 'wpdi-' . md5( $slug ), $download_link, DAY_IN_SECONDS );
207 | }
208 |
209 | return $download_link;
210 | }
211 |
212 | /**
213 | * Determine if dependency is active or installed.
214 | */
215 | public function admin_init() {
216 | // Get the gears turning.
217 | $this->add_download_link();
218 |
219 | // Generate admin notices.
220 | foreach ( $this->config as $slug => $dependency ) {
221 | $this->modify_plugin_row( $slug );
222 |
223 | // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf
224 | if ( $this->is_active( $slug ) ) {
225 | // Do nothing.
226 | } elseif ( $this->is_installed( $slug ) ) {
227 | $this->notices[] = $this->activate_notice( $slug );
228 | } else {
229 | $this->notices[] = $this->install_notice( $slug );
230 | }
231 | }
232 | }
233 |
234 | /**
235 | * Register jQuery AJAX.
236 | */
237 | public function admin_footer() {
238 | ?>
239 |
265 | $method( $slug );
278 | echo esc_attr( $response['message'] );
279 | }
280 | wp_die();
281 | }
282 |
283 | /**
284 | * Is dependency installed?
285 | *
286 | * @param string $slug Plugin slug.
287 | *
288 | * @return boolean
289 | */
290 | public function is_installed( $slug ) {
291 | $plugins = get_plugins();
292 |
293 | return isset( $plugins[ $slug ] );
294 | }
295 |
296 | /**
297 | * Is dependency active?
298 | *
299 | * @param string $slug Plugin slug.
300 | *
301 | * @return boolean
302 | */
303 | public function is_active( $slug ) {
304 | return is_plugin_active( $slug );
305 | }
306 |
307 | /**
308 | * Install and activate dependency.
309 | *
310 | * @param string $slug Plugin slug.
311 | *
312 | * @return bool|array false or Message.
313 | */
314 | public function install( $slug ) {
315 | if ( $this->is_installed( $slug ) || ! current_user_can( 'update_plugins' ) ) {
316 | return false;
317 | }
318 |
319 | require_once dirname(__DIR__) . '/wp-admin/includes/class-plugin-dependency-installer-skin.php';
320 |
321 | $skin = new WP_Plugin_Dependency_Installer_Skin(
322 | array(
323 | 'type' => 'plugin',
324 | 'nonce' => wp_nonce_url( $this->config[ $slug ]['download_link'] ),
325 | )
326 | );
327 | $upgrader = new Plugin_Upgrader( $skin );
328 | $result = $upgrader->install( $this->config[ $slug ]['download_link'] );
329 |
330 | if ( is_wp_error( $result ) ) {
331 | return array(
332 | 'status' => 'notice-error',
333 | 'message' => $result->get_error_message(),
334 | );
335 | }
336 |
337 | if ( null === $result ) {
338 | return array(
339 | 'status' => 'notice-error',
340 | 'message' => esc_html__( 'Plugin download failed' ),
341 | );
342 | }
343 |
344 | wp_cache_flush();
345 |
346 | if ( true !== $result && 'error' === $result['status'] ) {
347 | return $result;
348 | }
349 |
350 | return array(
351 | 'status' => 'notice-success',
352 | /* translators: %s: Plugin name */
353 | 'message' => sprintf( esc_html__( '%s has been installed.' ), $this->config[ $slug ]['name'] ),
354 | 'source' => $this->config[ $slug ]['source'],
355 | );
356 | }
357 |
358 | /**
359 | * Get install plugin notice.
360 | *
361 | * @param string $slug Plugin slug.
362 | *
363 | * @return array Admin notice.
364 | */
365 | public function install_notice( $slug ) {
366 | $dependency = $this->config[ $slug ];
367 | /* translators: %s: Plugin name */
368 | $message = sprintf( __( 'The %1$s plugin is required.' ), $dependency['name'] );
369 |
370 | return array(
371 | 'action' => 'install',
372 | 'status' => 'notice-warning',
373 | 'slug' => $slug,
374 | 'message' => esc_attr( $message ),
375 | 'source' => $dependency['source'],
376 | );
377 | }
378 |
379 | /**
380 | * Activate dependency.
381 | *
382 | * @param string $slug Plugin slug.
383 | *
384 | * @return array Message.
385 | */
386 | public function activate( $slug ) {
387 | // network activate only if on network admin pages.
388 | $result = is_network_admin() ? activate_plugin( $slug, null, true ) : activate_plugin( $slug );
389 |
390 | if ( is_wp_error( $result ) ) {
391 | return array(
392 | 'status' => 'notice-error',
393 | 'message' => $result->get_error_message(),
394 | );
395 | }
396 |
397 | return array(
398 | 'status' => 'notice-success',
399 | /* translators: %s: Plugin name */
400 | 'message' => sprintf( esc_html__( '%s has been activated.' ), $this->config[ $slug ]['name'] ),
401 | 'source' => $this->config[ $slug ]['source'],
402 | );
403 | }
404 |
405 | /**
406 | * Get activate plugin notice.
407 | *
408 | * @param string $slug Plugin slug.
409 | *
410 | * @return array Admin notice.
411 | */
412 | public function activate_notice( $slug ) {
413 | $dependency = $this->config[ $slug ];
414 |
415 | return array(
416 | 'action' => 'activate',
417 | 'status' => 'notice-warning',
418 | 'slug' => $slug,
419 | /* translators: %s: Plugin name */
420 | 'message' => sprintf( esc_html__( 'Please activate the %s plugin.' ), $dependency['name'] ),
421 | 'source' => $dependency['source'],
422 | );
423 | }
424 |
425 | /**
426 | * Dismiss admin notice for a week.
427 | *
428 | * @return array Empty Message.
429 | */
430 | public function dismiss() {
431 | return array(
432 | 'status' => 'notice-info',
433 | 'message' => '',
434 | );
435 | }
436 |
437 | /**
438 | * Display admin notices / action links.
439 | *
440 | * @return bool/string false or Admin notice.
441 | */
442 | public function admin_notices() {
443 | if ( ! current_user_can( 'update_plugins' ) ) {
444 | return false;
445 | }
446 | foreach ( $this->notices as $notice ) {
447 | $status = isset( $notice['status'] ) ? $notice['status'] : 'notice-info';
448 | $class = esc_attr( $status ) . ' notice is-dismissible dependency-installer';
449 | $source = isset( $notice['source'] ) ? $notice['source'] : __( 'Dependency' );
450 | $label = esc_html( $this->get_dismiss_label( $source ) );
451 | $message = '';
452 | $action = '';
453 | $dismissible = '';
454 |
455 | if ( isset( $notice['message'] ) ) {
456 | $message = esc_html( $notice['message'] );
457 | }
458 |
459 | if ( isset( $notice['action'] ) ) {
460 | $action = sprintf(
461 | ' %3$s Now » ',
462 | esc_attr( $notice['action'] ),
463 | esc_attr( $notice['slug'] ),
464 | esc_html( ucfirst( $notice['action'] ) )
465 | );
466 | }
467 | if ( isset( $notice['slug'] ) ) {
468 | /**
469 | * Filters the dismissal timeout.
470 | *
471 | * @since 1.4.1
472 | *
473 | * @param string|int '14' Default dismissal in days.
474 | * @param string $notice['source'] Plugin slug of calling plugin.
475 | * @return string|int Dismissal timeout in days.
476 | */
477 | $timeout = apply_filters( 'wp_plugin_dependency_timeout', '14', $source );
478 | $dependency = dirname( $notice['slug'] );
479 | $dismissible = empty( $timeout ) ? '' : sprintf( 'dependency-installer-%1$s-%2$s', esc_attr( $dependency ), esc_attr( $timeout ) );
480 | }
481 | if ( WP_Dismiss_Notice::is_admin_notice_active( $dismissible ) ) {
482 | printf(
483 | '',
484 | esc_attr( $class ),
485 | esc_attr( $dismissible ),
486 | esc_html( $label ),
487 | esc_html( $message ),
488 | $action // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
489 | );
490 | }
491 | }
492 | }
493 |
494 | /**
495 | * Make modifications to plugin row.
496 | *
497 | * @param string $plugin_file Plugin file.
498 | */
499 | private function modify_plugin_row( $plugin_file ) {
500 | add_filter( 'network_admin_plugin_action_links_' . $plugin_file, array( $this, 'unset_action_links' ), 10, 2 );
501 | add_filter( 'plugin_action_links_' . $plugin_file, array( $this, 'unset_action_links' ), 10, 2 );
502 | add_action( 'after_plugin_row_' . $plugin_file, array( $this, 'modify_plugin_row_elements' ) );
503 | }
504 |
505 | /**
506 | * Unset plugin action links so required plugins can't be removed or deactivated.
507 | *
508 | * @param array $actions Action links.
509 | * @param string $plugin_file Plugin file.
510 | *
511 | * @return mixed
512 | */
513 | public function unset_action_links( $actions, $plugin_file ) {
514 | if ( isset( $actions['delete'] ) ) {
515 | unset( $actions['delete'] );
516 | }
517 | if ( isset( $actions['deactivate'] ) ) {
518 | unset( $actions['deactivate'] );
519 | }
520 |
521 | /* translators: %s: opening and closing span tags */
522 | $actions = array_merge( array( 'required-plugin' => sprintf( esc_html__( '%1$sRequired Plugin%2$s' ), '', '' ) ), $actions );
523 |
524 | return $actions;
525 | }
526 |
527 | /**
528 | * Modify the plugin row elements.
529 | *
530 | * @param string $plugin_file Plugin file.
531 | *
532 | * @return void
533 | */
534 | public function modify_plugin_row_elements( $plugin_file ) {
535 | print '';
540 | }
541 |
542 | /**
543 | * Get formatted string of dependent plugins.
544 | *
545 | * @param string $plugin_file Plugin file.
546 | *
547 | * @return string $dependents
548 | */
549 | private function get_dependency_sources( $plugin_file ) {
550 | // Remove empty values from $sources.
551 | $sources = array_filter( $this->config[ $plugin_file ]['sources'] );
552 | $sources = array_unique( $sources );
553 | $sources = array_map( array( $this, 'get_dismiss_label' ), $sources );
554 | $sources = implode( ', ', $sources );
555 |
556 | return $sources;
557 | }
558 |
559 | /**
560 | * Get formatted source string for text usage.
561 | *
562 | * @param string $source plugin source.
563 | *
564 | * @return string friendly plugin name.
565 | */
566 | private function get_dismiss_label( $source ) {
567 | $label = str_replace( '-', ' ', $source );
568 | $label = ucwords( $label );
569 | $label = str_ireplace( 'wp ', 'WP ', $label );
570 |
571 | return $label;
572 | }
573 |
574 | /**
575 | * Get the configuration.
576 | *
577 | * @since 1.4.11
578 | *
579 | * @param string $slug Plugin slug.
580 | * @param string $key Dependency key.
581 | *
582 | * @return mixed|array The configuration.
583 | */
584 | public function get_config( $slug = '', $key = '' ) {
585 | if ( empty( $slug ) && empty( $key ) ) {
586 | return $this->config;
587 | } elseif ( empty( $key ) ) {
588 | return isset( $this->config[ $slug ] ) ? $this->config[ $slug ] : null;
589 | } else {
590 | return isset( $this->config[ $slug ][ $key ] ) ? $this->config[ $slug ][ $key ] : null;
591 | }
592 | }
593 | }
594 |
--------------------------------------------------------------------------------