├── CHANGES.md ├── composer.json ├── LICENSE ├── README.md ├── readme.txt ├── plugin.php └── wp-admin └── includes └── class-wp-plugin-dependencies.php /CHANGES.md: -------------------------------------------------------------------------------- 1 | [unreleased] 2 | 3 | #### 0.13.0 4 | * add plugin cards for slugs with no API data 5 | * hide action links and bottom of card in plugin cards for slugs with no API data 6 | * switch to WordPress/wp-plugin-dependencies 7 | 8 | #### 0.12.3 9 | *rename and reschuffle some functions 10 | 11 | #### 0.12.2 / 2022-04-06 12 | * harden a bit 13 | * clean up some testing stuff 14 | * `plugin_install_description` filter committed to core 15 | 16 | #### 0.12.0 / 2022-04-03 17 | * readme.txt 18 | * fix PHP error if no plugins with `Requires Plugins` header found 19 | * only show single, relevant admin notice 20 | 21 | #### 0.11.6.4 22 | * plugin to date with new changelog 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordpress/wp-plugin-dependencies", 3 | "description": "Parses 'Requires Plugin' header, add plugin install dependencies tab, and information about dependencies.", 4 | "type": "wordpress-plugin", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Andy Fragen", 9 | "email": "andy@thefragens.com", 10 | "homepage": "https://thefragens.com", 11 | "role": "Developer" 12 | } 13 | ], 14 | "support": { 15 | "issues": "https://github.com/WordPress/wp-plugin-dependencies/issues", 16 | "source": "https://github.com/WordPress/wp-plugin-dependencies" 17 | }, 18 | "prefer-stable": true, 19 | "require": { 20 | "php": ">=5.6" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code now at https://github.com/WordPress/wp-plugin-dependencies 2 | 3 | # Plugin Dependencies 4 | 5 | * Plugin Name: Plugin Dependencies 6 | * Plugin URI: https://github.com/afragen/plugin-dependencies-tab 7 | * Description: Parses 'Requires Plugins' header, add plugin install dependencies tab, and information about dependencies. 8 | * Author: Andy Fragen 9 | * License: MIT 10 | * Network: true 11 | * Requires at least: 5.2 12 | * Requires PHP: 5.6 13 | * Stable tag: master 14 | 15 | ## Descripton 16 | 17 | Parses a 'Requires Plugins' header and adds a Dependencies tab in the plugin install page. If a requiring plugin does not have all it's dependencies installed and active, it will not activate. 18 | 19 | My solution to [#22316](https://core.trac.wordpress.org/ticket/22316). Feature plugin version of [PR #1724](https://github.com/WordPress/wordpress-develop/pull/1724) 20 | 21 | * Parses the **Requires Plugins** header that defines plugin dependencies using a comma separated list of wp.org slugs. 22 | * Displays a single admin notice with link to **Plugins > Add New > Dependencies** if not all plugin dependencies have been installed. 23 | * Adds a new view/tab to plugins install page ( **Plugins > Add New** ) titled **Dependencies** that contains plugin cards for all plugin dependencies. 24 | * This view also lists which plugins require which plugin dependencies in the plugin card, though that feature requires the filter below to function. 😅 25 | * In the plugins page, a dependent plugin is unable to be deleted or deactivated if the requiring plugin is active. 26 | * Plugin dependencies can be deactivated or deleted if the requiring plugin is not active. 27 | * Messaging in the plugin row description is inserted; as is data noting which plugins require the dependency. 28 | * If the dependency API data is not available a generic plugin card will be displayed in the Dependencies tab. 29 | 30 | * Ensures that plugins with unmet dependencies cannot be activated. 31 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | # Plugin Dependencies 2 | 3 | Plugin Name: Plugin Dependencies 4 | Plugin URI: https://github.com/afragen/plugin-dependencies-tab 5 | Description: Parses 'Requires Plugins' header, add plugin install dependencies tab, and information about dependencies. 6 | Author: Andy Fragen 7 | License: MIT 8 | Network: true 9 | Requires at least: 5.2 10 | Requires PHP: 5.6 11 | Tested up to: 6.0 12 | Stable tag: trunk 13 | 14 | ## Descripton 15 | 16 | Parses a 'Requires Plugins' header and adds a Dependencies tab in the plugin install page. If a requiring plugin does not have all it's dependencies installed and active, it will not activate. 17 | 18 | My solution to [#22316](https://core.trac.wordpress.org/ticket/22316). Feature plugin version of [PR #1724](https://github.com/WordPress/wordpress-develop/pull/1724) 19 | 20 | * Parses the **Requires Plugins** header that defines plugin dependencies using a comma separated list of wp.org slugs. 21 | * Adds a new view/tab to plugins install page ( **Plugins > Add New** ) titled **Dependencies** that contains plugin cards for all plugin dependencies. 22 | * This view also lists which plugins require which plugin dependencies in the plugin card, though that feature requires the filter below to function. 😅 23 | * In the plugins page, a dependent plugin is unable to be deleted or deactivated if the requiring plugin is active. 24 | * Plugin dependencies can be deactivated or deleted if the requiring plugin is not active. 25 | * Messaging in the plugin row description is inserted; as is data noting which plugins require the dependency. 26 | * Displays a single admin notice with link to **Plugins > Add New > Dependencies** if not all plugin dependencies have been installed. 27 | * Ensures that plugins with unmet dependencies cannot be activated. 28 | * If the dependency API data is not available a generic plugin card will be displayed in the Dependencies tab. 29 | 30 | ## Screenshots 31 | 32 | 1. Plugins page 33 | 2. Plugin Dependencies tab 34 | 35 | ## Changelog 36 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | Add New'. 55 | * 56 | * @param array $tabs Array of plugin install tabs. 57 | * 58 | * @return array 59 | */ 60 | public function add_install_tab( $tabs ) { 61 | $tabs['dependencies'] = _x( 'Dependencies', 'Plugin Installer' ); 62 | 63 | return $tabs; 64 | } 65 | 66 | /** 67 | * Add args to plugins_api(). 68 | * 69 | * @param array $args Array of arguments to plugins_api(). 70 | * 71 | * @return array 72 | */ 73 | public function add_install_dependency_args( $args ) { 74 | $args = array( 75 | 'page' => 1, 76 | 'per_page' => 36, 77 | 'locale' => get_user_locale(), 78 | 'browse' => 'dependencies', 79 | ); 80 | 81 | return $args; 82 | } 83 | } 84 | 85 | new Init(); 86 | -------------------------------------------------------------------------------- /wp-admin/includes/class-wp-plugin-dependencies.php: -------------------------------------------------------------------------------- 1 | requires_plugins = array(); 51 | $this->plugin_data = array(); 52 | } 53 | 54 | /** 55 | * Initialize, load filters, and get started. 56 | * 57 | * @return void 58 | */ 59 | public function init() { 60 | if ( is_admin() && ! wp_doing_ajax() ) { 61 | add_filter( 'plugins_api_result', array( $this, 'plugins_api_result' ), 10, 3 ); 62 | add_filter( 'plugin_install_description', array( $this, 'plugin_install_description' ), 10, 2 ); 63 | add_action( 'admin_init', array( $this, 'modify_plugin_row' ) ); 64 | add_action( 'admin_notices', array( $this, 'admin_notices' ) ); 65 | add_action( 'network_admin_notices', array( $this, 'admin_notices' ) ); 66 | add_action( 'in_admin_header', array( $this, 'hide_action_links' ) ); 67 | 68 | // TODO: $this->get_dot_org_data() for core PR. 69 | add_action( 'plugins_loaded', array( $this, 'get_dot_org_data' ) ); 70 | 71 | $required_headers = $this->parse_headers(); 72 | $this->slugs = $this->sanitize_required_headers( $required_headers ); 73 | $this->deactivate_unmet_dependencies(); 74 | } 75 | } 76 | 77 | /** 78 | * Run get_plugins() and store result. 79 | * 80 | * @return array 81 | */ 82 | public function get_plugins() { 83 | if ( ! function_exists( 'get_plugins' ) ) { 84 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; 85 | } 86 | $this->plugins = get_plugins(); 87 | 88 | return $this->plugins; 89 | } 90 | 91 | /** 92 | * Parse 'Requires Plugins' header. 93 | * Store result with dependent plugin. 94 | * 95 | * @return \stdClass 96 | */ 97 | public function parse_headers() { 98 | $this->get_plugins(); 99 | $required_headers = array(); 100 | foreach ( array_keys( $this->plugins ) as $plugin ) { 101 | $requires_plugins = get_file_data( WP_PLUGIN_DIR . '/' . $plugin, array( 'RequiresPlugins' => 'Requires Plugins' ) ); 102 | if ( ! empty( $requires_plugins['RequiresPlugins'] ) ) { 103 | $required_headers[ $plugin ] = $requires_plugins; 104 | $sanitized_requires_slugs = implode( ', ', $this->sanitize_required_headers( $required_headers ) ); 105 | 106 | $this->requires_plugins[ $plugin ]['RequiresPlugins'] = $sanitized_requires_slugs; 107 | } 108 | } 109 | 110 | return $required_headers; 111 | } 112 | 113 | /** 114 | * Sanitize headers. 115 | * 116 | * @param array $required_headers Array of required plugin headers. 117 | * @return array 118 | */ 119 | public function sanitize_required_headers( $required_headers ) { 120 | $all_slugs = array(); 121 | foreach ( $required_headers as $key => $headers ) { 122 | $sanitized_slugs = array(); 123 | $exploded = explode( ',', $headers['RequiresPlugins'] ); 124 | foreach ( $exploded as $slug ) { 125 | $slug = trim( $slug ); 126 | 127 | // Match to dot org slug format. 128 | if ( preg_match( '/^[a-z0-9-]+$/', $slug ) ) { 129 | $sanitized_slugs[] = $slug; 130 | } 131 | } 132 | $sanitized_slugs = array_unique( $sanitized_slugs ); 133 | $this->plugins[ $key ]['RequiresPlugins'] = $sanitized_slugs; 134 | $all_slugs = array_merge( $all_slugs, $sanitized_slugs ); 135 | } 136 | $all_slugs = array_unique( $all_slugs ); 137 | sort( $all_slugs ); 138 | 139 | return $all_slugs; 140 | } 141 | 142 | /** 143 | * Deactivate plugins with unmet dependencies. 144 | * 145 | * @return void 146 | */ 147 | public function deactivate_unmet_dependencies() { 148 | $dependencies = $this->get_dependency_filepaths(); 149 | $deactivate_requires = array(); 150 | 151 | foreach ( array_keys( $this->requires_plugins ) as $requires ) { 152 | if ( array_key_exists( $requires, $this->plugins ) ) { 153 | $plugin_dependencies = $this->plugins[ $requires ]['RequiresPlugins']; 154 | foreach ( $plugin_dependencies as $plugin_dependency ) { 155 | if ( is_plugin_active( $requires ) ) { 156 | if ( ! $dependencies[ $plugin_dependency ] || is_plugin_inactive( $dependencies[ $plugin_dependency ] ) ) { 157 | $deactivate_requires[] = $requires; 158 | } 159 | } 160 | } 161 | } 162 | } 163 | 164 | $deactivate_requires = array_unique( $deactivate_requires ); 165 | deactivate_plugins( $deactivate_requires ); 166 | set_site_transient( 'wp_plugin_dependencies_deactivate_plugins', $deactivate_requires, 10 ); 167 | } 168 | 169 | /** 170 | * Modify plugins_api() response. 171 | * 172 | * @param \stdClas $res Object of results. 173 | * @param string $action Variable for plugins_api(). 174 | * @param \stdClass $args Object of plugins_api() args. 175 | * 176 | * @return \stdClass 177 | */ 178 | public function plugins_api_result( $res, $action, $args ) { 179 | if ( property_exists( $args, 'browse' ) && 'dependencies' === $args->browse ) { 180 | $res->info = array( 181 | 'page' => 1, 182 | 'pages' => 1, 183 | 'results' => count( (array) $this->plugin_data ), 184 | ); 185 | 186 | $res->plugins = $this->plugin_data; 187 | } 188 | 189 | return $res; 190 | } 191 | 192 | /** 193 | * Get plugin data from WordPress API. 194 | * Store result in $this->plugin_data. 195 | */ 196 | public function get_dot_org_data() { 197 | global $pagenow; 198 | $pages = array( 'plugin-install.php', 'plugins.php' ); 199 | if ( ! in_array( $pagenow, $pages, true ) ) { 200 | return; 201 | } 202 | 203 | $this->plugin_data = (array) get_site_transient( 'wp_plugin_dependencies_plugin_data' ); 204 | foreach ( $this->slugs as $key => $slug ) { 205 | // Don't hit plugins API if data exists. 206 | if ( array_key_exists( $slug, (array) $this->plugin_data ) ) { 207 | continue; 208 | } 209 | if ( ! function_exists( 'plugins_api' ) ) { 210 | require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; 211 | } 212 | $args = array( 213 | 'slug' => $slug, 214 | 'fields' => array( 215 | 'short_description' => true, 216 | 'icons' => true, 217 | ), 218 | ); 219 | $response = plugins_api( 'plugin_information', $args ); 220 | 221 | // If a proper slug is present but has no plugin data, generic data will be returned. 222 | $response = $this->get_empty_plugins_api_response( $response, $args ); 223 | 224 | if ( is_wp_error( $response ) ) { 225 | continue; 226 | } 227 | 228 | $this->plugin_data[ $response->slug ] = (array) $response; 229 | 230 | if ( ! in_array( $slug, $this->slugs, true ) ) { 231 | unset( $this->plugin_data[ $key ] ); 232 | } 233 | } 234 | 235 | // Remove from $this->plugin_data if slug no longer a dependency. 236 | $differences = array_diff( array_keys( $this->plugin_data ), $this->slugs ); 237 | if ( ! empty( $differences ) ) { 238 | foreach ( $differences as $difference ) { 239 | unset( $this->plugin_data[ $difference ] ); 240 | } 241 | } 242 | 243 | ksort( $this->plugin_data ); 244 | set_site_transient( 'wp_plugin_dependencies_plugin_data', $this->plugin_data, 12 * HOUR_IN_SECONDS ); 245 | } 246 | 247 | /** 248 | * Modify the plugin row. 249 | * 250 | * @return void 251 | */ 252 | public function modify_plugin_row() { 253 | global $pagenow; 254 | if ( 'plugins.php' !== $pagenow ) { 255 | return; 256 | } 257 | 258 | $dependency_paths = $this->get_dependency_filepaths(); 259 | foreach ( $dependency_paths as $plugin_file ) { 260 | if ( $plugin_file ) { 261 | $this->modify_dependency_plugin_row( $plugin_file ); 262 | } 263 | } 264 | foreach ( array_keys( $this->requires_plugins ) as $plugin_file ) { 265 | $this->modify_requires_plugin_row( $plugin_file ); 266 | } 267 | } 268 | 269 | /** 270 | * Actually make modifications to plugin row of plugin dependencies. 271 | * 272 | * @param string $plugin_file Plugin file. 273 | */ 274 | public function modify_dependency_plugin_row( $plugin_file ) { 275 | add_filter( 'network_admin_plugin_action_links_' . $plugin_file, array( $this, 'unset_action_links' ), 10, 2 ); 276 | add_filter( 'plugin_action_links_' . $plugin_file, array( $this, 'unset_action_links' ), 10, 2 ); 277 | add_action( 'after_plugin_row_' . $plugin_file, array( $this, 'modify_plugin_row_elements' ), 10, 2 ); 278 | } 279 | 280 | /** 281 | * Actually make modifications to plugin row of requiring plugin. 282 | * 283 | * @param string $plugin_file Plugin file. 284 | * 285 | * @return void 286 | */ 287 | public function modify_requires_plugin_row( $plugin_file ) { 288 | add_action( 'after_plugin_row_' . $plugin_file, array( $this, 'modify_plugin_row_elements_requires' ), 10, 1 ); 289 | } 290 | 291 | /** 292 | * Modify the plugin row elements. 293 | * Removes plugin row checkbox. 294 | * Adds 'Required by: ...' information. 295 | * 296 | * @param string $plugin_file Plugin file. 297 | * @param array $plugin_data Array of plugin data. 298 | * 299 | * @return void 300 | */ 301 | public function modify_plugin_row_elements( $plugin_file, $plugin_data ) { 302 | print ''; 306 | } 307 | 308 | /** 309 | * Modify the plugin row elements. 310 | * Add `Requires: ...` information 311 | * 312 | * @param string $plugin_file Plugin file. 313 | * 314 | * @return void 315 | */ 316 | public function modify_plugin_row_elements_requires( $plugin_file ) { 317 | $this->plugin_data = get_site_transient( 'wp_plugin_dependencies_plugin_data' ); 318 | 319 | // Exit if no plugin data found. 320 | if ( empty( $this->plugin_data ) ) { 321 | return; 322 | } 323 | 324 | $requires = $this->plugins[ $plugin_file ]['RequiresPlugins']; 325 | foreach ( $requires as $require ) { 326 | if ( isset( $this->plugin_data[ $require ] ) ) { 327 | $names[] = $this->plugin_data[ $require ]['name']; 328 | } 329 | } 330 | if ( ! empty( $names ) ) { 331 | $names = implode( ', ', $names ); 332 | print ''; 335 | } 336 | } 337 | 338 | /** 339 | * Unset plugin action links so required plugins can't be removed or deactivated. 340 | * Only when the requiring plugin is active. 341 | * 342 | * @param array $actions Action links. 343 | * @param string $plugin_file Plugin file. 344 | * 345 | * @return array 346 | */ 347 | public function unset_action_links( $actions, $plugin_file ) { 348 | foreach ( $this->requires_plugins as $plugin => $requires ) { 349 | $dependents = explode( ',', $requires['RequiresPlugins'] ); 350 | if ( is_plugin_active( $plugin ) && in_array( dirname( $plugin_file ), $dependents, true ) ) { 351 | if ( isset( $actions['delete'] ) ) { 352 | unset( $actions['delete'] ); 353 | } 354 | if ( isset( $actions['deactivate'] ) ) { 355 | unset( $actions['deactivate'] ); 356 | } 357 | } 358 | } 359 | 360 | return $actions; 361 | } 362 | 363 | /** 364 | * Add 'Required by: ...' to plugin install dependencies view. 365 | * 366 | * @param string $description Short description of plugin. 367 | * @param array $plugin Array of plugin data. 368 | * 369 | * @return string 370 | */ 371 | public function plugin_install_description( $description, $plugin ) { 372 | $required = null; 373 | if ( in_array( $plugin['slug'], array_keys( $this->plugin_data ), true ) ) { 374 | $dependents = $this->get_dependency_sources( $plugin ); 375 | $required = '' . __( 'Required by:' ) . ' ' . $dependents; 376 | $description = $description . '

