Changeset 57545
- Timestamp:
- 02/06/2024 11:44:09 PM (10 months ago)
- Location:
- trunk
- Files:
-
- 16 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/js/_enqueues/wp/updates.js
r56011 r57545 415 415 416 416 /** 417 * Sends a message from a modal to the main screen to update buttons in plugin cards. 418 * 419 * @since 6.5.0 420 * 421 * @param {Object} data An object of data to use for the button. 422 * @param {string} data.slug The plugin's slug. 423 * @param {string} data.text The text to use for the button. 424 * @param {string} data.ariaLabel The value for the button's aria-label attribute. An empty string removes the attribute. 425 * @param {string=} data.status Optional. An identifier for the status. 426 * @param {string=} data.removeClasses Optional. A space-separated list of classes to remove from the button. 427 * @param {string=} data.addClasses Optional. A space-separated list of classes to add to the button. 428 * @param {string=} data.href Optional. The button's URL. 429 * @param {string=} data.pluginName Optional. The plugin's name. 430 * @param {string=} data.plugin Optional. The plugin file, relative to the plugins directory. 431 */ 432 wp.updates.setCardButtonStatus = function( data ) { 433 var target = window.parent === window ? null : window.parent; 434 435 $.support.postMessage = !! window.postMessage; 436 if ( false !== $.support.postMessage && null !== target && -1 === window.parent.location.pathname.indexOf( 'index.php' ) ) { 437 target.postMessage( JSON.stringify( data ), window.location.origin ); 438 } 439 }; 440 441 /** 417 442 * Decrements the update counts throughout the various menus. 418 443 * … … 453 478 wp.updates.updatePlugin = function( args ) { 454 479 var $updateRow, $card, $message, message, 455 $adminBarUpdates = $( '#wp-admin-bar-updates' ); 480 $adminBarUpdates = $( '#wp-admin-bar-updates' ), 481 buttonText = __( 'Updating...' ); 456 482 457 483 args = _.extend( { … … 469 495 ); 470 496 } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { 471 $card = $( '.plugin-card-' + args.slug );497 $card = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' ); 472 498 $message = $card.find( '.update-now' ).addClass( 'updating-message' ); 473 499 message = sprintf( … … 489 515 $message 490 516 .attr( 'aria-label', message ) 491 .text( __( 'Updating...' ));517 .text( buttonText ); 492 518 493 519 $document.trigger( 'wp-plugin-updating', args ); 520 521 if ( 'plugin-information-footer' === $card.attr('id' ) ) { 522 wp.updates.setCardButtonStatus( 523 { 524 status: 'updating-plugin', 525 slug: args.slug, 526 addClasses: 'updating-message', 527 text: buttonText, 528 ariaLabel: message 529 } 530 ); 531 } 494 532 495 533 return wp.updates.ajax( 'update-plugin', args ); … … 512 550 wp.updates.updatePluginSuccess = function( response ) { 513 551 var $pluginRow, $updateMessage, newText, 514 $adminBarUpdates = $( '#wp-admin-bar-updates' ); 552 $adminBarUpdates = $( '#wp-admin-bar-updates' ), 553 buttonText = _x( 'Updated!', 'plugin' ), 554 ariaLabel = sprintf( 555 /* translators: %s: Plugin name and version. */ 556 _x( '%s updated!', 'plugin' ), 557 response.pluginName 558 ); 515 559 516 560 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) { … … 529 573 $pluginRow.find( '.auto-update-time' ).empty(); 530 574 } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { 531 $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' )575 $updateMessage = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.update-now' ) 532 576 .removeClass( 'updating-message' ) 533 577 .addClass( 'button-disabled updated-message' ); … … 537 581 538 582 $updateMessage 539 .attr( 540 'aria-label', 541 sprintf( 542 /* translators: %s: Plugin name and version. */ 543 _x( '%s updated!', 'plugin' ), 544 response.pluginName 545 ) 546 ) 547 .text( _x( 'Updated!', 'plugin' ) ); 583 .attr( 'aria-label', ariaLabel ) 584 .text( buttonText ); 548 585 549 586 wp.a11y.speak( __( 'Update completed successfully.' ) ); 550 587 551 wp.updates.decrementCount( 'plugin' ); 588 if ( 'plugin_install_from_iframe' !== $updateMessage.attr( 'id' ) ) { 589 wp.updates.decrementCount( 'plugin' ); 590 } else { 591 wp.updates.setCardButtonStatus( 592 { 593 status: 'updated-plugin', 594 slug: response.slug, 595 removeClasses: 'updating-message', 596 addClasses: 'button-disabled updated-message', 597 text: buttonText, 598 ariaLabel: ariaLabel 599 } 600 ); 601 } 552 602 553 603 $document.trigger( 'wp-plugin-update-success', response ); … … 568 618 */ 569 619 wp.updates.updatePluginError = function( response ) { 570 var $pluginRow, $card, $message, errorMessage, 620 var $pluginRow, $card, $message, errorMessage, buttonText, ariaLabel, 571 621 $adminBarUpdates = $( '#wp-admin-bar-updates' ); 572 622 … … 609 659 } 610 660 } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { 611 $card = $( '.plugin-card-' + response.slug ) 612 .addClass( 'plugin-card-update-failed' ) 661 buttonText = __( 'Update failed.' ); 662 663 $card = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ) 613 664 .append( wp.updates.adminNotice( { 614 665 className: 'update-message notice-error notice-alt is-dismissible', … … 616 667 } ) ); 617 668 669 if ( $card.hasClass( 'plugin-card-' + response.slug ) ) { 670 $card.addClass( 'plugin-card-update-failed' ); 671 } 672 618 673 $card.find( '.update-now' ) 619 .text( __( 'Update failed.' ))674 .text( buttonText ) 620 675 .removeClass( 'updating-message' ); 621 676 622 677 if ( response.pluginName ) { 623 $card.find( '.update-now' ) 624 .attr( 625 'aria-label', 626 sprintf( 627 /* translators: %s: Plugin name and version. */ 628 _x( '%s update failed.', 'plugin' ), 629 response.pluginName 630 ) 631 ); 678 ariaLabel = sprintf( 679 /* translators: %s: Plugin name and version. */ 680 _x( '%s update failed.', 'plugin' ), 681 response.pluginName 682 ); 683 684 $card.find( '.update-now' ).attr( 'aria-label', ariaLabel ); 632 685 } else { 686 ariaLabel = ''; 633 687 $card.find( '.update-now' ).removeAttr( 'aria-label' ); 634 688 } … … 652 706 653 707 wp.a11y.speak( errorMessage, 'assertive' ); 708 709 if ( 'plugin-information-footer' === $card.attr('id' ) ) { 710 wp.updates.setCardButtonStatus( 711 { 712 status: 'plugin-update-failed', 713 slug: response.slug, 714 removeClasses: 'updating-message', 715 text: buttonText, 716 ariaLabel: ariaLabel 717 } 718 ); 719 } 654 720 655 721 $document.trigger( 'wp-plugin-update-error', response ); … … 669 735 */ 670 736 wp.updates.installPlugin = function( args ) { 671 var $card = $( '.plugin-card-' + args.slug ), 672 $message = $card.find( '.install-now' ); 737 var $card = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' ), 738 $message = $card.find( '.install-now' ), 739 buttonText = __( 'Installing...' ), 740 ariaLabel; 673 741 674 742 args = _.extend( { … … 685 753 } 686 754 755 ariaLabel = sprintf( 756 /* translators: %s: Plugin name and version. */ 757 _x( 'Installing %s...', 'plugin' ), 758 $message.data( 'name' ) 759 ); 760 687 761 $message 688 762 .addClass( 'updating-message' ) 689 .attr( 690 'aria-label', 691 sprintf( 692 /* translators: %s: Plugin name and version. */ 693 _x( 'Installing %s...', 'plugin' ), 694 $message.data( 'name' ) 695 ) 696 ) 697 .text( __( 'Installing...' ) ); 763 .attr( 'aria-label', ariaLabel ) 764 .text( buttonText ); 698 765 699 766 wp.a11y.speak( __( 'Installing... please wait.' ) ); … … 703 770 704 771 $document.trigger( 'wp-plugin-installing', args ); 772 773 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 774 wp.updates.setCardButtonStatus( 775 { 776 status: 'installing-plugin', 777 slug: args.slug, 778 addClasses: 'updating-message', 779 text: buttonText, 780 ariaLabel: ariaLabel 781 } 782 ); 783 } 705 784 706 785 return wp.updates.ajax( 'install-plugin', args ); … … 718 797 */ 719 798 wp.updates.installPluginSuccess = function( response ) { 720 var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' ); 799 var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ), 800 buttonText = _x( 'Installed!', 'plugin' ), 801 ariaLabel = sprintf( 802 /* translators: %s: Plugin name and version. */ 803 _x( '%s installed!', 'plugin' ), 804 response.pluginName 805 ); 721 806 722 807 $message 723 808 .removeClass( 'updating-message' ) 724 809 .addClass( 'updated-message installed button-disabled' ) 725 .attr( 726 'aria-label', 727 sprintf( 728 /* translators: %s: Plugin name and version. */ 729 _x( '%s installed!', 'plugin' ), 730 response.pluginName 731 ) 732 ) 733 .text( _x( 'Installed!', 'plugin' ) ); 810 .attr( 'aria-label', ariaLabel ) 811 .text( buttonText ); 734 812 735 813 wp.a11y.speak( __( 'Installation completed successfully.' ) ); … … 739 817 if ( response.activateUrl ) { 740 818 setTimeout( function() { 741 742 // Transform the 'Install' button into an 'Activate' button. 743 $message.removeClass( 'install-now installed button-disabled updated-message' ) 744 .addClass( 'activate-now button-primary' ) 745 .attr( 'href', response.activateUrl ); 746 747 if ( 'plugins-network' === pagenow ) { 748 $message 749 .attr( 750 'aria-label', 751 sprintf( 752 /* translators: %s: Plugin name. */ 753 _x( 'Network Activate %s', 'plugin' ), 754 response.pluginName 755 ) 756 ) 757 .text( __( 'Network Activate' ) ); 758 } else { 759 $message 760 .attr( 761 'aria-label', 762 sprintf( 763 /* translators: %s: Plugin name. */ 764 _x( 'Activate %s', 'plugin' ), 765 response.pluginName 766 ) 767 ) 768 .text( __( 'Activate' ) ); 769 } 819 wp.updates.checkPluginDependencies( { 820 slug: response.slug 821 } ); 770 822 }, 1000 ); 823 } 824 825 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 826 wp.updates.setCardButtonStatus( 827 { 828 status: 'installed-plugin', 829 slug: response.slug, 830 removeClasses: 'updating-message', 831 addClasses: 'updated-message installed button-disabled', 832 text: buttonText, 833 ariaLabel: ariaLabel 834 } 835 ); 771 836 } 772 837 }; … … 784 849 */ 785 850 wp.updates.installPluginError = function( response ) { 786 var $card = $( '.plugin-card-' + response.slug ),851 var $card = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ), 787 852 $button = $card.find( '.install-now' ), 853 buttonText = __( 'Installation failed.' ), 854 ariaLabel = sprintf( 855 /* translators: %s: Plugin name and version. */ 856 _x( '%s installation failed', 'plugin' ), 857 $button.data( 'name' ) 858 ), 788 859 errorMessage; 789 860 … … 818 889 $button 819 890 .removeClass( 'updating-message' ).addClass( 'button-disabled' ) 820 .attr( 821 'aria-label', 822 sprintf( 823 /* translators: %s: Plugin name and version. */ 824 _x( '%s installation failed', 'plugin' ), 825 $button.data( 'name' ) 826 ) 827 ) 828 .text( __( 'Installation failed.' ) ); 891 .attr( 'aria-label', ariaLabel ) 892 .text( buttonText ); 829 893 830 894 wp.a11y.speak( errorMessage, 'assertive' ); 831 895 896 wp.updates.setCardButtonStatus( 897 { 898 status: 'plugin-install-failed', 899 slug: response.slug, 900 removeClasses: 'updating-message', 901 addClasses: 'button-disabled', 902 text: buttonText, 903 ariaLabel: ariaLabel 904 } 905 ); 906 832 907 $document.trigger( 'wp-plugin-install-error', response ); 908 }; 909 910 /** 911 * Sends an Ajax request to the server to check a plugin's dependencies. 912 * 913 * @since 6.5.0 914 * 915 * @param {Object} args Arguments. 916 * @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository. 917 * @param {checkPluginDependenciesSuccess=} args.success Optional. Success callback. Default: wp.updates.checkPluginDependenciesSuccess 918 * @param {checkPluginDependenciesError=} args.error Optional. Error callback. Default: wp.updates.checkPluginDependenciesError 919 * @return {$.promise} A jQuery promise that represents the request, 920 * decorated with an abort() method. 921 */ 922 wp.updates.checkPluginDependencies = function( args ) { 923 args = _.extend( { 924 success: wp.updates.checkPluginDependenciesSuccess, 925 error: wp.updates.checkPluginDependenciesError 926 }, args ); 927 928 wp.a11y.speak( __( 'Checking plugin dependencies... please wait.' ) ); 929 $document.trigger( 'wp-checking-plugin-dependencies', args ); 930 931 return wp.updates.ajax( 'check_plugin_dependencies', args ); 932 }; 933 934 /** 935 * Updates the UI appropriately after a successful plugin dependencies check. 936 * 937 * @since 6.5.0 938 * 939 * @param {Object} response Response from the server. 940 * @param {string} response.slug Slug of the checked plugin. 941 * @param {string} response.pluginName Name of the checked plugin. 942 * @param {string} response.plugin The plugin file, relative to the plugins directory. 943 * @param {string} response.activateUrl URL to activate the just checked plugin. 944 */ 945 wp.updates.checkPluginDependenciesSuccess = function( response ) { 946 var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ), 947 buttonText, ariaLabel; 948 949 // Transform the 'Install' button into an 'Activate' button. 950 $message 951 .removeClass( 'install-now installed button-disabled updated-message' ) 952 .addClass( 'activate-now button-primary' ) 953 .attr( 'href', response.activateUrl ); 954 955 wp.a11y.speak( __( 'Plugin dependencies check completed successfully.' ) ); 956 $document.trigger( 'wp-check-plugin-dependencies-success', response ); 957 958 if ( 'plugins-network' === pagenow ) { 959 buttonText = _x( 'Network Activate' ); 960 ariaLabel = sprintf( 961 /* translators: %s: Plugin name. */ 962 _x( 'Network Activate %s', 'plugin' ), 963 response.pluginName 964 ); 965 966 $message 967 .attr( 'aria-label', ariaLabel ) 968 .text( buttonText ); 969 } else { 970 buttonText = _x( 'Activate', 'plugin' ); 971 ariaLabel = sprintf( 972 /* translators: %s: Plugin name. */ 973 _x( 'Activate %s', 'plugin' ), 974 response.pluginName 975 ); 976 977 $message 978 .attr( 'aria-label', ariaLabel ) 979 .attr( 'data-name', response.pluginName ) 980 .attr( 'data-slug', response.slug ) 981 .attr( 'data-plugin', response.plugin ) 982 .text( buttonText ); 983 } 984 985 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 986 wp.updates.setCardButtonStatus( 987 { 988 status: 'dependencies-check-success', 989 slug: response.slug, 990 removeClasses: 'install-now installed button-disabled updated-message', 991 addClasses: 'activate-now button-primary', 992 text: buttonText, 993 ariaLabel: ariaLabel, 994 pluginName: response.pluginName, 995 plugin: response.plugin, 996 href: response.activateUrl 997 } 998 ); 999 } 1000 }; 1001 1002 /** 1003 * Updates the UI appropriately after a failed plugin dependencies check. 1004 * 1005 * @since 6.5.0 1006 * 1007 * @param {Object} response Response from the server. 1008 * @param {string} response.slug Slug of the plugin to be checked. 1009 * @param {string=} response.pluginName Optional. Name of the plugin to be checked. 1010 * @param {string} response.errorCode Error code for the error that occurred. 1011 * @param {string} response.errorMessage The error that occurred. 1012 */ 1013 wp.updates.checkPluginDependenciesError = function( response ) { 1014 var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ), 1015 buttonText = __( 'Activate' ), 1016 ariaLabel = sprintf( 1017 /* translators: 1: Plugin name, 2. The reason the plugin cannot be activated. */ 1018 _x( 'Cannot activate %1$s. %2$s', 'plugin' ), 1019 response.pluginName, 1020 response.errorMessage 1021 ), 1022 errorMessage; 1023 1024 if ( ! wp.updates.isValidResponse( response, 'check-dependencies' ) ) { 1025 return; 1026 } 1027 1028 errorMessage = sprintf( 1029 /* translators: %s: Error string for a failed activation. */ 1030 __( 'Activation failed: %s' ), 1031 response.errorMessage 1032 ); 1033 1034 wp.a11y.speak( errorMessage, 'assertive' ); 1035 $document.trigger( 'wp-check-plugin-dependencies-error', response ); 1036 1037 $message 1038 .removeClass( 'install-now installed updated-message' ) 1039 .addClass( 'activate-now button-primary' ) 1040 .attr( 'aria-label', ariaLabel ) 1041 .text( buttonText ); 1042 1043 if ( 'plugin-information-footer' === $message.parent().attr('id' ) ) { 1044 wp.updates.setCardButtonStatus( 1045 { 1046 status: 'dependencies-check-failed', 1047 slug: response.slug, 1048 removeClasses: 'install-now installed updated-message', 1049 addClasses: 'activate-now button-primary', 1050 text: buttonText, 1051 ariaLabel: ariaLabel 1052 } 1053 ); 1054 } 1055 }; 1056 1057 /** 1058 * Sends an Ajax request to the server to activate a plugin. 1059 * 1060 * @since 6.5.0 1061 * 1062 * @param {Object} args Arguments. 1063 * @param {string} args.name The name of the plugin. 1064 * @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository. 1065 * @param {string} args.plugin The plugin file, relative to the plugins directory. 1066 * @param {activatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.activatePluginSuccess 1067 * @param {activatePluginError=} args.error Optional. Error callback. Default: wp.updates.activatePluginError 1068 * @return {$.promise} A jQuery promise that represents the request, 1069 * decorated with an abort() method. 1070 */ 1071 wp.updates.activatePlugin = function( args ) { 1072 var $message = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' ).find( '.activate-now, .activating-message' ); 1073 1074 args = _.extend( { 1075 success: wp.updates.activatePluginSuccess, 1076 error: wp.updates.activatePluginError 1077 }, args ); 1078 1079 wp.a11y.speak( __( 'Activating... please wait.' ) ); 1080 $document.trigger( 'wp-activating-plugin', args ); 1081 1082 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 1083 wp.updates.setCardButtonStatus( 1084 { 1085 status: 'activating-plugin', 1086 slug: args.slug, 1087 removeClasses: 'installed updated-message button-primary', 1088 addClasses: 'activating-message', 1089 text: _x( 'Activating...', 'plugin' ), 1090 ariaLabel: sprintf( 1091 /* translators: %s: Plugin name. */ 1092 _x( 'Activating %s', 'plugin' ), 1093 args.name 1094 ) 1095 } 1096 ); 1097 } 1098 1099 return wp.updates.ajax( 'activate-plugin', args ); 1100 }; 1101 1102 /** 1103 * Updates the UI appropriately after a successful plugin activation. 1104 * 1105 * @since 6.5.0 1106 * 1107 * @param {Object} response Response from the server. 1108 * @param {string} response.slug Slug of the activated plugin. 1109 * @param {string} response.pluginName Name of the activated plugin. 1110 * @param {string} response.plugin The plugin file, relative to the plugins directory. 1111 */ 1112 wp.updates.activatePluginSuccess = function( response ) { 1113 var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.activating-message' ), 1114 buttonText = _x( 'Activated!', 'plugin' ), 1115 ariaLabel = sprintf( 1116 /* translators: %s: The plugin name. */ 1117 '%s activated successfully.', 1118 response.pluginName 1119 ); 1120 1121 wp.a11y.speak( __( 'Activation completed successfully.' ) ); 1122 $document.trigger( 'wp-plugin-activate-success', response ); 1123 1124 $message 1125 .removeClass( 'activating-message' ) 1126 .addClass( 'activated-message button-disabled' ) 1127 .attr( 'aria-label', ariaLabel ) 1128 .text( buttonText ); 1129 1130 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 1131 wp.updates.setCardButtonStatus( 1132 { 1133 status: 'activated-plugin', 1134 slug: response.slug, 1135 removeClasses: 'activating-message', 1136 addClasses: 'activated-message button-disabled', 1137 text: buttonText, 1138 ariaLabel: ariaLabel 1139 } 1140 ); 1141 } 1142 1143 setTimeout( function() { 1144 $message.removeClass( 'activated-message' ) 1145 .text( _x( 'Active', 'plugin' ) ); 1146 1147 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 1148 wp.updates.setCardButtonStatus( 1149 { 1150 status: 'plugin-active', 1151 slug: response.slug, 1152 removeClasses: 'activated-message', 1153 text: _x( 'Active', 'plugin' ), 1154 ariaLabel: sprintf( 1155 /* translators: %s: The plugin name. */ 1156 '%s is active.', 1157 response.pluginName 1158 ) 1159 } 1160 ); 1161 } 1162 }, 1000 ); 1163 }; 1164 1165 /** 1166 * Updates the UI appropriately after a failed plugin activation. 1167 * 1168 * @since 6.5.0 1169 * 1170 * @param {Object} response Response from the server. 1171 * @param {string} response.slug Slug of the plugin to be activated. 1172 * @param {string=} response.pluginName Optional. Name of the plugin to be activated. 1173 * @param {string} response.errorCode Error code for the error that occurred. 1174 * @param {string} response.errorMessage The error that occurred. 1175 */ 1176 wp.updates.activatePluginError = function( response ) { 1177 var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.activating-message' ), 1178 buttonText = __( 'Activation failed.' ), 1179 ariaLabel = sprintf( 1180 /* translators: %s: Plugin name. */ 1181 _x( '%s activation failed', 'plugin' ), 1182 response.pluginName 1183 ), 1184 errorMessage; 1185 1186 if ( ! wp.updates.isValidResponse( response, 'activate' ) ) { 1187 return; 1188 } 1189 1190 errorMessage = sprintf( 1191 /* translators: %s: Error string for a failed activation. */ 1192 __( 'Activation failed: %s' ), 1193 response.errorMessage 1194 ); 1195 1196 wp.a11y.speak( errorMessage, 'assertive' ); 1197 $document.trigger( 'wp-plugin-activate-error', response ); 1198 1199 $message 1200 .removeClass( 'install-now installed activating-message' ) 1201 .addClass( 'button-disabled' ) 1202 .attr( 'aria-label', ariaLabel ) 1203 .text( buttonText ); 1204 1205 if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) { 1206 wp.updates.setCardButtonStatus( 1207 { 1208 status: 'plugin-activation-failed', 1209 slug: response.slug, 1210 removeClasses: 'install-now installed activating-message', 1211 addClasses: 'button-disabled', 1212 text: buttonText, 1213 ariaLabel: ariaLabel 1214 } 1215 ); 1216 } 833 1217 }; 834 1218 … … 1971 2355 break; 1972 2356 2357 case 'check-dependencies': 2358 /* translators: %s: Error string for a failed dependencies check. */ 2359 errorMessage = __( 'Dependencies check failed: %s' ); 2360 break; 2361 2362 case 'activate': 2363 /* translators: %s: Error string for a failed activation. */ 2364 errorMessage = __( 'Activation failed: %s' ); 2365 break; 2366 1973 2367 case 'delete': 1974 2368 /* translators: %s: Error string for a failed deletion. */ … … 2026 2420 2027 2421 $( function() { 2028 var $pluginFilter = $( '#plugin-filter ' ),2422 var $pluginFilter = $( '#plugin-filter, #plugin-information-footer' ), 2029 2423 $bulkActionForm = $( '#bulk-action-form' ), 2030 2424 $filesystemForm = $( '#request-filesystem-credentials-form' ), … … 2243 2637 2244 2638 /** 2639 * Click handler for plugin activations in plugin activation view. 2640 * 2641 * @since 6.5.0 2642 * 2643 * @param {Event} event Event interface. 2644 */ 2645 $pluginFilter.on( 'click', '.activate-now', function( event ) { 2646 var $activateButton = $( event.target ); 2647 2648 event.preventDefault(); 2649 2650 if ( $activateButton.hasClass( 'activating-message' ) || $activateButton.hasClass( 'button-disabled' ) ) { 2651 return; 2652 } 2653 2654 $activateButton 2655 .removeClass( 'activate-now button-primary' ) 2656 .addClass( 'activating-message' ) 2657 .attr( 2658 'aria-label', 2659 sprintf( 2660 /* translators: %s: Plugin name. */ 2661 _x( 'Activating %s', 'plugin' ), 2662 $activateButton.data( 'name' ) 2663 ) 2664 ) 2665 .text( _x( 'Activating...', 'plugin' ) ); 2666 2667 wp.updates.activatePlugin( 2668 { 2669 name: $activateButton.data( 'name' ), 2670 slug: $activateButton.data( 'slug' ), 2671 plugin: $activateButton.data( 'plugin' ) 2672 } 2673 ); 2674 }); 2675 2676 /** 2245 2677 * Click handler for importer plugins installs in the Import screen. 2246 2678 * … … 2768 3200 2769 3201 /** 2770 * Click handler for installing a plugin from the details modal on `plugin-install.php`.2771 *2772 * @since 4.6.02773 *2774 * @param {Event} event Event interface.2775 */2776 $( '#plugin_install_from_iframe' ).on( 'click', function( event ) {2777 var target = window.parent === window ? null : window.parent,2778 install;2779 2780 $.support.postMessage = !! window.postMessage;2781 2782 if ( false === $.support.postMessage || null === target || -1 !== window.parent.location.pathname.indexOf( 'index.php' ) ) {2783 return;2784 }2785 2786 event.preventDefault();2787 2788 install = {2789 action: 'install-plugin',2790 data: {2791 slug: $( this ).data( 'slug' )2792 }2793 };2794 2795 target.postMessage( JSON.stringify( install ), window.location.origin );2796 } );2797 2798 /**2799 3202 * Handles postMessage events. 2800 3203 * … … 2819 3222 } 2820 3223 2821 if ( ! message || 'undefined' === typeof message.action ) { 3224 if ( ! message ) { 3225 return; 3226 } 3227 3228 if ( 3229 'undefined' !== typeof message.status && 3230 'undefined' !== typeof message.slug && 3231 'undefined' !== typeof message.text && 3232 'undefined' !== typeof message.ariaLabel 3233 ) { 3234 var $card = $( '.plugin-card-' + message.slug ), 3235 $message = $card.find( '[data-slug="' + message.slug + '"]' ); 3236 3237 if ( 'undefined' !== typeof message.removeClasses ) { 3238 $message.removeClass( message.removeClasses ); 3239 } 3240 3241 if ( 'undefined' !== typeof message.addClasses ) { 3242 $message.addClass( message.addClasses ); 3243 } 3244 3245 if ( '' === message.ariaLabel ) { 3246 $message.removeAttr( 'aria-label' ); 3247 } else { 3248 $message.attr( 'aria-label', message.ariaLabel ); 3249 } 3250 3251 if ( 'dependencies-check-success' === message.status ) { 3252 $message 3253 .attr( 'data-name', message.pluginName ) 3254 .attr( 'data-slug', message.slug ) 3255 .attr( 'data-plugin', message.plugin ) 3256 .attr( 'href', message.href ); 3257 } 3258 3259 $message.text( message.text ); 3260 } 3261 3262 if ( 'undefined' === typeof message.action || 'undefined' === typeof message.data.slug ) { 2822 3263 return; 2823 3264 } … … 2833 3274 case 'install-plugin': 2834 3275 case 'update-plugin': 2835 /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */2836 window.tb_remove();2837 /* jscs:enable */2838 2839 3276 message.data = wp.updates._addCallbacks( message.data, message.action ); 2840 3277 -
trunk/src/wp-admin/admin-ajax.php
r53363 r57545 118 118 'destroy-sessions', 119 119 'install-plugin', 120 'activate-plugin', 120 121 'update-plugin', 121 122 'crop-image', … … 170 171 add_action( 'wp_ajax_nopriv_heartbeat', 'wp_ajax_nopriv_heartbeat', 1 ); 171 172 173 // Register Plugin Dependencies Ajax calls. 174 add_action( 'wp_ajax_check_plugin_dependencies', array( 'WP_Plugin_Dependencies', 'check_plugin_dependencies_during_ajax' ) ); 175 172 176 $action = $_REQUEST['action']; 173 177 -
trunk/src/wp-admin/css/common.css
r57363 r57545 1501 1501 } 1502 1502 1503 #plugin-information-footer .update-now:not(.button-disabled):before { 1504 color: #d63638; 1505 content: "\f463"; 1506 display: inline-block; 1507 font: normal 20px/1 dashicons; 1508 margin: -3px 5px 0 -2px; 1509 speak: never; 1510 -webkit-font-smoothing: antialiased; 1511 -moz-osx-font-smoothing: grayscale; 1512 vertical-align: middle; 1513 } 1514 1515 #plugin-information-footer .notice { 1516 margin-top: -5px; 1517 } 1518 1503 1519 .update-message p:before, 1504 1520 .updating-message p:before, … … 1508 1524 .button.updated-message:before, 1509 1525 .button.installed:before, 1510 .button.installing:before { 1526 .button.installing:before, 1527 .button.activating-message:before, 1528 .button.activated-message:before { 1511 1529 display: inline-block; 1512 1530 font: normal 20px/1 'dashicons'; … … 1545 1563 .import-php .updating-message:before, 1546 1564 .button.updating-message:before, 1547 .button.installing:before { 1565 .button.installing:before, 1566 .button.activating-message:before { 1548 1567 color: #d63638; 1549 1568 content: "\f463"; … … 1555 1574 .button.updating-message:before, 1556 1575 .button.installing:before, 1576 .button.activating-message:before, 1557 1577 .plugins .column-auto-updates .dashicons-update.spin, 1558 1578 .theme-overlay .theme-autoupdate .dashicons-update.spin { … … 1565 1585 .button.updating-message:before, 1566 1586 .button.installing:before, 1587 .button.activating-message:before, 1567 1588 .plugins .column-auto-updates .dashicons-update.spin, 1568 1589 .theme-overlay .theme-autoupdate .dashicons-update.spin { … … 1578 1599 .updated-message p:before, 1579 1600 .installed p:before, 1580 .button.updated-message:before { 1601 .button.updated-message:before, 1602 .button.activated-message:before { 1581 1603 color: #68de7c; 1582 1604 content: "\f147"; … … 1663 1685 .button.updated-message:before, 1664 1686 .button.installed:before, 1665 .button.installing:before { 1687 .button.installing:before, 1688 .button.activated-message:before, 1689 .button.activating-message:before { 1666 1690 margin: 3px 5px 0 -2px; 1667 1691 } 1668 1692 1669 .button-primary.updating-message:before { 1693 #plugin-information-footer .button.installed:before, 1694 #plugin-information-footer .button.installing:before, 1695 #plugin-information-footer .button.updating-message:before, 1696 #plugin-information-footer .button.updated-message:before, 1697 #plugin-information-footer .button.activated-message:before, 1698 #plugin-information-footer .button.activating-message:before { 1699 margin: 9px 5px 0 -2px; 1700 } 1701 1702 #plugin-information-footer .button.update-now.updating-message:before { 1703 margin: -3px 5px 0 -2px; 1704 } 1705 1706 .button-primary.updating-message:before, 1707 .button-primary.activating-message:before { 1670 1708 color: #fff; 1671 1709 } 1672 1710 1673 .button-primary.updated-message:before { 1711 .button-primary.updated-message:before, 1712 .button-primary.activated-message:before { 1674 1713 color: #9ec2e6; 1675 1714 } 1676 1715 1677 .button.updated-message { 1716 .button.updated-message, 1717 .button.activated-message { 1678 1718 transition-property: border, background, color; 1679 1719 transition-duration: .05s; -
trunk/src/wp-admin/css/list-tables.css
r57321 r57545 586 586 } 587 587 588 .check-column input:where(:not(:disabled)):hover, 589 .check-column:hover input:where(:not(:disabled)) { 588 .check-column .label-covers-full-cell:hover + input:not(:disabled) { 590 589 box-shadow: 0 0 0 1px #2271b1; 591 590 } … … 1549 1548 } 1550 1549 1551 .plugin-card .name,1552 1550 .plugin-card .desc { 1553 margin-left: 148px; /* icon + margin */ 1554 margin-right: 128px; /* action links + margin */ 1551 margin-inline: 0; 1552 } 1553 1554 .plugin-card .name, .plugin-card .desc > p { 1555 margin-left: 148px; 1556 } 1557 1558 @media (min-width: 1101px) { 1559 .plugin-card .name, .plugin-card .desc > p { 1560 margin-right: 128px; 1561 } 1562 } 1563 1564 @media (min-width: 481px) and (max-width: 781px) { 1565 .plugin-card .name, .plugin-card .desc > p { 1566 margin-right: 128px; 1567 } 1568 } 1569 1570 .plugin-card .column-description { 1571 display: flex; 1572 flex-direction: column; 1573 justify-content: flex-start; 1574 } 1575 1576 .plugin-card .column-description > p { 1577 margin-top: 0; 1578 } 1579 1580 .plugin-card .column-description .authors { 1581 order: 1; 1582 } 1583 1584 .plugin-card .column-description .plugin-dependencies { 1585 order: 2; 1586 } 1587 1588 .plugin-card .column-description p:empty { 1589 display: none; 1590 } 1591 1592 .plugin-card .plugin-dependencies { 1593 background-color: #e5f5fa; 1594 border-left: 3px solid #72aee6; 1595 margin-bottom: .5em; 1596 padding: 15px; 1597 } 1598 1599 .plugin-card .plugin-dependencies-explainer-text { 1600 margin-block: 0; 1601 } 1602 1603 .plugin-card .plugin-dependency { 1604 align-items: center; 1605 display: flex; 1606 flex-wrap: wrap; 1607 margin-top: .5em; 1608 column-gap: 1%; 1609 row-gap: .5em; 1610 } 1611 1612 .plugin-card .plugin-dependency:nth-child(2), 1613 .plugin-card .plugin-dependency:last-child { 1614 margin-top: 1em; 1615 } 1616 1617 .plugin-card .plugin-dependency-name { 1618 flex-basis: 74%; 1619 } 1620 1621 .plugin-card .plugin-dependency .more-details-link { 1622 margin-left: auto; 1623 } 1624 1625 .rtl .plugin-card .plugin-dependency .more-details-link { 1626 margin-right: auto; 1627 } 1628 1629 @media (max-width: 939px) { 1630 .plugin-card .plugin-dependency-name { 1631 flex-basis: 69%; 1632 } 1633 .plugin-card .plugin-dependency .more-details-link { 1634 } 1635 } 1636 1637 .plugins #the-list .required-by, 1638 .plugins #the-list .requires { 1639 margin-top: 1em; 1555 1640 } 1556 1641 -
trunk/src/wp-admin/includes/ajax-actions.php
r57136 r57545 4580 4580 4581 4581 /** 4582 * Handles activating a plugin via AJAX. 4583 * 4584 * @since 6.5.0 4585 */ 4586 function wp_ajax_activate_plugin() { 4587 check_ajax_referer( 'updates' ); 4588 4589 if ( empty( $_POST['name'] ) || empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) { 4590 wp_send_json_error( 4591 array( 4592 'slug' => '', 4593 'pluginName' => '', 4594 'plugin' => '', 4595 'errorCode' => 'no_plugin_specified', 4596 'errorMessage' => __( 'No plugin specified.' ), 4597 ) 4598 ); 4599 } 4600 4601 $status = array( 4602 'activate' => 'plugin', 4603 'slug' => wp_unslash( $_POST['slug'] ), 4604 'pluginName' => wp_unslash( $_POST['name'] ), 4605 'plugin' => wp_unslash( $_POST['plugin'] ), 4606 ); 4607 4608 if ( ! current_user_can( 'activate_plugin', $status['plugin'] ) ) { 4609 $status['errorMessage'] = __( 'Sorry, you are not allowed to activate plugins on this site.' ); 4610 wp_send_json_error( $status ); 4611 } 4612 4613 if ( is_plugin_active( $status['plugin'] ) ) { 4614 $status['errorMessage'] = sprintf( 4615 /* translators: %s: Plugin name. */ 4616 __( '%s is already active.' ), 4617 $status['pluginName'] 4618 ); 4619 } 4620 4621 $activated = activate_plugin( $status['plugin'] ); 4622 4623 if ( is_wp_error( $activated ) ) { 4624 $status['errorMessage'] = $activated->get_error_message(); 4625 wp_send_json_error( $status ); 4626 } 4627 4628 wp_send_json_success( $status ); 4629 } 4630 4631 /** 4582 4632 * Handles updating a plugin via AJAX. 4583 4633 * -
trunk/src/wp-admin/includes/class-plugin-upgrader.php
r57239 r57545 155 155 // Force refresh of plugin update information. 156 156 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); 157 158 $all_plugin_data = get_option( 'plugin_data', array() ); 159 $plugin_file = $this->new_plugin_data['file']; 160 unset( $this->new_plugin_data['file'] ); 161 $all_plugin_data[ $plugin_file ] = $this->new_plugin_data; 162 update_option( 'plugin_data', $all_plugin_data ); 157 163 158 164 if ( $parsed_args['overwrite_package'] ) { … … 483 489 $info = get_plugin_data( $file, false, false ); 484 490 if ( ! empty( $info['Name'] ) ) { 485 $this->new_plugin_data = $info; 491 $basename = basename( $file ); 492 $dirname = basename( dirname( $file ) ); 493 494 if ( '.' === $dirname ) { 495 $plugin_file = $basename; 496 } else { 497 $plugin_file = "$dirname/$basename"; 498 } 499 $this->new_plugin_data = ( $info ); 500 $this->new_plugin_data['file'] = $plugin_file; 486 501 break; 487 502 } -
trunk/src/wp-admin/includes/class-wp-plugin-install-list-table.php
r56599 r57545 526 526 $description = strip_tags( $plugin['short_description'] ); 527 527 528 $description .= $this->get_dependencies_notice( $plugin ); 529 528 530 /** 529 531 * Filters the plugin card description on the Add Plugins screen. … … 556 558 $action_links = array(); 557 559 558 if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) { 559 $status = install_plugin_install_status( $plugin ); 560 561 switch ( $status['status'] ) { 562 case 'install': 563 if ( $status['url'] ) { 564 if ( $compatible_php && $compatible_wp ) { 565 $action_links[] = sprintf( 566 '<a class="install-now button" data-slug="%s" href="%s" aria-label="%s" data-name="%s">%s</a>', 567 esc_attr( $plugin['slug'] ), 568 esc_url( $status['url'] ), 569 /* translators: %s: Plugin name and version. */ 570 esc_attr( sprintf( _x( 'Install %s now', 'plugin' ), $name ) ), 571 esc_attr( $name ), 572 __( 'Install Now' ) 573 ); 574 } else { 575 $action_links[] = sprintf( 576 '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', 577 _x( 'Cannot Install', 'plugin' ) 578 ); 579 } 580 } 581 break; 582 583 case 'update_available': 584 if ( $status['url'] ) { 585 if ( $compatible_php && $compatible_wp ) { 586 $action_links[] = sprintf( 587 '<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>', 588 esc_attr( $status['file'] ), 589 esc_attr( $plugin['slug'] ), 590 esc_url( $status['url'] ), 591 /* translators: %s: Plugin name and version. */ 592 esc_attr( sprintf( _x( 'Update %s now', 'plugin' ), $name ) ), 593 esc_attr( $name ), 594 __( 'Update Now' ) 595 ); 596 } else { 597 $action_links[] = sprintf( 598 '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', 599 _x( 'Cannot Update', 'plugin' ) 600 ); 601 } 602 } 603 break; 604 605 case 'latest_installed': 606 case 'newer_installed': 607 if ( is_plugin_active( $status['file'] ) ) { 608 $action_links[] = sprintf( 609 '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', 610 _x( 'Active', 'plugin' ) 611 ); 612 } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) { 613 if ( $compatible_php && $compatible_wp ) { 614 $button_text = __( 'Activate' ); 615 /* translators: %s: Plugin name. */ 616 $button_label = _x( 'Activate %s', 'plugin' ); 617 $activate_url = add_query_arg( 618 array( 619 '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ), 620 'action' => 'activate', 621 'plugin' => $status['file'], 622 ), 623 network_admin_url( 'plugins.php' ) 624 ); 625 626 if ( is_network_admin() ) { 627 $button_text = __( 'Network Activate' ); 628 /* translators: %s: Plugin name. */ 629 $button_label = _x( 'Network Activate %s', 'plugin' ); 630 $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url ); 631 } 632 633 $action_links[] = sprintf( 634 '<a href="%1$s" class="button activate-now" aria-label="%2$s">%3$s</a>', 635 esc_url( $activate_url ), 636 esc_attr( sprintf( $button_label, $plugin['name'] ) ), 637 $button_text 638 ); 639 } else { 640 $action_links[] = sprintf( 641 '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', 642 _x( 'Cannot Activate', 'plugin' ) 643 ); 644 } 645 } else { 646 $action_links[] = sprintf( 647 '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', 648 _x( 'Installed', 'plugin' ) 649 ); 650 } 651 break; 652 } 653 } 560 $action_links[] = wp_get_plugin_action_button( $name, $plugin, $compatible_php, $compatible_wp ); 654 561 655 562 $details_link = self_admin_url( … … 829 736 } 830 737 } 738 739 /** 740 * Returns a notice containing a list of dependencies required by the plugin. 741 * 742 * @since 6.5.0 743 * 744 * @param array $plugin_data An array of plugin data. See {@see plugins_api()} 745 * for the list of possible values. 746 * @return string A notice containing a list of dependencies required by the plugin, 747 * or an empty string if none is required. 748 */ 749 protected function get_dependencies_notice( $plugin_data ) { 750 if ( empty( $plugin_data['requires_plugins'] ) ) { 751 return ''; 752 } 753 754 $no_name_markup = '<div class="plugin-dependency"><span class="plugin-dependency-name">%s</span></div>'; 755 $has_name_markup = '<div class="plugin-dependency"><span class="plugin-dependency-name">%s</span> %s</div>'; 756 757 $dependencies_list = ''; 758 foreach ( $plugin_data['requires_plugins'] as $dependency ) { 759 $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $dependency ); 760 761 if ( 762 false !== $dependency_data && 763 ! empty( $dependency_data['name'] ) && 764 ! empty( $dependency_data['slug'] ) && 765 ! empty( $dependency_data['version'] ) 766 ) { 767 $more_details_link = $this->get_more_details_link( $dependency_data['name'], $dependency_data['slug'] ); 768 $dependencies_list .= sprintf( $has_name_markup, esc_html( $dependency_data['name'] ), $more_details_link ); 769 continue; 770 } 771 772 $result = plugins_api( 'plugin_information', array( 'slug' => $dependency ) ); 773 774 if ( ! empty( $result->name ) ) { 775 $more_details_link = $this->get_more_details_link( $result->name, $result->slug ); 776 $dependencies_list .= sprintf( $has_name_markup, esc_html( $result->name ), $more_details_link ); 777 continue; 778 } 779 780 $dependencies_list .= sprintf( $no_name_markup, esc_html( $dependency ) ); 781 } 782 783 $dependencies_notice = sprintf( 784 '<div class="plugin-dependencies"><p class="plugin-dependencies-explainer-text">%s</p> %s</div>', 785 '<strong>' . __( 'Additional plugins are required' ) . '</strong>', 786 $dependencies_list 787 ); 788 789 return $dependencies_notice; 790 } 791 792 /** 793 * Creates a 'More details' link for the plugin. 794 * 795 * @since 6.5.0 796 * 797 * @param string $name The plugin's name. 798 * @param string $slug The plugin's slug. 799 * @return string The 'More details' link for the plugin. 800 */ 801 protected function get_more_details_link( $name, $slug ) { 802 $url = add_query_arg( 803 array( 804 'tab' => 'plugin-information', 805 'plugin' => $slug, 806 'TB_iframe' => 'true', 807 'width' => '600', 808 'height' => '550', 809 ), 810 network_admin_url( 'plugin-install.php' ) 811 ); 812 813 $more_details_link = sprintf( 814 '<a href="%1$s" class="more-details-link thickbox open-plugin-details-modal" aria-label="%2$s" data-title="%3$s">%4$s</a>', 815 esc_url( $url ), 816 /* translators: %s: Plugin name. */ 817 sprintf( __( 'More information about %s' ), esc_html( $name ) ), 818 esc_attr( $name ), 819 __( 'More Details' ) 820 ); 821 822 return $more_details_link; 823 } 831 824 } -
trunk/src/wp-admin/includes/class-wp-plugins-list-table.php
r56824 r57545 755 755 $compatible_wp = is_wp_version_compatible( $requires_wp ); 756 756 757 $has_dependents = WP_Plugin_Dependencies::has_dependents( $plugin_file ); 758 $has_active_dependents = WP_Plugin_Dependencies::has_active_dependents( $plugin_file ); 759 $has_unmet_dependencies = WP_Plugin_Dependencies::has_unmet_dependencies( $plugin_file ); 760 $has_circular_dependency = WP_Plugin_Dependencies::has_circular_dependency( $plugin_file ); 761 757 762 if ( 'mustuse' === $context ) { 758 763 $is_active = true; … … 797 802 if ( $is_active ) { 798 803 if ( current_user_can( 'manage_network_plugins' ) ) { 799 $actions['deactivate'] = sprintf( 800 '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', 801 wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ), 802 esc_attr( $plugin_id_attr ), 803 /* translators: %s: Plugin name. */ 804 esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), 805 __( 'Network Deactivate' ) 806 ); 804 if ( $has_active_dependents ) { 805 $actions['deactivate'] = __( 'Deactivate' ) . 806 '<span class="screen-reader-text">' . 807 __( 'You cannot deactivate this plugin as other plugins require it.' ) . 808 '</span>'; 809 810 } else { 811 $deactivate_url = 'plugins.php?action=deactivate' . 812 '&plugin=' . urlencode( $plugin_file ) . 813 '&plugin_status=' . $context . 814 '&paged=' . $page . 815 '&s=' . $s; 816 817 $actions['deactivate'] = sprintf( 818 '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', 819 wp_nonce_url( $deactivate_url, 'deactivate-plugin_' . $plugin_file ), 820 esc_attr( $plugin_id_attr ), 821 /* translators: %s: Plugin name. */ 822 esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), 823 __( 'Network Deactivate' ) 824 ); 825 } 807 826 } 808 827 } else { 809 828 if ( current_user_can( 'manage_network_plugins' ) ) { 810 829 if ( $compatible_php && $compatible_wp ) { 811 $actions['activate'] = sprintf( 812 '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', 813 wp_nonce_url( 'plugins.php?action=activate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ), 814 esc_attr( $plugin_id_attr ), 815 /* translators: %s: Plugin name. */ 816 esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ), 817 __( 'Network Activate' ) 818 ); 830 if ( $has_unmet_dependencies ) { 831 $actions['activate'] = __( 'Network Activate' ) . 832 '<span class="screen-reader-text">' . 833 __( 'You cannot activate this plugin as it has unmet requirements.' ) . 834 '</span>'; 835 } else { 836 $activate_url = 'plugins.php?action=activate' . 837 '&plugin=' . urlencode( $plugin_file ) . 838 '&plugin_status=' . $context . 839 '&paged=' . $page . 840 '&s=' . $s; 841 842 $actions['activate'] = sprintf( 843 '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', 844 wp_nonce_url( $activate_url, 'activate-plugin_' . $plugin_file ), 845 esc_attr( $plugin_id_attr ), 846 /* translators: %s: Plugin name. */ 847 esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ), 848 __( 'Network Activate' ) 849 ); 850 } 819 851 } else { 820 852 $actions['activate'] = sprintf( … … 826 858 827 859 if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) { 828 $actions['delete'] = sprintf( 829 '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', 830 wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ), 831 esc_attr( $plugin_id_attr ), 832 /* translators: %s: Plugin name. */ 833 esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), 834 __( 'Delete' ) 835 ); 860 if ( $has_dependents && ! $has_circular_dependency ) { 861 $actions['delete'] = __( 'Delete' ) . 862 '<span class="screen-reader-text">' . 863 __( 'You cannot delete this plugin as other plugins require it.' ) . 864 '</span>'; 865 } else { 866 $delete_url = 'plugins.php?action=delete-selected' . 867 '&checked[]=' . urlencode( $plugin_file ) . 868 '&plugin_status=' . $context . 869 '&paged=' . $page . 870 '&s=' . $s; 871 872 $actions['delete'] = sprintf( 873 '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', 874 wp_nonce_url( $delete_url, 'bulk-plugins' ), 875 esc_attr( $plugin_id_attr ), 876 /* translators: %s: Plugin name. */ 877 esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), 878 __( 'Delete' ) 879 ); 880 } 836 881 } 837 882 } … … 847 892 } elseif ( $is_active ) { 848 893 if ( current_user_can( 'deactivate_plugin', $plugin_file ) ) { 849 $actions['deactivate'] = sprintf( 850 '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', 851 wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ), 852 esc_attr( $plugin_id_attr ), 853 /* translators: %s: Plugin name. */ 854 esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), 855 __( 'Deactivate' ) 856 ); 894 if ( $has_active_dependents ) { 895 $actions['deactivate'] = __( 'Deactivate' ) . 896 '<span class="screen-reader-text">' . 897 __( 'You cannot deactivate this plugin as other plugins depend on it.' ) . 898 '</span>'; 899 } else { 900 $deactivate_url = 'plugins.php?action=deactivate' . 901 '&plugin=' . urlencode( $plugin_file ) . 902 '&plugin_status=' . $context . 903 '&paged=' . $page . 904 '&s=' . $s; 905 906 $actions['deactivate'] = sprintf( 907 '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', 908 wp_nonce_url( $deactivate_url, 'deactivate-plugin_' . $plugin_file ), 909 esc_attr( $plugin_id_attr ), 910 /* translators: %s: Plugin name. */ 911 esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), 912 __( 'Deactivate' ) 913 ); 914 } 857 915 } 858 916 859 917 if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) { 918 $resume_url = 'plugins.php?action=resume' . 919 '&plugin=' . urlencode( $plugin_file ) . 920 '&plugin_status=' . $context . 921 '&paged=' . $page . 922 '&s=' . $s; 923 860 924 $actions['resume'] = sprintf( 861 925 '<a href="%s" id="resume-%s" class="resume-link" aria-label="%s">%s</a>', 862 wp_nonce_url( 'plugins.php?action=resume&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'resume-plugin_' . $plugin_file ),926 wp_nonce_url( $resume_url, 'resume-plugin_' . $plugin_file ), 863 927 esc_attr( $plugin_id_attr ), 864 928 /* translators: %s: Plugin name. */ … … 870 934 if ( current_user_can( 'activate_plugin', $plugin_file ) ) { 871 935 if ( $compatible_php && $compatible_wp ) { 872 $actions['activate'] = sprintf( 873 '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', 874 wp_nonce_url( 'plugins.php?action=activate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ), 875 esc_attr( $plugin_id_attr ), 876 /* translators: %s: Plugin name. */ 877 esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ), 878 __( 'Activate' ) 879 ); 936 if ( $has_unmet_dependencies ) { 937 $actions['activate'] = __( 'Activate' ) . 938 '<span class="screen-reader-text">' . 939 __( 'You cannot activate this plugin as it has unmet requirements.' ) . 940 '</span>'; 941 } else { 942 $activate_url = 'plugins.php?action=activate' . 943 '&plugin=' . urlencode( $plugin_file ) . 944 '&plugin_status=' . $context . 945 '&paged=' . $page . 946 '&s=' . $s; 947 948 $actions['activate'] = sprintf( 949 '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', 950 wp_nonce_url( $activate_url, 'activate-plugin_' . $plugin_file ), 951 esc_attr( $plugin_id_attr ), 952 /* translators: %s: Plugin name. */ 953 esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ), 954 __( 'Activate' ) 955 ); 956 } 880 957 } else { 881 958 $actions['activate'] = sprintf( … … 887 964 888 965 if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) { 889 $actions['delete'] = sprintf( 890 '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', 891 wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ), 892 esc_attr( $plugin_id_attr ), 893 /* translators: %s: Plugin name. */ 894 esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), 895 __( 'Delete' ) 896 ); 966 if ( $has_dependents && ! $has_circular_dependency ) { 967 $actions['delete'] = __( 'Delete' ) . 968 '<span class="screen-reader-text">' . 969 __( 'You cannot delete this plugin as other plugins require it.' ) . 970 '</span>'; 971 } else { 972 $delete_url = 'plugins.php?action=delete-selected' . 973 '&checked[]=' . urlencode( $plugin_file ) . 974 '&plugin_status=' . $context . 975 '&paged=' . $page . 976 '&s=' . $s; 977 978 $actions['delete'] = sprintf( 979 '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', 980 wp_nonce_url( $delete_url, 'bulk-plugins' ), 981 esc_attr( $plugin_id_attr ), 982 /* translators: %s: Plugin name. */ 983 esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), 984 __( 'Delete' ) 985 ); 986 } 897 987 } 898 988 } // End if $is_active. … … 989 1079 $class = $is_active ? 'active' : 'inactive'; 990 1080 $checkbox_id = 'checkbox_' . md5( $plugin_file ); 991 992 if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ), true ) || ! $compatible_php ) { 1081 $disabled = ''; 1082 1083 if ( $has_active_dependents || $has_unmet_dependencies ) { 1084 $disabled = 'disabled'; 1085 } 1086 1087 if ( 1088 $restrict_network_active || 1089 $restrict_network_only || 1090 in_array( $status, array( 'mustuse', 'dropins' ), true ) || 1091 ! $compatible_php 1092 ) { 993 1093 $checkbox = ''; 994 1094 } else { 995 1095 $checkbox = sprintf( 996 '< input type="checkbox" name="checked[]" value="%1$s" id="%2$s" />' .997 '< label for="%2$s"><span class="screen-reader-text">%3$s</span></label>',998 esc_attr( $plugin_file ),1096 '<label class="label-covers-full-cell" for="%1$s">' . 1097 '<span class="screen-reader-text">%2$s</span></label>' . 1098 '<input type="checkbox" name="checked[]" value="%3$s" id="%1$s" ' . $disabled . '/>', 999 1099 $checkbox_id, 1000 1100 /* translators: Hidden accessibility text. %s: Plugin name. */ 1001 sprintf( __( 'Select %s' ), $plugin_data['Name'] ) 1101 sprintf( __( 'Select %s' ), $plugin_data['Name'] ), 1102 esc_attr( $plugin_file ) 1002 1103 ); 1003 1104 } … … 1008 1109 } 1009 1110 1010 if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) 1011 || ! $compatible_php || ! $compatible_wp 1111 if ( 1112 ! empty( $totals['upgrade'] ) && 1113 ! empty( $plugin_data['update'] ) || 1114 ! $compatible_php || 1115 ! $compatible_wp 1012 1116 ) { 1013 1117 $class .= ' update'; … … 1058 1162 1059 1163 $plugin_meta = array(); 1164 1060 1165 if ( ! empty( $plugin_data['Version'] ) ) { 1061 1166 /* translators: %s: Plugin version number. */ 1062 1167 $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); 1063 1168 } 1169 1064 1170 if ( ! empty( $plugin_data['Author'] ) ) { 1065 1171 $author = $plugin_data['Author']; 1172 1066 1173 if ( ! empty( $plugin_data['AuthorURI'] ) ) { 1067 1174 $author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>'; 1068 1175 } 1176 1069 1177 /* translators: %s: Plugin author name. */ 1070 1178 $plugin_meta[] = sprintf( __( 'By %s' ), $author ); … … 1150 1258 echo '</div>'; 1151 1259 1260 if ( $has_dependents ) { 1261 $this->add_dependents_to_dependency_plugin_row( $plugin_file ); 1262 } 1263 1264 if ( WP_Plugin_Dependencies::has_dependencies( $plugin_file ) ) { 1265 $this->add_dependencies_to_dependent_plugin_row( $plugin_file ); 1266 } 1267 1268 /** 1269 * Fires after plugin row meta. 1270 * 1271 * @since 6.5.0 1272 * 1273 * @param string $plugin_file Refer to {@see 'plugin_row_meta'} filter. 1274 * @param array $plugin_data Refer to {@see 'plugin_row_meta'} filter. 1275 */ 1276 do_action( 'after_plugin_row_meta', $plugin_file, $plugin_data ); 1277 1152 1278 if ( $paused ) { 1153 1279 $notice_text = __( 'This plugin failed to load properly and is paused during recovery mode.' ); … … 1392 1518 return 'name'; 1393 1519 } 1520 1521 /** 1522 * Prints a list of other plugins that depend on the plugin. 1523 * 1524 * @since 6.5.0 1525 * 1526 * @param string $dependency The dependency's filepath, relative to the plugins directory. 1527 */ 1528 protected function add_dependents_to_dependency_plugin_row( $dependency ) { 1529 $dependent_names = WP_Plugin_Dependencies::get_dependent_names( $dependency ); 1530 1531 if ( empty( $dependent_names ) ) { 1532 return; 1533 } 1534 1535 $dependency_note = __( 'Note: this plugin cannot be deactivated or deleted until the plugins that require it are deactivated or deleted.' ); 1536 printf( 1537 '<div class="required-by"><p><strong>%1$s</strong> %2$s</p><p>%3$s</p></div>', 1538 __( 'Required by:' ), 1539 esc_html( implode( ' | ', $dependent_names ) ), 1540 $dependency_note 1541 ); 1542 } 1543 1544 /** 1545 * Prints a list of other plugins that the plugin depends on. 1546 * 1547 * @since 6.5.0 1548 * 1549 * @param string $dependent The dependent plugin's filepath, relative to the plugins directory. 1550 */ 1551 protected function add_dependencies_to_dependent_plugin_row( $dependent ) { 1552 $dependency_names = WP_Plugin_Dependencies::get_dependency_names( $dependent ); 1553 1554 if ( array() === $dependency_names ) { 1555 return; 1556 } 1557 1558 $links = array(); 1559 foreach ( $dependency_names as $slug => $name ) { 1560 $links[] = $this->get_dependency_view_details_link( $name, $slug ); 1561 } 1562 1563 $dependency_note = __( 'Note: this plugin cannot be activated until the plugins that are required by it are activated.' ); 1564 1565 printf( 1566 '<div class="requires"><p><strong>%1$s</strong> %2$s</p><p>%3$s</p></div>', 1567 __( 'Requires:' ), 1568 implode( ' | ', $links ), 1569 $dependency_note 1570 ); 1571 } 1572 1573 /** 1574 * Returns a 'View details' like link for a dependency. 1575 * 1576 * @since 6.5.0 1577 * 1578 * @param string $name The dependency's name. 1579 * @param string $slug The dependency's slug. 1580 * @return string A 'View details' link for the dependency. 1581 */ 1582 protected function get_dependency_view_details_link( $name, $slug ) { 1583 $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $slug ); 1584 1585 if ( false === $dependency_data 1586 || $name === $slug 1587 || $name !== $dependency_data['name'] 1588 || empty( $dependency_data['version'] ) 1589 ) { 1590 return $name; 1591 } 1592 1593 return $this->get_view_details_link( $name, $slug ); 1594 } 1595 1596 /** 1597 * Returns a 'View details' link for the plugin. 1598 * 1599 * @since 6.5.0 1600 * 1601 * @param string $name The plugin's name. 1602 * @param string $slug The plugin's slug. 1603 * @return string A 'View details' link for the plugin. 1604 */ 1605 protected function get_view_details_link( $name, $slug ) { 1606 $url = add_query_arg( 1607 array( 1608 'tab' => 'plugin-information', 1609 'plugin' => $slug, 1610 'TB_iframe' => 'true', 1611 'width' => '600', 1612 'height' => '550', 1613 ), 1614 network_admin_url( 'plugin-install.php' ) 1615 ); 1616 1617 $name_attr = esc_attr( $name ); 1618 return sprintf( 1619 "<a href='%s' class='thickbox open-plugin-details-modal' aria-label='%s' data-title='%s'>%s</a>", 1620 esc_url( $url ), 1621 /* translators: %s: Plugin name. */ 1622 sprintf( __( 'More information about %s' ), $name_attr ), 1623 $name_attr, 1624 esc_html( $name ) 1625 ); 1626 } 1394 1627 } -
trunk/src/wp-admin/includes/plugin-install.php
r56571 r57545 885 885 echo "<div id='$tab-footer'>\n"; 886 886 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 */ 922 function 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' ) ) { 888 961 switch ( $status['status'] ) { 889 962 case 'install': 890 963 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 ); 893 974 } 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' ) 897 978 ); 898 979 } 899 980 } 900 981 break; 982 901 983 case 'update_available': 902 984 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 ); 905 996 } 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' ) 909 1000 ); 910 1001 } 911 1002 } 912 1003 break; 1004 1005 case 'latest_installed': 913 1006 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 } 916 1054 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 } 926 1059 } -
trunk/src/wp-admin/includes/plugin.php
r57337 r57545 46 46 * @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers. 47 47 * @since 5.8.0 Added support for `Update URI` header. 48 * @since 6.5.0 Added support for `Requires Plugins` header. 48 49 * 49 50 * @param string $plugin_file Absolute path to the main plugin file. … … 54 55 * Plugin data. Values will be empty if not supplied by the plugin. 55 56 * 56 * @type string $Name Name of the plugin. Should be unique. 57 * @type string $PluginURI Plugin URI. 58 * @type string $Version Plugin version. 59 * @type string $Description Plugin description. 60 * @type string $Author Plugin author's name. 61 * @type string $AuthorURI Plugin author's website address (if set). 62 * @type string $TextDomain Plugin textdomain. 63 * @type string $DomainPath Plugin's relative directory path to .mo files. 64 * @type bool $Network Whether the plugin can only be activated network-wide. 65 * @type string $RequiresWP Minimum required version of WordPress. 66 * @type string $RequiresPHP Minimum required version of PHP. 67 * @type string $UpdateURI ID of the plugin for update purposes, should be a URI. 68 * @type string $Title Title of the plugin and link to the plugin's site (if set). 69 * @type string $AuthorName Plugin author's name. 57 * @type string $Name Name of the plugin. Should be unique. 58 * @type string $PluginURI Plugin URI. 59 * @type string $Version Plugin version. 60 * @type string $Description Plugin description. 61 * @type string $Author Plugin author's name. 62 * @type string $AuthorURI Plugin author's website address (if set). 63 * @type string $TextDomain Plugin textdomain. 64 * @type string $DomainPath Plugin's relative directory path to .mo files. 65 * @type bool $Network Whether the plugin can only be activated network-wide. 66 * @type string $RequiresWP Minimum required version of WordPress. 67 * @type string $RequiresPHP Minimum required version of PHP. 68 * @type string $UpdateURI ID of the plugin for update purposes, should be a URI. 69 * @type string $RequiresPlugins Comma separated list of dot org plugin slugs. 70 * @type string $Title Title of the plugin and link to the plugin's site (if set). 71 * @type string $AuthorName Plugin author's name. 70 72 * } 71 73 */ … … 73 75 74 76 $default_headers = array( 75 'Name' => 'Plugin Name', 76 'PluginURI' => 'Plugin URI', 77 'Version' => 'Version', 78 'Description' => 'Description', 79 'Author' => 'Author', 80 'AuthorURI' => 'Author URI', 81 'TextDomain' => 'Text Domain', 82 'DomainPath' => 'Domain Path', 83 'Network' => 'Network', 84 'RequiresWP' => 'Requires at least', 85 'RequiresPHP' => 'Requires PHP', 86 'UpdateURI' => 'Update URI', 77 'Name' => 'Plugin Name', 78 'PluginURI' => 'Plugin URI', 79 'Version' => 'Version', 80 'Description' => 'Description', 81 'Author' => 'Author', 82 'AuthorURI' => 'Author URI', 83 'TextDomain' => 'Text Domain', 84 'DomainPath' => 'Domain Path', 85 'Network' => 'Network', 86 'RequiresWP' => 'Requires at least', 87 'RequiresPHP' => 'Requires PHP', 88 'UpdateURI' => 'Update URI', 89 'RequiresPlugins' => 'Requires Plugins', 87 90 // Site Wide Only is deprecated in favor of Network. 88 '_sitewide' => 'Site Wide Only',91 '_sitewide' => 'Site Wide Only', 89 92 ); 90 93 … … 331 334 } 332 335 336 $new_plugin_data = array(); 333 337 foreach ( $plugin_files as $plugin_file ) { 334 338 if ( ! is_readable( "$plugin_root/$plugin_file" ) ) { … … 343 347 } 344 348 349 $new_plugin_file = str_replace( 350 trailingslashit( WP_PLUGIN_DIR ), 351 '', 352 "$plugin_root/$plugin_file" 353 ); 354 355 $new_plugin_data[ $new_plugin_file ] = $plugin_data; 345 356 $wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data; 346 357 } … … 350 361 $cache_plugins[ $plugin_folder ] = $wp_plugins; 351 362 wp_cache_set( 'plugins', $cache_plugins, 'plugins' ); 363 update_option( 'plugin_data', $new_plugin_data ); 352 364 353 365 return $wp_plugins; … … 952 964 953 965 $plugin_translations = wp_get_installed_translations( 'plugins' ); 966 $all_plugin_data = get_option( 'plugin_data', array() ); 954 967 955 968 $errors = array(); … … 996 1009 continue; 997 1010 } 1011 unset( $all_plugin_data[ $plugin_file ] ); 998 1012 999 1013 $plugin_slug = dirname( $plugin_file ); … … 1044 1058 return new WP_Error( 'could_not_remove_plugin', sprintf( $message, implode( ', ', $errors ) ) ); 1045 1059 } 1060 update_option( 'plugin_data', $all_plugin_data ); 1046 1061 1047 1062 return true; … … 1115 1130 * Validates the plugin requirements for WordPress version and PHP version. 1116 1131 * 1117 * Uses the information from `Requires at least` and `Requires PHP` headers1132 * Uses the information from `Requires at least`, `Requires PHP` and `Requires Plugins` headers 1118 1133 * defined in the plugin's main PHP file. 1119 1134 * … … 1122 1137 * main PHP file, with `readme.txt` as a fallback. 1123 1138 * @since 5.8.0 Removed support for using `readme.txt` as a fallback. 1139 * @since 6.5.0 Added support for the 'Requires Plugins' header. 1124 1140 * 1125 1141 * @param string $plugin Path to the plugin file relative to the plugins directory. … … 1130 1146 1131 1147 $requirements = array( 1132 'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '', 1133 'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '', 1148 'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '', 1149 'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '', 1150 'requires_plugins' => ! empty( $plugin_headers['RequiresPlugins'] ) ? $plugin_headers['RequiresPlugins'] : '', 1134 1151 ); 1135 1152 … … 1186 1203 } 1187 1204 1205 if ( WP_Plugin_Dependencies::has_unmet_dependencies( $plugin ) ) { 1206 $dependencies = WP_Plugin_Dependencies::get_dependencies( $plugin ); 1207 $unmet_dependencies = array(); 1208 1209 foreach ( $dependencies as $dependency ) { 1210 $dependency_file = WP_Plugin_Dependencies::get_dependency_filepath( $dependency ); 1211 1212 if ( false === $dependency_file ) { 1213 $unmet_dependencies['not_installed'][] = $dependency; 1214 } elseif ( is_plugin_inactive( $dependency_file ) ) { 1215 $unmet_dependencies['inactive'][] = $dependency; 1216 } 1217 } 1218 1219 return new WP_Error( 1220 'plugin_missing_dependencies', 1221 '<p>' . sprintf( 1222 /* translators: %s: Plugin name. */ 1223 _x( '<strong>Error:</strong> %s requires plugins that are not installed or activated.', 'plugin' ), 1224 $plugin_headers['Name'] 1225 ) . '</p>', 1226 $unmet_dependencies 1227 ); 1228 } 1229 1188 1230 return true; 1189 1231 } -
trunk/src/wp-admin/plugin-install.php
r55412 r57545 135 135 */ 136 136 require_once ABSPATH . 'wp-admin/admin-header.php'; 137 138 WP_Plugin_Dependencies::display_admin_notice_for_unmet_dependencies(); 139 WP_Plugin_Dependencies::display_admin_notice_for_deactivated_dependents(); 140 WP_Plugin_Dependencies::display_admin_notice_for_circular_dependencies(); 137 141 ?> 138 142 <div class="wrap <?php echo esc_attr( "plugin-install-tab-$tab" ); ?>"> -
trunk/src/wp-admin/plugins.php
r56695 r57545 740 740 ?> 741 741 742 <?php WP_Plugin_Dependencies::display_admin_notice_for_unmet_dependencies(); ?> 743 <?php WP_Plugin_Dependencies::display_admin_notice_for_deactivated_dependents(); ?> 744 <?php WP_Plugin_Dependencies::display_admin_notice_for_circular_dependencies(); ?> 742 745 <div class="wrap"> 743 746 <h1 class="wp-heading-inline"> -
trunk/src/wp-includes/functions.php
r57543 r57545 3360 3360 */ 3361 3361 3362 3362 // Divide the header string into 4 byte groups. 3363 3363 $magic = str_split( $magic, 8 ); 3364 3364 -
trunk/src/wp-includes/load.php
r57504 r57545 987 987 $network_plugins = is_multisite() ? wp_get_active_network_plugins() : false; 988 988 989 $invalid_plugins = array(); 989 990 foreach ( $active_plugins as $plugin ) { 990 991 if ( ! validate_file( $plugin ) // $plugin must validate as file. … … 995 996 ) { 996 997 $plugins[] = WP_PLUGIN_DIR . '/' . $plugin; 998 } else { 999 $invalid_plugins[] = $plugin; 1000 } 1001 } 1002 1003 if ( ! empty( $invalid_plugins ) ) { 1004 $all_plugin_data = get_option( 'plugin_data', array() ); 1005 1006 if ( ! empty( $all_plugin_data ) ) { 1007 foreach ( $invalid_plugins as $invalid_plugin ) { 1008 unset( $all_plugin_data[ $invalid_plugin ] ); 1009 } 1010 1011 update_option( 'plugin_data', $all_plugin_data ); 997 1012 } 998 1013 } … … 1191 1206 'update-plugin', // Update an existing plugin. 1192 1207 'update-theme', // Update an existing theme. 1208 'activate-plugin', // Activating an existing plugin. 1193 1209 ); 1194 1210 -
trunk/src/wp-settings.php
r57539 r57545 413 413 $GLOBALS['wp_plugin_paths'] = array(); 414 414 415 // Load and initialize WP_Plugin_Dependencies. 416 require_once ABSPATH . WPINC . '/class-wp-plugin-dependencies.php'; 417 if ( ! defined( 'WP_RUN_CORE_TESTS' ) ) { 418 WP_Plugin_Dependencies::initialize(); 419 } 420 415 421 // Load must-use plugins. 416 422 foreach ( wp_get_mu_plugins() as $mu_plugin ) { … … 487 493 488 494 // Load active plugins. 495 $all_plugin_data = get_option( 'plugin_data', array() ); 496 $failed_plugins = array(); 497 $update_plugin_data = false; 489 498 foreach ( wp_get_active_and_valid_plugins() as $plugin ) { 499 $plugin_file = str_replace( trailingslashit( WP_PLUGIN_DIR ), '', $plugin ); 500 if ( ! isset( $all_plugin_data[ $plugin_file ] ) ) { 501 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 502 $all_plugin_data[ $plugin_file ] = get_plugin_data( WP_PLUGIN_DIR . "/$plugin_file" ); 503 504 $update_plugin_data = true; 505 } 506 507 $plugin_headers = $all_plugin_data[ $plugin_file ]; 508 $errors = array(); 509 $requirements = array( 510 'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '', 511 'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '', 512 'requires_plugins' => ! empty( $plugin_headers['RequiresPlugins'] ) ? $plugin_headers['RequiresPlugins'] : '', 513 ); 514 $compatible_wp = is_wp_version_compatible( $requirements['requires'] ); 515 $compatible_php = is_php_version_compatible( $requirements['requires_php'] ); 516 $dependencies_met = ! WP_Plugin_Dependencies::has_unmet_dependencies( $plugin_file ); 517 518 $php_update_message = '</p><p>' . sprintf( 519 /* translators: %s: URL to Update PHP page. */ 520 __( '<a href="%s">Learn more about updating PHP</a>.' ), 521 esc_url( wp_get_update_php_url() ) 522 ); 523 524 $annotation = wp_get_update_php_annotation(); 525 526 if ( $annotation ) { 527 $php_update_message .= '</p><p><em>' . $annotation . '</em>'; 528 } 529 530 if ( ! $dependencies_met ) { 531 $errors[] = sprintf( 532 /* translators: %s: The plugin's name. */ 533 _x( '%s has unmet dependencies.', 'plugin' ), 534 $plugin_headers['Name'] 535 ); 536 } 537 538 if ( ! $compatible_wp && ! $compatible_php ) { 539 $errors[] = sprintf( 540 /* translators: 1: Current WordPress version, 2: Current PHP version, 3: Plugin name, 4: Required WordPress version, 5: Required PHP version. */ 541 _x( '<strong>Error:</strong> Current versions of WordPress (%1$s) and PHP (%2$s) do not meet minimum requirements for %3$s. The plugin requires WordPress %4$s and PHP %5$s.', 'plugin' ), 542 get_bloginfo( 'version' ), 543 PHP_VERSION, 544 $plugin_headers['Name'], 545 $requirements['requires'], 546 $requirements['requires_php'] 547 ) . $php_update_message; 548 } elseif ( ! $compatible_php ) { 549 $errors[] = sprintf( 550 /* translators: 1: Current PHP version, 2: Plugin name, 3: Required PHP version. */ 551 _x( '<strong>Error:</strong> Current PHP version (%1$s) does not meet minimum requirements for %2$s. The plugin requires PHP %3$s.', 'plugin' ), 552 PHP_VERSION, 553 $plugin_headers['Name'], 554 $requirements['requires_php'] 555 ) . $php_update_message; 556 } elseif ( ! $compatible_wp ) { 557 $errors[] = sprintf( 558 /* translators: 1: Current WordPress version, 2: Plugin name, 3: Required WordPress version. */ 559 _x( '<strong>Error:</strong> Current WordPress version (%1$s) does not meet minimum requirements for %2$s. The plugin requires WordPress %3$s.', 'plugin' ), 560 get_bloginfo( 'version' ), 561 $plugin_headers['Name'], 562 $requirements['requires'] 563 ); 564 } 565 566 if ( ! empty( $errors ) ) { 567 $failed_plugins[ $plugin_file ] = ''; 568 foreach ( $errors as $error ) { 569 $failed_plugins[ $plugin_file ] .= wp_get_admin_notice( 570 $error, 571 array( 572 'type' => 'error', 573 'dismissible' => true, 574 ) 575 ); 576 } 577 continue; 578 } 579 490 580 wp_register_plugin_realpath( $plugin ); 491 581 … … 505 595 unset( $plugin, $_wp_plugin_file ); 506 596 597 if ( $update_plugin_data ) { 598 update_option( 'plugin_data', $all_plugin_data ); 599 } 600 601 if ( ! empty( $failed_plugins ) ) { 602 add_action( 603 'admin_notices', 604 function () use ( $failed_plugins ) { 605 global $pagenow; 606 607 if ( 'index.php' === $pagenow || 'plugins.php' === $pagenow ) { 608 echo implode( '', $failed_plugins ); 609 } 610 } 611 ); 612 } 613 unset( $failed_plugins ); 614 507 615 // Load pluggable functions. 508 616 require ABSPATH . WPINC . '/pluggable.php';
Note: See TracChangeset
for help on using the changeset viewer.