Make WordPress Core


Ignore:
Timestamp:
02/06/2024 11:44:09 PM (9 months ago)
Author:
costdev
Message:

Upgrade/Install: Introduce Plugin Dependencies.

Introduces a new "Requires Plugins" plugin header so that plugin developers can list the slugs of the plugins theirs depends on.

This will inform users of the requirements, and provide links to the WordPress.org Plugins Repository that they can click to install and activate the dependencies first.

Plugins whose requirements are not met cannot be installed or activated, and they will be deactivated automatically if their requirements become unmet.
Plugins that others rely on cannot be deactivated or deleted until their dependent plugins are deactivated or deleted.

In memory of Alex Mills and Alex King.
WordPress Remembers.

Props ahoereth, afragen, alanfuller, alexkingorg, amykamala, anonymized_10690803, apeatling, ashfame, atimmer, audrasjb, aristath, azaozz, batmoo, beaulebens, blobaugh, bobbingwide, boonebgorges, brianhenryie, chanthaboune, chrisdavidmiles, coolmann, costdev, courane01, danielbachhuber, davidperez, dd32, Denis-de-Bernardy, dingo_d, DJPaul, dougal, DrewAPicture, ethitter, filosofo, georgestephanis, giuseppemazzapica-1, goldenapples, griffinjt, hellofromTonya, husobj, ideag, jarednova, jbobich, jbrinley, jltallon, joedolson, johnciacia, johnjamesjacoby, joppuyo, jsmoriss, karmatosed, kebbet, knutsp, kraftbj, kraftner, kurtpayne, lkraav, logikal16, luisherranz, man4toman, markjaquith, matt, mbijon, megphillips91, mikeschinkel, mordauk, morehawes, mrwweb, mte90, mukesh27, mzaweb, nacin, norcross, nvwd, nwjames, obliviousharmony, ocean90, oglekler, paaljoachim, pauldewouters, pbaylies, pbiron, peterwilsoncc, Philipp15b, poena, pogidude, retlehs, rmccue, ryan, sabreuse, sc0ttkclark, scribu, sereedmedia, SergeyBiryukov, ShaneF, shidouhikari, soean, spacedmonkey, stephenh1988, swissspidy, taylorde, tazotodua, threadi, TimothyBlynJacobs, TJNowell, tollmanz, toscho, tropicalista, Viper007Bond, westi, whiteshadow, williamsba1, wpsmith, ZaneMatthew.
Fixes #22316.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/plugin-install.php

    r56571 r57545  
    885885    echo "<div id='$tab-footer'>\n";
    886886    if ( ! empty( $api->download_link ) && ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) ) {
    887         $status = install_plugin_install_status( $api );
     887        $button = wp_get_plugin_action_button( $api->name, $api, $compatible_php, $compatible_wp );
     888        $button = str_replace( 'class="', 'class="right ', $button );
     889
     890        if ( ! str_contains( $button, __( 'Activate' ) ) ) {
     891            $button = str_replace( 'class="', 'id="plugin_install_from_iframe" class="', $button );
     892        }
     893
     894        echo wp_kses_post( $button );
     895    }
     896    echo "</div>\n";
     897
     898    wp_print_request_filesystem_credentials_modal();
     899    wp_print_admin_notice_templates();
     900
     901    iframe_footer();
     902    exit;
     903}
     904
     905/**
     906 * Gets the markup for the plugin install action button.
     907 *
     908 * @since 6.5.0
     909 *
     910 * @param string       $name           Plugin name.
     911 * @param array|object $data           {
     912 *     An array or object of plugin data. Can be retrieved from the API.
     913 *
     914 *     @type string   $slug             The plugin slug.
     915 *     @type string[] $requires_plugins An array of plugin dependency slugs.
     916 *     @type string   $version          The plugin's version string. Used when getting the install status.
     917 * }
     918 * @param bool         $compatible_php   The result of a PHP compatibility check.
     919 * @param bool         $compatible_wp    The result of a WP compatibility check.
     920 * @return string $button The markup for the dependency row button.
     921 */
     922function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible_wp ) {
     923    $button           = '';
     924    $data             = (object) $data;
     925    $status           = install_plugin_install_status( $data );
     926    $requires_plugins = $data->requires_plugins ?? array();
     927
     928    // Determine the status of plugin dependencies.
     929    $installed_plugins                   = get_plugins();
     930    $active_plugins                      = get_option( 'active_plugins' );
     931    $plugin_dependencies_count           = count( $requires_plugins );
     932    $installed_plugin_dependencies_count = 0;
     933    $active_plugin_dependencies_count    = 0;
     934    foreach ( $requires_plugins as $dependency ) {
     935        foreach ( array_keys( $installed_plugins ) as $installed_plugin_file ) {
     936            if ( str_contains( $installed_plugin_file, '/' ) && explode( '/', $installed_plugin_file )[0] === $dependency ) {
     937                ++$installed_plugin_dependencies_count;
     938            }
     939        }
     940
     941        foreach ( $active_plugins as $active_plugin_file ) {
     942            if ( str_contains( $active_plugin_file, '/' ) && explode( '/', $active_plugin_file )[0] === $dependency ) {
     943                ++$active_plugin_dependencies_count;
     944            }
     945        }
     946    }
     947    $all_plugin_dependencies_installed = $installed_plugin_dependencies_count === $plugin_dependencies_count;
     948    $all_plugin_dependencies_active    = $active_plugin_dependencies_count === $plugin_dependencies_count;
     949
     950    sprintf(
     951        '<a class="install-now button" data-slug="%s" href="%s" aria-label="%s" data-name="%s">%s</a>',
     952        esc_attr( $data->slug ),
     953        esc_url( $status['url'] ),
     954        /* translators: %s: Plugin name and version. */
     955        esc_attr( sprintf( _x( 'Install %s now', 'plugin' ), $name ) ),
     956        esc_attr( $name ),
     957        __( 'Install Now' )
     958    );
     959
     960    if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) {
    888961        switch ( $status['status'] ) {
    889962            case 'install':
    890963                if ( $status['url'] ) {
    891                     if ( $compatible_php && $compatible_wp ) {
    892                         echo '<a data-slug="' . esc_attr( $api->slug ) . '" id="plugin_install_from_iframe" class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . __( 'Install Now' ) . '</a>';
     964                    if ( $compatible_php && $compatible_wp && $all_plugin_dependencies_installed && ! empty( $data->download_link ) ) {
     965                        $button = sprintf(
     966                            '<a class="install-now button" data-slug="%s" href="%s" aria-label="%s" data-name="%s">%s</a>',
     967                            esc_attr( $data->slug ),
     968                            esc_url( $status['url'] ),
     969                            /* translators: %s: Plugin name and version. */
     970                            esc_attr( sprintf( _x( 'Install %s now', 'plugin' ), $name ) ),
     971                            esc_attr( $name ),
     972                            __( 'Install Now' )
     973                        );
    893974                    } else {
    894                         printf(
    895                             '<button type="button" class="button button-primary button-disabled right" disabled="disabled">%s</button>',
    896                             _x( 'Cannot Install', 'plugin' )
     975                        $button = sprintf(
     976                            '<button type="button" class="install-now button button-disabled" disabled="disabled">%s</button>',
     977                            _x( 'Install Now', 'plugin' )
    897978                        );
    898979                    }
    899980                }
    900981                break;
     982
    901983            case 'update_available':
    902984                if ( $status['url'] ) {
    903                     if ( $compatible_php ) {
    904                         echo '<a data-slug="' . esc_attr( $api->slug ) . '" data-plugin="' . esc_attr( $status['file'] ) . '" id="plugin_update_from_iframe" class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . __( 'Install Update Now' ) . '</a>';
     985                    if ( $compatible_php && $compatible_wp ) {
     986                        $button = sprintf(
     987                            '<a class="update-now button aria-button-if-js" data-plugin="%s" data-slug="%s" href="%s" aria-label="%s" data-name="%s">%s</a>',
     988                            esc_attr( $status['file'] ),
     989                            esc_attr( $data->slug ),
     990                            esc_url( $status['url'] ),
     991                            /* translators: %s: Plugin name and version. */
     992                            esc_attr( sprintf( _x( 'Update %s now', 'plugin' ), $name ) ),
     993                            esc_attr( $name ),
     994                            __( 'Update Now' )
     995                        );
    905996                    } else {
    906                         printf(
    907                             '<button type="button" class="button button-primary button-disabled right" disabled="disabled">%s</button>',
    908                             _x( 'Cannot Update', 'plugin' )
     997                        $button = sprintf(
     998                            '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
     999                            _x( 'Update Now', 'plugin' )
    9091000                        );
    9101001                    }
    9111002                }
    9121003                break;
     1004
     1005            case 'latest_installed':
    9131006            case 'newer_installed':
    914                 /* translators: %s: Plugin version. */
    915                 echo '<a class="button button-primary right disabled">' . sprintf( __( 'Newer Version (%s) Installed' ), esc_html( $status['version'] ) ) . '</a>';
     1007                if ( is_plugin_active( $status['file'] ) ) {
     1008                    $button = sprintf(
     1009                        '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
     1010                        _x( 'Active', 'plugin' )
     1011                    );
     1012                } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) {
     1013                    if ( $compatible_php && $compatible_wp && $all_plugin_dependencies_active ) {
     1014                        $button_text = __( 'Activate' );
     1015                        /* translators: %s: Plugin name. */
     1016                        $button_label = _x( 'Activate %s', 'plugin' );
     1017                        $activate_url = add_query_arg(
     1018                            array(
     1019                                '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
     1020                                'action'   => 'activate',
     1021                                'plugin'   => $status['file'],
     1022                            ),
     1023                            network_admin_url( 'plugins.php' )
     1024                        );
     1025
     1026                        if ( is_network_admin() ) {
     1027                            $button_text = __( 'Network Activate' );
     1028                            /* translators: %s: Plugin name. */
     1029                            $button_label = _x( 'Network Activate %s', 'plugin' );
     1030                            $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
     1031                        }
     1032
     1033                        $button = sprintf(
     1034                            '<a href="%1$s" data-name="%2$s" data-slug="%3$s" data-plugin="%4$s" class="button button-primary activate-now" aria-label="%5$s">%6$s</a>',
     1035                            esc_url( $activate_url ),
     1036                            esc_attr( $name ),
     1037                            esc_attr( $data->slug ),
     1038                            esc_attr( $status['file'] ),
     1039                            esc_attr( sprintf( $button_label, $name ) ),
     1040                            $button_text
     1041                        );
     1042                    } else {
     1043                        $button = sprintf(
     1044                            '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
     1045                            is_network_admin() ? _x( 'Network Activate %s', 'plugin' ) : _x( 'Activate', 'plugin' )
     1046                        );
     1047                    }
     1048                } else {
     1049                    $button = sprintf(
     1050                        '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
     1051                        _x( 'Installed', 'plugin' )
     1052                    );
     1053                }
    9161054                break;
    917             case 'latest_installed':
    918                 echo '<a class="button button-primary right disabled">' . __( 'Latest Version Installed' ) . '</a>';
    919                 break;
    920         }
    921     }
    922     echo "</div>\n";
    923 
    924     iframe_footer();
    925     exit;
     1055        }
     1056
     1057        return $button;
     1058    }
    9261059}
Note: See TracChangeset for help on using the changeset viewer.