' . $required . '

'; 377 | } 378 | 379 | return $description; 380 | } 381 | 382 | /** 383 | * Display admin notice if dependencies not installed. 384 | * 385 | * @return void 386 | */ 387 | public function admin_notices() { 388 | // Plugin deactivated if dependencies not met. 389 | // Transient on a 10 second timeout. 390 | $deactivate_requires = get_site_transient( 'wp_plugin_dependencies_deactivate_plugins' ); 391 | if ( ! empty( $deactivate_requires ) ) { 392 | foreach ( $deactivate_requires as $deactivated ) { 393 | $deactivated_plugins[] = $this->plugins[ $deactivated ]['Name']; 394 | } 395 | $deactivated_plugins = implode( ', ', $deactivated_plugins ); 396 | printf( 397 | '

' 398 | /* translators: 1: plugin names, 2: opening tag and link to Dependencies install page, 3: closing tag */ 399 | . esc_html__( '%1$s plugin(s) could not be activated. There are uninstalled or inactive dependencies. Go to the %2$sDependencies%3$s install page.' ) 400 | . '

', 401 | '' . esc_html( $deactivated_plugins ) . '', 402 | '', 403 | '' 404 | ); 405 | } else { 406 | // More dependencies to install. 407 | $installed_slugs = array_map( 'dirname', array_keys( $this->plugins ) ); 408 | $intersect = array_intersect( $this->slugs, $installed_slugs ); 409 | asort( $intersect ); 410 | if ( $intersect !== $this->slugs ) { 411 | printf( 412 | '

' 413 | /* translators: 1: opening tag and link to Dependencies install page, 2:closing tag */ 414 | . esc_html__( 'There are additional plugins that must be installed. Go to the %1$sDependencies%2$s install page.' ) 415 | . '

', 416 | '', 417 | '' 418 | ); 419 | } 420 | } 421 | } 422 | 423 | /** 424 | * Get filepath of installed dependencies. 425 | * If dependency is not installed filepath defaults to false. 426 | * 427 | * @return array 428 | */ 429 | private function get_dependency_filepaths() { 430 | $dependency_filepaths = array(); 431 | foreach ( $this->slugs as $slug ) { 432 | foreach ( array_keys( $this->plugins ) as $plugin ) { 433 | if ( false !== strpos( $plugin, trailingslashit( $slug ) ) ) { 434 | $dependency_filepaths[ $slug ] = $plugin; 435 | break; 436 | } else { 437 | $dependency_filepaths[ $slug ] = false; 438 | } 439 | } 440 | } 441 | 442 | return $dependency_filepaths; 443 | } 444 | 445 | /** 446 | * Get formatted string of dependent plugins. 447 | * 448 | * @param array $plugin_data Array of plugin data. 449 | * 450 | * @return string 451 | */ 452 | private function get_dependency_sources( $plugin_data ) { 453 | $sources = array(); 454 | foreach ( $this->plugins as $plugin ) { 455 | if ( ! empty( $plugin['RequiresPlugins'] ) ) { 456 | // Default TextDomain derived from plugin directory name, should be slug equivalent. 457 | $plugin_data['slug'] = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : $plugin_data['TextDomain']; 458 | if ( in_array( $plugin_data['slug'], $plugin['RequiresPlugins'], true ) ) { 459 | $sources[] = $plugin['Name']; 460 | } 461 | } 462 | } 463 | $sources = array_unique( $sources ); 464 | sort( $sources ); 465 | $sources = implode( ', ', $sources ); 466 | 467 | return $sources; 468 | } 469 | 470 | /** 471 | * Return empty plugins_api() response. 472 | * 473 | * @param \stdClass|WP_Error $response Response from plugins_api(). 474 | * @param array $args Array of arguments passed to plugins_api(). 475 | * 476 | * @return \stdClass 477 | */ 478 | public function get_empty_plugins_api_response( $response, $args ) { 479 | if ( is_wp_error( $response ) ) { 480 | $response = array( 481 | 'name' => $args['slug'], 482 | 'slug' => $args['slug'], 483 | 'version' => '', 484 | 'author' => '', 485 | 'contributors' => '', 486 | 'requires' => '', 487 | 'tested' => '', 488 | 'requires_php' => '', 489 | 'sections' => array( 'description' => '' ), 490 | 'short_description' => __( 'This plugin has no API data. Please contact the plugin developer and ask them to integrate with plugin dependencies.' ), 491 | 'download_link' => '', 492 | 'banners' => array(), 493 | 'icons' => array( 'default' => "https://s.w.org/plugins/geopattern-icon/{$args['slug']}.svg" ), 494 | 'last_updated' => '', 495 | 'num_ratings' => 0, 496 | 'rating' => 0, 497 | 'active_installs' => 0, 498 | ); 499 | $response = (object) $response; 500 | } 501 | 502 | return $response; 503 | } 504 | 505 | /** 506 | * Hide plugin card action links for plugins with no API data. 507 | * 508 | * @global $pagenow Current page. 509 | * @return void 510 | */ 511 | public function hide_action_links() { 512 | global $pagenow; 513 | 514 | if ( 'plugin-install.php' !== $pagenow ) { 515 | return; 516 | } 517 | 518 | $hide_selectors = array(); 519 | foreach ( $this->plugin_data as $plugin_data ) { 520 | if ( empty( $plugin_data['version'] ) ) { 521 | $hide_selectors[] = sprintf( '.plugin-card-%1$s .action-links, .plugin-card-%1$s .plugin-card-bottom', $plugin_data['slug'] ); 522 | } 523 | } 524 | if ( ! empty( $hide_selectors ) ) { 525 | $hide_selectors = implode( ', ', $hide_selectors ); 526 | printf( '', esc_attr( $hide_selectors ) ); 527 | } 528 | } 529 | } 530 | 531 | ( new WP_Plugin_Dependencies() )->init(); 532 | --------------------------------------------------------------------------------