Make WordPress Core

Ticket #46573: 46573.5.patch

File 46573.5.patch, 136.3 KB (added by Clorith, 5 years ago)
  • src/js/_enqueues/admin/site-health.js

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1/**
     2 * Interactions used by the Site Health modules in WordPress.
     3 *
     4 * @output wp-admin/js/site-health.js
     5 */
     6
     7/* global ajaxurl, SiteHealth, wp */
     8
     9// Debug information copy section.
     10jQuery(document).ready(function($) {
     11        $( '.health-check-copy-field' ).click(function( e ) {
     12                var $textarea = $( '#system-information-' + $( this ).data( 'copy-field' ) + '-copy-field' ),
     13                        $wrapper = $( this ).closest( 'div' );
     14
     15                e.preventDefault();
     16
     17                $textarea.select();
     18
     19                if ( document.execCommand( 'copy' ) ) {
     20                        $( '.copy-field-success', $wrapper ).addClass( 'visible' );
     21                        $( this ).focus();
     22
     23                        wp.a11y.speak( SiteHealth.string.site_info_copied, 'polite' );
     24                }
     25        });
     26
     27        $( '.health-check-toggle-copy-section' ).click(function( e ) {
     28                var $copySection = $( '.system-information-copy-wrapper' );
     29
     30                e.preventDefault();
     31
     32                if ( $copySection.hasClass( 'hidden' ) ) {
     33                        $copySection.removeClass( 'hidden' );
     34
     35                        $( this ).text( SiteHealth.string.site_info_hide_copy );
     36                } else {
     37                        $copySection.addClass( 'hidden' );
     38
     39                        $( this ).text( SiteHealth.string.site_info_show_copy );
     40                }
     41        });
     42});
     43
     44// Accordion handling in various areas.
     45jQuery(document).ready(function($) {
     46        $( '.health-check-accordion' ).on( 'click', '.health-check-accordion-trigger', function() {
     47                var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) );
     48
     49                if ( isExpanded ) {
     50                        $( this ).attr( 'aria-expanded', 'false' );
     51                        $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', true );
     52                } else {
     53                        $( this ).attr( 'aria-expanded', 'true' );
     54                        $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false );
     55                }
     56        });
     57
     58        $( '.health-check-accordion' ).on( 'keyup', '.health-check-accordion-trigger', function( e ) {
     59                if ( '38' === e.keyCode.toString() ) {
     60                        $( '.health-check-accordion-trigger', $( this ).closest( 'dt' ).prevAll( 'dt' ) ).focus();
     61                } else if ( '40' === e.keyCode.toString() ) {
     62                        $( '.health-check-accordion-trigger', $( this ).closest( 'dt' ).nextAll( 'dt' ) ).focus();
     63                }
     64        });
     65});
     66
     67// Site Health test handling.
     68jQuery( document ).ready(function( $ ) {
     69        var data;
     70
     71        $( '.site-health-view-passed' ).on( 'click', function() {
     72                var goodIssuesWrapper = $( '#health-check-issues-good' );
     73
     74                goodIssuesWrapper.toggleClass( 'hidden' );
     75                $( this ).attr( 'aria-expanded', ! goodIssuesWrapper.hasClass( 'hidden' ) );
     76        });
     77
     78        function HCAppendIssue( issue ) {
     79                var htmlOutput,
     80                        issueWrapper,
     81                        issueCounter;
     82
     83                SiteHealth.site_status.issues[ issue.status ]++;
     84
     85                issueWrapper = $( '#health-check-issues-' + issue.status );
     86
     87                issueCounter = $( '.issue-count', issueWrapper );
     88
     89                htmlOutput = '<dt role="heading" aria-level="4">\n' +
     90                        '                <button aria-expanded="false" class="health-check-accordion-trigger" aria-controls="health-check-accordion-block-' + issue.test + '" id="health-check-accordion-heading-' + issue.test + '" type="button">\n' +
     91                        '                    <span class="title">\n' +
     92                        '                        ' + issue.label + '\n' +
     93                        '                    </span>\n' +
     94                        '                    <span class="badge ' + issue.badge.color + '">' + issue.badge.label + '</span>\n' +
     95                        '                    <span class="icon"></span>\n' +
     96                        '                </button>\n' +
     97                        '            </dt>\n' +
     98                        '            <dd id="health-check-accordion-block-' + issue.test + '" aria-labelledby="health-check-accordion-heading-' + issue.test + '" role="region" class="health-check-accordion-panel" hidden="hidden">\n' +
     99                        '                ' + issue.description + '\n' +
     100                        '                <div class="actions"><p>' + issue.actions + '</p></div>' +
     101                        '            </dd>';
     102
     103                issueCounter.text( SiteHealth.site_status.issues[ issue.status ] );
     104                $( '.issues', '#health-check-issues-' + issue.status ).append( htmlOutput );
     105        }
     106
     107        function HCRecalculateProgression() {
     108                var r, c, pct;
     109                var $progressBar = $( '#progressbar' );
     110                var $circle = $( '#progressbar svg #bar' );
     111                var totalTests = parseInt( SiteHealth.site_status.issues.good, 0 ) + parseInt( SiteHealth.site_status.issues.recommended, 0 ) + ( parseInt( SiteHealth.site_status.issues.critical, 0 ) * 1.5 );
     112                var failedTests = parseInt( SiteHealth.site_status.issues.recommended, 0 ) + ( parseInt( SiteHealth.site_status.issues.critical, 0 ) * 1.5 );
     113                var val = 100 - Math.ceil( ( failedTests / totalTests ) * 100 );
     114
     115                if ( 0 === totalTests ) {
     116                        $progressBar.addClass( 'hidden' );
     117                        return;
     118                }
     119
     120                $progressBar.removeClass( 'loading' );
     121
     122                if ( isNaN( val ) ) {
     123                        val = 100;
     124                }
     125
     126                r = $circle.attr( 'r' );
     127                c = Math.PI * ( r * 2 );
     128
     129                if ( val < 0 ) {
     130                        val = 0;
     131                }
     132                if ( val > 100 ) {
     133                        val = 100;
     134                }
     135
     136                pct = ( ( 100 - val ) / 100 ) * c;
     137
     138                $circle.css( { strokeDashoffset: pct } );
     139
     140                if ( parseInt( SiteHealth.site_status.issues.critical, 0 ) < 1 ) {
     141                        $( '#health-check-issues-critical' ).addClass( 'hidden' );
     142                }
     143
     144                if ( parseInt( SiteHealth.site_status.issues.recommended, 0 ) < 1 ) {
     145                        $( '#health-check-issues-recommended' ).addClass( 'hidden' );
     146                }
     147
     148                if ( val >= 50 ) {
     149                        $circle.addClass( 'orange' ).removeClass( 'red' );
     150                }
     151
     152                if ( val >= 90 ) {
     153                        $circle.addClass( 'green' ).removeClass( 'orange' );
     154                }
     155
     156                if ( 100 === val ) {
     157                        $( '.site-status-all-clear' ).removeClass( 'hide' );
     158                        $( '.site-status-has-issues' ).addClass( 'hide' );
     159                }
     160
     161                $progressBar.attr( 'data-pct', val );
     162                $progressBar.attr( 'aria-valuenow', val );
     163
     164                $( '.health-check-body' ).attr( 'aria-hidden', false );
     165
     166                $.post(
     167                        ajaxurl,
     168                        {
     169                                'action': 'health-check-site-status-result',
     170                                '_wpnonce': SiteHealth.nonce.site_status_result,
     171                                'counts': SiteHealth.site_status.issues
     172                        }
     173                );
     174
     175                wp.a11y.speak( SiteHealth.string.site_health_complete_screen_reader.replace( '%s', val + '%' ), 'polite' );
     176        }
     177
     178        function maybeRunNextAsyncTest() {
     179                var doCalculation = true;
     180
     181                if ( SiteHealth.site_status.async.length >= 1 ) {
     182                        $.each( SiteHealth.site_status.async, function() {
     183                                var data = {
     184                                        'action': 'health-check-site-status',
     185                                        'feature': this.test,
     186                                        '_wpnonce': SiteHealth.nonce.site_status
     187                                };
     188
     189                                if ( this.completed ) {
     190                                        return true;
     191                                }
     192
     193                                doCalculation = false;
     194
     195                                this.completed = true;
     196
     197                                $.post(
     198                                        ajaxurl,
     199                                        data,
     200                                        function( response ) {
     201                                                HCAppendIssue( response.data );
     202                                                maybeRunNextAsyncTest();
     203                                        }
     204                                );
     205
     206                                return false;
     207                        } );
     208                }
     209
     210                if ( doCalculation ) {
     211                        HCRecalculateProgression();
     212                }
     213        }
     214
     215        if ( 'undefined' !== typeof SiteHealth ) {
     216                if ( 0 === SiteHealth.site_status.direct.length && 0 === SiteHealth.site_status.async.length ) {
     217                        HCRecalculateProgression();
     218                } else {
     219                        SiteHealth.site_status.issues = {
     220                                'good': 0,
     221                                'recommended': 0,
     222                                'critical': 0
     223                        };
     224                }
     225
     226                if ( SiteHealth.site_status.direct.length > 0 ) {
     227                        $.each( SiteHealth.site_status.direct, function() {
     228                                HCAppendIssue( this );
     229                        });
     230                }
     231
     232                if ( SiteHealth.site_status.async.length > 0 ) {
     233                        data = {
     234                                'action': 'health-check-site-status',
     235                                'feature': SiteHealth.site_status.async[0].test,
     236                                '_wpnonce': SiteHealth.nonce.site_status
     237                        };
     238
     239                        SiteHealth.site_status.async[0].completed = true;
     240
     241                        $.post(
     242                                ajaxurl,
     243                                data,
     244                                function( response ) {
     245                                        HCAppendIssue( response.data );
     246                                        maybeRunNextAsyncTest();
     247                                }
     248                        );
     249                } else {
     250                        HCRecalculateProgression();
     251                }
     252        }
     253});
  • src/wp-admin/css/site-health.css

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1body.site-health {
     2        background: #fff;
     3}
     4
     5body.site-health #wpcontent {
     6        padding-left: 0;
     7}
     8
     9body.site-health .wrap {
     10        margin-right: 0;
     11        margin-left: 0;
     12}
     13
     14body.site-health .wrap h2 {
     15        padding: 1rem 0.5rem;
     16        font-weight: 600;
     17}
     18
     19@media all and (min-width: 960px) {
     20        body.site-health .wrap h2 {
     21                padding: 1rem 0;
     22        }
     23}
     24
     25body.site-health ul li,
     26body.site-health ol li {
     27        line-height: 1.5;
     28}
     29
     30body.site-health .health-check-header {
     31        text-align: center;
     32        margin-bottom: 1rem;
     33        border-bottom: 2px solid #ccc;
     34}
     35
     36body.site-health .health-check-header .title-section {
     37        display: flex;
     38        align-items: center;
     39        justify-content: center;
     40}
     41
     42body.site-health .health-check-header .title-section h1 {
     43        display: inline-block;
     44        font-weight: 600;
     45        margin: 1rem 0.8rem 1rem 0.8rem;
     46}
     47
     48body.site-health .health-check-header .title-section #progressbar {
     49        display: inline-block;
     50        height: 40px;
     51        width: 40px;
     52        margin: 0;
     53        border-radius: 100%;
     54        position: relative;
     55        font-weight: 600;
     56        font-size: 0.4rem;
     57}
     58
     59body.site-health .health-check-header .title-section #progressbar:after {
     60        position: absolute;
     61        display: block;
     62        height: 80px;
     63        width: 80px;
     64        left: 50%;
     65        top: 50%;
     66        content: attr(data-pct) "%";
     67        margin-top: -40px;
     68        margin-left: -40px;
     69        border-radius: 100%;
     70        line-height: 80px;
     71        font-size: 2em;
     72}
     73
     74body.site-health .health-check-header .title-section #progressbar.hidden {
     75        display: none;
     76}
     77
     78body.site-health .health-check-header .title-section #progressbar.loading:after {
     79        animation: loadingEllipsis 3s infinite ease-in-out;
     80}
     81
     82body.site-health .health-check-header .title-section #progressbar.loading svg #bar {
     83        stroke-dashoffset: 0;
     84        stroke: #adc5d2;
     85        animation: loadingPulse 3s infinite ease-in-out;
     86}
     87
     88body.site-health .health-check-header .title-section #progressbar svg circle {
     89        stroke-dashoffset: 0;
     90        transition: stroke-dashoffset 1s linear;
     91        stroke: #ccc;
     92        stroke-width: 2em;
     93}
     94
     95body.site-health .health-check-header .title-section #progressbar svg #bar {
     96        stroke-dashoffset: 565;
     97        stroke: #dc3232;
     98}
     99
     100body.site-health .health-check-header .title-section #progressbar svg #bar.green {
     101        stroke: #46b450;
     102}
     103
     104body.site-health .health-check-header .title-section #progressbar svg #bar.orange {
     105        stroke: #ffb900;
     106}
     107
     108@keyframes loadingPulse {
     109        0% {
     110                stroke: #adc5d2;
     111        }
     112        50% {
     113                stroke: #00a0d2;
     114        }
     115        100% {
     116                stroke: #adc5d2;
     117        }
     118}
     119
     120@keyframes loadingEllipsis {
     121        0% {
     122                content: ".";
     123        }
     124        50% {
     125                content: "..";
     126        }
     127        100% {
     128                content: "...";
     129        }
     130}
     131
     132body.site-health .health-check-header .tabs-wrapper {
     133        display: block;
     134}
     135
     136body.site-health .health-check-header .tabs-wrapper .tab {
     137        float: none;
     138        display: inline-block;
     139        text-decoration: none;
     140        color: inherit;
     141        padding: 0.5rem;
     142        margin: 0 1rem;
     143        border-bottom: 4px solid transparent;
     144        transition: border 0.5s ease-in-out;
     145}
     146
     147body.site-health .health-check-header .tabs-wrapper .tab.active {
     148        border-bottom: 4px solid #00a0d2;
     149        font-weight: 600;
     150}
     151
     152body.site-health .health-check-header .tabs-wrapper .tab:focus, body.site-health .health-check-header .tabs-wrapper .tab:hover, body.site-health .health-check-header .tabs-wrapper .tab:active {
     153        border-bottom: 4px solid #00a0d2;
     154}
     155
     156body.site-health .health-check-body {
     157        max-width: 800px;
     158        width: 100%;
     159        margin: 0 auto;
     160}
     161
     162body.site-health .health-check-table thead th:first-child,
     163body.site-health .health-check-table thead td:first-child {
     164        width: 30%;
     165}
     166
     167body.site-health .health-check-table tbody td {
     168        width: 70%;
     169}
     170
     171body.site-health .health-check-table tbody td:first-child {
     172        width: 30%;
     173}
     174
     175body.site-health .health-check-table tbody td ul,
     176body.site-health .health-check-table tbody td ol {
     177        margin: 0;
     178}
     179
     180body.site-health .pass:before,
     181body.site-health .good:before {
     182        content: "\f147";
     183        display: inline-block;
     184        color: #46b450;
     185        font-family: dashicons;
     186}
     187
     188body.site-health .warning:before {
     189        content: "\f460";
     190        display: inline-block;
     191        color: #ffb900;
     192        font-family: dashicons;
     193}
     194
     195body.site-health .info:before {
     196        content: "\f348";
     197        display: inline-block;
     198        color: #00a0d2;
     199        font-family: dashicons;
     200}
     201
     202body.site-health .fail:before,
     203body.site-health .error:before {
     204        content: "\f335";
     205        display: inline-block;
     206        color: #dc3232;
     207        font-family: dashicons;
     208}
     209
     210body.site-health .spinner {
     211        float: none;
     212}
     213
     214body.site-health .system-information-copy-wrapper {
     215        display: block;
     216        margin: 1rem 0;
     217}
     218
     219body.site-health .system-information-copy-wrapper.hidden {
     220        display: none;
     221}
     222
     223body.site-health .system-information-copy-wrapper textarea {
     224        width: 100%;
     225}
     226
     227body.site-health .system-information-copy-wrapper .copy-button-wrapper {
     228        margin: 0.5rem 0 1rem;
     229}
     230
     231body.site-health .copy-field-success {
     232        display: none;
     233        color: #40860a;
     234        line-height: 1.8;
     235        margin-left: 0.5rem;
     236}
     237
     238body.site-health .copy-field-success.visible {
     239        display: inline-block;
     240}
     241
     242body.site-health .site-status-has-issues {
     243        display: block;
     244}
     245
     246body.site-health .site-status-has-issues.hide {
     247        display: none;
     248}
     249
     250body.site-health h3 {
     251        padding: 0 0.5rem;
     252        font-weight: 600;
     253}
     254
     255body.site-health .view-more {
     256        text-align: center;
     257}
     258
     259body.site-health .issues-wrapper {
     260        margin-bottom: 5rem;
     261}
     262
     263body.site-health .site-status-all-clear {
     264        display: flex;
     265        flex-direction: column;
     266        align-items: center;
     267        justify-content: center;
     268        text-align: center;
     269        height: 100%;
     270        width: 100%;
     271        margin-top: 0;
     272}
     273
     274@media all and (min-width: 784px) {
     275        body.site-health .site-status-all-clear {
     276                margin: 5rem 0;
     277        }
     278}
     279
     280body.site-health .site-status-all-clear.hide {
     281        display: none;
     282}
     283
     284body.site-health .site-status-all-clear .dashicons {
     285        font-size: 150px;
     286        height: 130px;
     287        width: 150px;
     288}
     289
     290body.site-health .site-status-all-clear .encouragement {
     291        font-size: 1.5rem;
     292        font-weight: 600;
     293}
     294
     295body.site-health .site-status-all-clear p {
     296        margin: 0;
     297}
     298
     299body .health-check-accordion {
     300        border: 1px solid #d1d1d1;
     301}
     302
     303body .health-check-accordion dt {
     304        font-weight: 600;
     305        border-top: 1px solid #d1d1d1;
     306}
     307
     308body .health-check-accordion dt:first-child {
     309        border-radius: 0.3em 0.3em 0 0;
     310        border-top: none;
     311}
     312
     313body .health-check-accordion .health-check-accordion-trigger {
     314        background: #fff;
     315        border: 0;
     316        color: #212121;
     317        cursor: pointer;
     318        display: block;
     319        font-weight: 400;
     320        margin: 0;
     321        padding: 1em 1.5em;
     322        position: relative;
     323        text-align: left;
     324        width: 100%;
     325}
     326
     327body .health-check-accordion .health-check-accordion-trigger:hover, body .health-check-accordion .health-check-accordion-trigger:focus, body .health-check-accordion .health-check-accordion-trigger:active {
     328        background: #dedede;
     329}
     330
     331body .health-check-accordion .health-check-accordion-trigger .title {
     332        display: inline-block;
     333        pointer-events: none;
     334        font-weight: 600;
     335}
     336
     337body .health-check-accordion .health-check-accordion-trigger .icon {
     338        border: solid #191e23;
     339        border-width: 0 2px 2px 0;
     340        height: 0.5rem;
     341        pointer-events: none;
     342        position: absolute;
     343        right: 1.5em;
     344        top: 50%;
     345        transform: translateY(-60%) rotate(45deg);
     346        width: 0.5rem;
     347}
     348
     349body .health-check-accordion .health-check-accordion-trigger .badge {
     350        display: inline-block;
     351        padding: 0.1rem 0.5rem;
     352        background-color: #dcdcdc;
     353        color: #000;
     354        font-weight: 600;
     355        margin: 0 0.5rem;
     356}
     357
     358body .health-check-accordion .health-check-accordion-trigger .badge.blue {
     359        background-color: #0073af;
     360        color: #fff;
     361}
     362
     363body .health-check-accordion .health-check-accordion-trigger .badge.orange {
     364        background-color: #ffb900;
     365        color: #000;
     366}
     367
     368body .health-check-accordion .health-check-accordion-trigger .badge.red {
     369        background-color: #dc3232;
     370        color: #fff;
     371}
     372
     373body .health-check-accordion .health-check-accordion-trigger .badge.green {
     374        background-color: #40860a;
     375        color: #fff;
     376}
     377
     378body .health-check-accordion .health-check-accordion-trigger .badge.pink {
     379        background-color: #f4b0fc;
     380        color: #000;
     381}
     382
     383body .health-check-accordion .health-check-accordion-trigger .badge.gray {
     384        background-color: #ccc;
     385        color: #000;
     386}
     387
     388body .health-check-accordion .health-check-accordion-trigger .badge.light-blue {
     389        background-color: #10e9fb;
     390        color: #000;
     391}
     392
     393body .health-check-accordion .health-check-accordion-trigger .badge.light-green {
     394        background-color: #60f999;
     395        color: #000;
     396}
     397
     398body .health-check-accordion .health-check-accordion-trigger[aria-expanded="true"] .icon {
     399        transform: translateY(-50%) rotate(-135deg);
     400}
     401
     402body .health-check-accordion .health-check-accordion-panel {
     403        margin: 0;
     404        padding: 1em 1.5em;
     405        background: #fff;
     406}
     407
     408body .health-check-accordion .health-check-accordion-panel > div {
     409        display: block;
     410}
     411
     412body .health-check-accordion .health-check-accordion-panel[hidden] {
     413        display: none;
     414}
     415
     416body .health-check-accordion dl dd {
     417        margin: 0 0 0.5em 2em;
     418}
  • src/wp-admin/css/wp-admin.css

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    1212@import url(widgets.css);
    1313@import url(site-icon.css);
    1414@import url(l10n.css);
     15@import url(site-health.css);
  • src/wp-admin/includes/ajax-actions.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    48334833
    48344834        wp_send_json_success( $response );
    48354835}
     4836
     4837function wp_ajax_health_check_site_status() {
     4838        check_ajax_referer( 'health-check-site-status' );
     4839
     4840        if ( ! current_user_can( 'manage_options' ) ) {
     4841                wp_send_json_error();
     4842        }
     4843
     4844        if ( ! class_exists( 'WP_Site_Health' ) ) {
     4845                require_once( ABSPATH . 'wp-admin/includes/class-wp-site-health.php' );
     4846        }
     4847
     4848        $site_health = new WP_Site_Health();
     4849
     4850        $function = sprintf(
     4851                'json_test_%s',
     4852                $_POST['feature']
     4853        );
     4854
     4855        if ( ! method_exists( $site_health, $function ) || ! is_callable( array( $site_health, $function ) ) ) {
     4856                return;
     4857        }
     4858
     4859        call_user_func( array( $site_health, $function ) );
     4860}
     4861
     4862function wp_ajax_health_check_site_status_result() {
     4863        check_ajax_referer( 'health-check-site-status-result' );
     4864
     4865        if ( ! current_user_can( 'manage_options' ) ) {
     4866                wp_send_json_error();
     4867        }
     4868
     4869        set_transient( 'health-check-site-status-result', wp_json_encode( $_POST['counts'] ) );
     4870
     4871        wp_send_json_success();
     4872}
  • src/wp-admin/includes/class-wp-debug-data.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2/**
     3 * Class for providing debug data based on a users WordPress environment.
     4 *
     5 * @package WordPress
     6 * @subpackage Site_Health
     7 * @since 5.2.0
     8 */
     9
     10// Make sure the file is not directly accessible.
     11if ( ! defined( 'ABSPATH' ) ) {
     12        die( 'We\'re sorry, but you can not directly access this file.' );
     13}
     14
     15/**
     16 * Class WP_Debug_Data
     17 */
     18class WP_Debug_Data {
     19
     20        /**
     21         * Calls all core functions to check for updates
     22         *
     23         * @return void
     24         */
     25        static function check_for_updates() {
     26
     27                wp_version_check();
     28                wp_update_plugins();
     29                wp_update_themes();
     30
     31        }
     32
     33        static function debug_data( $locale = null ) {
     34                if ( ! empty( $locale ) ) {
     35                        // Change the language used for translations
     36                        if ( function_exists( 'switch_to_locale' ) ) {
     37                                $original_locale = get_locale();
     38                                $switched_locale = switch_to_locale( $locale );
     39                        }
     40                }
     41                global $wpdb;
     42
     43                $upload_dir = wp_upload_dir();
     44                if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
     45                        $wp_config_path = ABSPATH . 'wp-config.php';
     46                        // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
     47                } elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
     48                        $wp_config_path = dirname( ABSPATH ) . '/wp-config.php';
     49                }
     50
     51                $core_current_version = get_bloginfo( 'version' );
     52                $core_updates         = get_core_updates();
     53                $core_update_needed   = '';
     54
     55                foreach ( $core_updates as $core => $update ) {
     56                        if ( 'upgrade' === $update->response ) {
     57                                // translators: %s: Latest WordPress version number.
     58                                $core_update_needed = ' ' . sprintf( __( '( Latest version: %s )' ), $update->version );
     59                        } else {
     60                                $core_update_needed = '';
     61                        }
     62                }
     63
     64                $info = array(
     65                        'wp-core'             => array(
     66                                'label'  => __( 'WordPress' ),
     67                                'fields' => array(
     68                                        'version' => array(
     69                                                'label' => __( 'Version' ),
     70                                                'value' => $core_current_version . $core_update_needed,
     71                                        ),
     72                                        'language' => array(
     73                                                'label' => __( 'Language' ),
     74                                                'value' => ( ! empty( $locale ) ? $original_locale : get_locale() ),
     75                                        ),
     76                                        'home_url' => array(
     77                                                'label'   => __( 'Home URL' ),
     78                                                'value'   => get_bloginfo( 'url' ),
     79                                                'private' => true,
     80                                        ),
     81                                        'site_url' => array(
     82                                                'label'   => __( 'Site URL' ),
     83                                                'value'   => get_bloginfo( 'wpurl' ),
     84                                                'private' => true,
     85                                        ),
     86                                        'permalink' => array(
     87                                                'label' => __( 'Permalink structure' ),
     88                                                'value' => get_option( 'permalink_structure' ),
     89                                        ),
     90                                        'https_status' => array(
     91                                                'label' => __( 'Is this site using HTTPS?' ),
     92                                                'value' => ( is_ssl() ? __( 'Yes' ) : __( 'No' ) ),
     93                                        ),
     94                                        'user_registration' => array(
     95                                                'label' => __( 'Can anyone register on this site?' ),
     96                                                'value' => ( get_option( 'users_can_register' ) ? __( 'Yes' ) : __( 'No' ) ),
     97                                        ),
     98                                        'default_comment_status' => array(
     99                                                'label' => __( 'Default comment status' ),
     100                                                'value' => get_option( 'default_comment_status' ),
     101                                        ),
     102                                        'multisite' => array(
     103                                                'label' => __( 'Is this a multisite?' ),
     104                                                'value' => ( is_multisite() ? __( 'Yes' ) : __( 'No' ) ),
     105                                        ),
     106                                ),
     107                        ),
     108                        'wp-dropins'          => array(
     109                                'label'       => __( 'Drop-ins' ),
     110                                'description' => __( 'Drop-ins are single files that replace or enhance WordPress features in ways that are not possible for traditional plugins' ),
     111                                'fields'      => array(),
     112                        ),
     113                        'wp-active-theme'     => array(
     114                                'label'  => __( 'Active Theme' ),
     115                                'fields' => array(),
     116                        ),
     117                        'wp-themes'           => array(
     118                                'label'      => __( 'Other themes' ),
     119                                'show_count' => true,
     120                                'fields'     => array(),
     121                        ),
     122                        'wp-mu-plugins'       => array(
     123                                'label'      => __( 'Must Use Plugins' ),
     124                                'show_count' => true,
     125                                'fields'     => array(),
     126                        ),
     127                        'wp-plugins-active'   => array(
     128                                'label'      => __( 'Active Plugins' ),
     129                                'show_count' => true,
     130                                'fields'     => array(),
     131                        ),
     132                        'wp-plugins-inactive' => array(
     133                                'label'      => __( 'Inactive Plugins' ),
     134                                'show_count' => true,
     135                                'fields'     => array(),
     136                        ),
     137                        'wp-media'            => array(
     138                                'label'  => __( 'Media handling' ),
     139                                'fields' => array(),
     140                        ),
     141                        'wp-server'           => array(
     142                                'label'       => __( 'Server' ),
     143                                'description' => __( 'The options shown below relate to your server setup. If changes are required, you may need your web host\'s assistance.' ),
     144                                'fields'      => array(),
     145                        ),
     146                        'wp-database'         => array(
     147                                'label'  => __( 'Database' ),
     148                                'fields' => array(),
     149                        ),
     150                        'wp-constants'        => array(
     151                                'label'       => __( 'WordPress Constants' ),
     152                                'description' => __( 'These values represent values set in your websites code which affect WordPress in various ways that may be of importance when seeking help with your site.' ),
     153                                'fields'      => array(
     154                                        'ABSPATH' => array(
     155                                                'label'   => 'ABSPATH',
     156                                                'value'   => ( ! defined( 'ABSPATH' ) ? __( 'Undefined' ) : ABSPATH ),
     157                                                'private' => true,
     158                                        ),
     159                                        'WP_HOME' => array(
     160                                                'label' => 'WP_HOME',
     161                                                'value' => ( ! defined( 'WP_HOME' ) ? __( 'Undefined' ) : WP_HOME ),
     162                                        ),
     163                                        'WP_SITEURL' => array(
     164                                                'label' => 'WP_SITEURL',
     165                                                'value' => ( ! defined( 'WP_SITEURL' ) ? __( 'Undefined' ) : WP_SITEURL ),
     166                                        ),
     167                                        'WP_DEBUG' => array(
     168                                                'label' => 'WP_DEBUG',
     169                                                'value' => ( ! defined( 'WP_DEBUG' ) ? __( 'Undefined' ) : ( WP_DEBUG ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     170                                        ),
     171                                        'WP_MAX_MEMORY_LIMIT' => array(
     172                                                'label' => 'WP_MAX_MEMORY_LIMIT',
     173                                                'value' => ( ! defined( 'WP_MAX_MEMORY_LIMIT' ) ? __( 'Undefined' ) : WP_MAX_MEMORY_LIMIT ),
     174                                        ),
     175                                        'WP_DEBUG_DISPLAY' => array(
     176                                                'label' => 'WP_DEBUG_DISPLAY',
     177                                                'value' => ( ! defined( 'WP_DEBUG_DISPLAY' ) ? __( 'Undefined' ) : ( WP_DEBUG_DISPLAY ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     178                                        ),
     179                                        'WP_DEBUG_LOG' => array(
     180                                                'label' => 'WP_DEBUG_LOG',
     181                                                'value' => ( ! defined( 'WP_DEBUG_LOG' ) ? __( 'Undefined' ) : ( WP_DEBUG_LOG ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     182                                        ),
     183                                        'SCRIPT_DEBUG' => array(
     184                                                'label' => 'SCRIPT_DEBUG',
     185                                                'value' => ( ! defined( 'SCRIPT_DEBUG' ) ? __( 'Undefined' ) : ( SCRIPT_DEBUG ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     186                                        ),
     187                                        'WP_CACHE' => array(
     188                                                'label' => 'WP_CACHE',
     189                                                'value' => ( ! defined( 'WP_CACHE' ) ? __( 'Undefined' ) : ( WP_CACHE ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     190                                        ),
     191                                        'CONCATENATE_SCRIPTS' => array(
     192                                                'label' => 'CONCATENATE_SCRIPTS',
     193                                                'value' => ( ! defined( 'CONCATENATE_SCRIPTS' ) ? __( 'Undefined' ) : ( CONCATENATE_SCRIPTS ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     194                                        ),
     195                                        'COMPRESS_SCRIPTS' => array(
     196                                                'label' => 'COMPRESS_SCRIPTS',
     197                                                'value' => ( ! defined( 'COMPRESS_SCRIPTS' ) ? __( 'Undefined' ) : ( COMPRESS_SCRIPTS ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     198                                        ),
     199                                        'COMPRESS_CSS' => array(
     200                                                'label' => 'COMPRESS_CSS',
     201                                                'value' => ( ! defined( 'COMPRESS_CSS' ) ? __( 'Undefined' ) : ( COMPRESS_CSS ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     202                                        ),
     203                                        'WP_LOCAL_DEV' => array(
     204                                                'label' => 'WP_LOCAL_DEV',
     205                                                'value' => ( ! defined( 'WP_LOCAL_DEV' ) ? __( 'Undefined' ) : ( WP_LOCAL_DEV ? __( 'Enabled' ) : __( 'Disabled' ) ) ),
     206                                        ),
     207                                ),
     208                        ),
     209                        'wp-filesystem'       => array(
     210                                'label'       => __( 'Filesystem Permissions' ),
     211                                'description' => __( 'The status of various locations WordPress needs to write files in various scenarios.' ),
     212                                'fields'      => array(
     213                                        'all' => array(
     214                                                'label' => __( 'The main WordPress directory' ),
     215                                                'value' => ( wp_is_writable( ABSPATH ) ? __( 'Writable' ) : __( 'Not writable' ) ),
     216                                        ),
     217                                        'wp-content' => array(
     218                                                'label' => __( 'The wp-content directory' ),
     219                                                'value' => ( wp_is_writable( WP_CONTENT_DIR ) ? __( 'Writable' ) : __( 'Not writable' ) ),
     220                                        ),
     221                                        'uploads' => array(
     222                                                'label' => __( 'The uploads directory' ),
     223                                                'value' => ( wp_is_writable( $upload_dir['basedir'] ) ? __( 'Writable' ) : __( 'Not writable' ) ),
     224                                        ),
     225                                        'plugins' => array(
     226                                                'label' => __( 'The plugins directory' ),
     227                                                'value' => ( wp_is_writable( WP_PLUGIN_DIR ) ? __( 'Writable' ) : __( 'Not writable' ) ),
     228                                        ),
     229                                        'themes' => array(
     230                                                'label' => __( 'The themes directory' ),
     231                                                'value' => ( wp_is_writable( get_template_directory() . '/..' ) ? __( 'Writable' ) : __( 'Not writable' ) ),
     232                                        ),
     233                                ),
     234                        ),
     235                );
     236
     237                if ( is_multisite() ) {
     238                        $network_query = new WP_Network_Query();
     239                        $network_ids   = $network_query->query( array(
     240                                'fields'        => 'ids',
     241                                'number'        => 100,
     242                                'no_found_rows' => false,
     243                        ) );
     244
     245                        $site_count = 0;
     246                        foreach ( $network_ids as $network_id ) {
     247                                $site_count += get_blog_count( $network_id );
     248                        }
     249
     250                        $info['wp-core']['fields']['user_count'] = array(
     251                                'label' => __( 'User Count' ),
     252                                'value' => get_user_count(),
     253                        );
     254                        $info['wp-core']['fields']['site_count'] = array(
     255                                'label' => __( 'Site Count' ),
     256                                'value' => $site_count,
     257                        );
     258                        $info['wp-core']['fields']['network_count'] = array(
     259                                'label' => __( 'Network Count' ),
     260                                'value' => $network_query->found_networks,
     261                        );
     262                } else {
     263                        $user_count = count_users();
     264
     265                        $info['wp-core']['fields']['user_count'] = array(
     266                                'label' => __( 'User Count' ),
     267                                'value' => $user_count['total_users'],
     268                        );
     269                }
     270
     271                // WordPress features requiring processing.
     272                $wp_dotorg = wp_remote_get( 'https://wordpress.org', array(
     273                        'timeout' => 10,
     274                ) );
     275                if ( ! is_wp_error( $wp_dotorg ) ) {
     276                        $info['wp-core']['fields']['dotorg_communication'] = array(
     277                                'label' => __( 'Communication with WordPress.org' ),
     278                                'value' => sprintf(
     279                                        __( 'WordPress.org is reachable' )
     280                                ),
     281                        );
     282                } else {
     283                        $info['wp-core']['fields']['dotorg_communication'] = array(
     284                                'label' => __( 'Communication with WordPress.org' ),
     285                                'value' => sprintf(
     286                                // translators: %1$s: The IP address WordPress.org resolves to. %2$s: The error returned by the lookup.
     287                                        __( 'Unable to reach WordPress.org at %1$s: %2$s' ),
     288                                        gethostbyname( 'wordpress.org' ),
     289                                        $wp_dotorg->get_error_message()
     290                                ),
     291                        );
     292                }
     293
     294                // Get drop-ins.
     295                $dropins            = get_dropins();
     296                $dropin_description = _get_dropins();
     297                foreach ( $dropins as $dropin_key => $dropin ) {
     298                        $info['wp-dropins']['fields'][ sanitize_key( $dropin_key ) ] = array(
     299                                'label' => $dropin_key,
     300                                'value' => $dropin_description[ $dropin_key ][0],
     301                        );
     302                }
     303
     304                // Populate the media fields.
     305                $info['wp-media']['fields']['image_editor'] = array(
     306                        'label' => __( 'Active editor' ),
     307                        'value' => _wp_image_editor_choose(),
     308                );
     309
     310                // Get ImageMagic information, if available.
     311                if ( class_exists( 'Imagick' ) ) {
     312                        // Save the Imagick instance for later use.
     313                        $imagick         = new Imagick();
     314                        $imagick_version = $imagick->getVersion();
     315                } else {
     316                        $imagick_version = __( 'Imagick not available' );
     317                }
     318                $info['wp-media']['fields']['imagick_module_version'] = array(
     319                        'label' => __( 'Imagick Module Version' ),
     320                        'value' => ( is_array( $imagick_version ) ? $imagick_version['versionNumber'] : $imagick_version ),
     321                );
     322                $info['wp-media']['fields']['imagemagick_version'] = array(
     323                        'label' => __( 'ImageMagick Version' ),
     324                        'value' => ( is_array( $imagick_version ) ? $imagick_version['versionString'] : $imagick_version ),
     325                );
     326
     327                // If Imagick is used as our editor, provide some more information about its limitations.
     328                if ( 'WP_Image_Editor_Imagick' === _wp_image_editor_choose() && isset( $imagick ) && $imagick instanceof Imagick ) {
     329                        $limits = array(
     330                                'area'   => ( defined( 'imagick::RESOURCETYPE_AREA' ) ? size_format( $imagick->getResourceLimit( imagick::RESOURCETYPE_AREA ) ) : __( 'Not Available' ) ),
     331                                'disk'   => ( defined( 'imagick::RESOURCETYPE_DISK' ) ? $imagick->getResourceLimit( imagick::RESOURCETYPE_DISK ) : __( 'Not Available' ) ),
     332                                'file'   => ( defined( 'imagick::RESOURCETYPE_FILE' ) ? $imagick->getResourceLimit( imagick::RESOURCETYPE_FILE ) : __( 'Not Available' ) ),
     333                                'map'    => ( defined( 'imagick::RESOURCETYPE_MAP' ) ? size_format( $imagick->getResourceLimit( imagick::RESOURCETYPE_MAP ) ) : __( 'Not Available' ) ),
     334                                'memory' => ( defined( 'imagick::RESOURCETYPE_MEMORY' ) ? size_format( $imagick->getResourceLimit( imagick::RESOURCETYPE_MEMORY ) ) : __( 'Not Available' ) ),
     335                                'thread' => ( defined( 'imagick::RESOURCETYPE_THREAD' ) ? $imagick->getResourceLimit( imagick::RESOURCETYPE_THREAD ) : __( 'Not Available' ) ),
     336                        );
     337
     338                        $info['wp-media']['fields']['imagick_limits'] = array(
     339                                'label' => __( 'Imagick Resource Limits' ),
     340                                'value' => $limits,
     341                        );
     342                }
     343
     344                // Get GD information, if available.
     345                if ( function_exists( 'gd_info' ) ) {
     346                        $gd = gd_info();
     347                } else {
     348                        $gd = false;
     349                }
     350                $info['wp-media']['fields']['gd_version'] = array(
     351                        'label' => __( 'GD Version' ),
     352                        'value' => ( is_array( $gd ) ? $gd['GD Version'] : __( 'GD not available' ) ),
     353                );
     354
     355                // Get Ghostscript information, if available.
     356                if ( function_exists( 'exec' ) ) {
     357                        $gs = exec( 'gs --version' );
     358                        $gs = ( ! empty( $gs ) ? $gs : __( 'Not available' ) );
     359                } else {
     360                        $gs = __( 'Unable to determine if Ghostscript is installed' );
     361                }
     362                $info['wp-media']['fields']['ghostscript_version'] = array(
     363                        'label' => __( 'Ghostscript Version' ),
     364                        'value' => $gs,
     365                );
     366
     367                // Populate the server debug fields.
     368                $info['wp-server']['fields']['server_architecture'] = array(
     369                        'label' => __( 'Server architecture' ),
     370                        'value' => ( ! function_exists( 'php_uname' ) ? __( 'Unable to determine server architecture' ) : sprintf( '%s %s %s', php_uname( 's' ), php_uname( 'r' ), php_uname( 'm' ) ) ),
     371                );
     372                $info['wp-server']['fields']['httpd_software'] = array(
     373                        'label' => __( 'Web Server Software' ),
     374                        'value' => ( isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : __( 'Unable to determine what web server software is used' ) ),
     375                );
     376                $info['wp-server']['fields']['php_version'] = array(
     377                        'label' => __( 'PHP Version' ),
     378                        'value' => ( ! function_exists( 'phpversion' ) ? __( 'Unable to determine PHP version' ) : sprintf(
     379                                '%s %s',
     380                                phpversion(),
     381                                ( 64 === PHP_INT_SIZE * 8 ? __( '(Supports 64bit values)' ) : __( '(Does not support 64bit values)' ) )
     382                        )
     383                        ),
     384                );
     385                $info['wp-server']['fields']['php_sapi'] = array(
     386                        'label' => __( 'PHP SAPI' ),
     387                        'value' => ( ! function_exists( 'php_sapi_name' ) ? __( 'Unable to determine PHP SAPI' ) : php_sapi_name() ),
     388                );
     389
     390                if ( ! function_exists( 'ini_get' ) ) {
     391                        $info['wp-server']['fields']['ini_get'] = array(
     392                                'label' => __( 'Server settings' ),
     393                                'value' => __( 'Unable to determine some settings as the ini_get() function has been disabled' ),
     394                        );
     395                } else {
     396                        $info['wp-server']['fields']['max_input_variables'] = array(
     397                                'label' => __( 'PHP max input variables' ),
     398                                'value' => ini_get( 'max_input_vars' ),
     399                        );
     400                        $info['wp-server']['fields']['time_limit'] = array(
     401                                'label' => __( 'PHP time limit' ),
     402                                'value' => ini_get( 'max_execution_time' ),
     403                        );
     404                        $info['wp-server']['fields']['memory_limit'] = array(
     405                                'label' => __( 'PHP memory limit' ),
     406                                'value' => ini_get( 'memory_limit' ),
     407                        );
     408                        $info['wp-server']['fields']['max_input_time'] = array(
     409                                'label' => __( 'Max input time' ),
     410                                'value' => ini_get( 'max_input_time' ),
     411                        );
     412                        $info['wp-server']['fields']['upload_max_size'] = array(
     413                                'label' => __( 'Upload max filesize' ),
     414                                'value' => ini_get( 'upload_max_filesize' ),
     415                        );
     416                        $info['wp-server']['fields']['php_post_max_size'] = array(
     417                                'label' => __( 'PHP post max size' ),
     418                                'value' => ini_get( 'post_max_size' ),
     419                        );
     420                }
     421
     422                if ( function_exists( 'curl_version' ) ) {
     423                        $curl                          = curl_version();
     424                        $info['wp-server']['fields']['curl_version'] = array(
     425                                'label' => __( 'cURL Version' ),
     426                                'value' => sprintf( '%s %s', $curl['version'], $curl['ssl_version'] ),
     427                        );
     428                } else {
     429                        $info['wp-server']['fields']['curl_version'] = array(
     430                                'label' => __( 'cURL Version' ),
     431                                'value' => __( 'Your server does not support cURL' ),
     432                        );
     433                }
     434
     435                $info['wp-server']['fields']['suhosin'] = array(
     436                        'label' => __( 'SUHOSIN installed' ),
     437                        'value' => ( ( extension_loaded( 'suhosin' ) || ( defined( 'SUHOSIN_PATCH' ) && constant( 'SUHOSIN_PATCH' ) ) ) ? __( 'Yes' ) : __( 'No' ) ),
     438                );
     439
     440                $info['wp-server']['fields']['imagick_availability'] = array(
     441                        'label' => __( 'Is the Imagick library available' ),
     442                        'value' => ( extension_loaded( 'imagick' ) ? __( 'Yes' ) : __( 'No' ) ),
     443                );
     444
     445                // Check if a .htaccess file exists.
     446                if ( is_file( ABSPATH . '/.htaccess' ) ) {
     447                        // If the file exists, grab the content of it.
     448                        $htaccess_content = file_get_contents( ABSPATH . '/.htaccess' );
     449
     450                        // Filter away the core WordPress rules.
     451                        $filtered_htaccess_content = trim( preg_replace( '/\# BEGIN WordPress[\s\S]+?# END WordPress/si', '', $htaccess_content ) );
     452
     453                        $info['wp-server']['fields']['htaccess_extra_rules'] = array(
     454                                'label' => __( 'htaccess rules' ),
     455                                'value' => ( ! empty( $filtered_htaccess_content ) ? __( 'Custom rules have been added to your htaccess file' ) : __( 'Your htaccess file only contains core WordPress features' ) ),
     456                        );
     457                }
     458
     459                // Populate the database debug fields.
     460                if ( is_resource( $wpdb->dbh ) ) {
     461                        // Old mysql extension.
     462                        $extension = 'mysql';
     463                } elseif ( is_object( $wpdb->dbh ) ) {
     464                        // mysqli or PDO.
     465                        $extension = get_class( $wpdb->dbh );
     466                } else {
     467                        // Unknown sql extension.
     468                        $extension = null;
     469                }
     470
     471                if ( method_exists( $wpdb, 'db_version' ) ) {
     472                        if ( $wpdb->use_mysqli ) {
     473                                // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_get_server_info
     474                                $server = mysqli_get_server_info( $wpdb->dbh );
     475                        } else {
     476                                // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysql_get_server_info
     477                                $server = mysql_get_server_info( $wpdb->dbh );
     478                        }
     479                } else {
     480                        $server = null;
     481                }
     482
     483                if ( isset( $wpdb->use_mysqli ) && $wpdb->use_mysqli ) {
     484                        $client_version = $wpdb->dbh->client_info;
     485                } else {
     486                        // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysql_get_client_info
     487                        if ( preg_match( '|[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}|', mysql_get_client_info(), $matches ) ) {
     488                                $client_version = $matches[0];
     489                        } else {
     490                                $client_version = null;
     491                        }
     492                }
     493
     494                $info['wp-database']['fields']['extension'] = array(
     495                        'label' => __( 'Extension' ),
     496                        'value' => $extension,
     497                );
     498                $info['wp-database']['fields']['server_version'] = array(
     499                        'label' => __( 'Server version' ),
     500                        'value' => $server,
     501                );
     502                $info['wp-database']['fields']['client_version'] = array(
     503                        'label' => __( 'Client version' ),
     504                        'value' => $client_version,
     505                );
     506                $info['wp-database']['fields']['database_user'] = array(
     507                        'label'   => __( 'Database user' ),
     508                        'value'   => $wpdb->dbuser,
     509                        'private' => true,
     510                );
     511                $info['wp-database']['fields']['database_host'] = array(
     512                        'label'   => __( 'Database host' ),
     513                        'value'   => $wpdb->dbhost,
     514                        'private' => true,
     515                );
     516                $info['wp-database']['fields']['database_name'] = array(
     517                        'label'   => __( 'Database name' ),
     518                        'value'   => $wpdb->dbname,
     519                        'private' => true,
     520                );
     521                $info['wp-database']['fields']['database_prefix'] = array(
     522                        'label'   => __( 'Database prefix' ),
     523                        'value'   => $wpdb->prefix,
     524                        'private' => true,
     525                );
     526
     527                // List must use plugins if there are any.
     528                $mu_plugins = get_mu_plugins();
     529
     530                foreach ( $mu_plugins as $plugin_path => $plugin ) {
     531                        $plugin_version = $plugin['Version'];
     532                        $plugin_author  = $plugin['Author'];
     533
     534                        $plugin_version_string = __( 'No version or author information available' );
     535
     536                        if ( ! empty( $plugin_version ) && ! empty( $plugin_author ) ) {
     537                                // translators: %1$s: Plugin version number. %2$s: Plugin author name.
     538                                $plugin_version_string = sprintf( __( 'Version %1$s by %2$s' ), $plugin_version, $plugin_author );
     539                        }
     540                        if ( empty( $plugin_version ) && ! empty( $plugin_author ) ) {
     541                                // translators: %s: Plugin author name.
     542                                $plugin_version_string = sprintf( __( 'By %s' ), $plugin_author );
     543                        }
     544                        if ( ! empty( $plugin_version ) && empty( $plugin_author ) ) {
     545                                // translators: %s: Plugin version number.
     546                                $plugin_version_string = sprintf( __( 'Version %s' ), $plugin_version );
     547                        }
     548
     549                        $info['wp-mu-plugins']['fields'][ sanitize_key( $plugin['Name'] ) ] = array(
     550                                'label' => $plugin['Name'],
     551                                'value' => $plugin_version_string,
     552                        );
     553                }
     554
     555                // List all available plugins.
     556                $plugins        = get_plugins();
     557                $plugin_updates = get_plugin_updates();
     558
     559                foreach ( $plugins as $plugin_path => $plugin ) {
     560                        $plugin_part = ( is_plugin_active( $plugin_path ) ) ? 'wp-plugins-active' : 'wp-plugins-inactive';
     561
     562                        $plugin_version = $plugin['Version'];
     563                        $plugin_author  = $plugin['Author'];
     564
     565                        $plugin_version_string = __( 'No version or author information available' );
     566
     567                        if ( ! empty( $plugin_version ) && ! empty( $plugin_author ) ) {
     568                                // translators: %1$s: Plugin version number. %2$s: Plugin author name.
     569                                $plugin_version_string = sprintf( __( 'Version %1$s by %2$s' ), $plugin_version, $plugin_author );
     570                        }
     571                        if ( empty( $plugin_version ) && ! empty( $plugin_author ) ) {
     572                                // translators: %s: Plugin author name.
     573                                $plugin_version_string = sprintf( __( 'By %s' ), $plugin_author );
     574                        }
     575                        if ( ! empty( $plugin_version ) && empty( $plugin_author ) ) {
     576                                // translators: %s: Plugin version number.
     577                                $plugin_version_string = sprintf( __( 'Version %s' ), $plugin_version );
     578                        }
     579
     580                        if ( array_key_exists( $plugin_path, $plugin_updates ) ) {
     581                                // translators: %s: Latest plugin version number.
     582                                $plugin_update_needed = ' ' . sprintf( __( '( Latest version: %s )' ), $plugin_updates[ $plugin_path ]->update->new_version );
     583                        } else {
     584                                $plugin_update_needed = '';
     585                        }
     586
     587                        $info[ $plugin_part ]['fields'][ sanitize_key( $plugin['Name'] ) ] = array(
     588                                'label' => $plugin['Name'],
     589                                'value' => $plugin_version_string . $plugin_update_needed,
     590                        );
     591                }
     592
     593                // Populate the section for the currently active theme.
     594                global $_wp_theme_features;
     595                $theme_features = array();
     596                if ( ! empty( $_wp_theme_features ) ) {
     597                        foreach ( $_wp_theme_features as $feature => $options ) {
     598                                $theme_features[] = $feature;
     599                        }
     600                }
     601
     602                $active_theme  = wp_get_theme();
     603                $theme_updates = get_theme_updates();
     604
     605                if ( array_key_exists( $active_theme->stylesheet, $theme_updates ) ) {
     606                        // translators: %s: Latest theme version number.
     607                        $theme_update_needed_active = ' ' . sprintf( __( '( Latest version: %s )' ), $theme_updates[ $active_theme->stylesheet ]->update['new_version'] );
     608                } else {
     609                        $theme_update_needed_active = '';
     610                }
     611
     612                $info['wp-active-theme']['fields'] = array(
     613                        'name' => array(
     614                                'label' => __( 'Name' ),
     615                                // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
     616                                'value' => $active_theme->Name,
     617                        ),
     618                        'version' => array(
     619                                'label' => __( 'Version' ),
     620                                // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
     621                                'value' => $active_theme->Version . $theme_update_needed_active,
     622                        ),
     623                        'author' => array(
     624                                'label' => __( 'Author' ),
     625                                // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
     626                                'value' => wp_kses( $active_theme->Author, array() ),
     627                        ),
     628                        'author_website' => array(
     629                                'label' => __( 'Author website' ),
     630                                'value' => ( $active_theme->offsetGet( 'Author URI' ) ? $active_theme->offsetGet( 'Author URI' ) : __( 'Undefined' ) ),
     631                        ),
     632                        'parent_theme' => array(
     633                                'label' => __( 'Parent theme' ),
     634                                'value' => ( $active_theme->parent_theme ? $active_theme->parent_theme : __( 'Not a child theme' ) ),
     635                        ),
     636                        'theme_features' => array(
     637                                'label' => __( 'Supported theme features' ),
     638                                'value' => implode( ', ', $theme_features ),
     639                        ),
     640                );
     641
     642                // Populate a list of all themes available in the install.
     643                $all_themes = wp_get_themes();
     644
     645                foreach ( $all_themes as $theme_slug => $theme ) {
     646                        // Ignore the currently active theme from the list of all themes.
     647                        if ( $active_theme->stylesheet == $theme_slug ) {
     648                                continue;
     649                        }
     650                        // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
     651                        $theme_version = $theme->Version;
     652                        // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
     653                        $theme_author = $theme->Author;
     654
     655                        $theme_version_string = __( 'No version or author information available' );
     656
     657                        if ( ! empty( $theme_version ) && ! empty( $theme_author ) ) {
     658                                // translators: %1$s: Theme version number. %2$s: Theme author name.
     659                                $theme_version_string = sprintf( __( 'Version %1$s by %2$s' ), $theme_version, wp_kses( $theme_author, array() ) );
     660                        }
     661                        if ( empty( $theme_version ) && ! empty( $theme_author ) ) {
     662                                // translators: %s: Theme author name.
     663                                $theme_version_string = sprintf( __( 'By %s' ), wp_kses( $theme_author, array() ) );
     664                        }
     665                        if ( ! empty( $theme_version ) && empty( $theme_author ) ) {
     666                                // translators: %s: Theme version number.
     667                                $theme_version_string = sprintf( __( 'Version %s' ), $theme_version );
     668                        }
     669
     670                        if ( array_key_exists( $theme_slug, $theme_updates ) ) {
     671                                // translators: %s: Latest theme version number.
     672                                $theme_update_needed = ' ' . sprintf( __( '( Latest version: %s )' ), $theme_updates[ $theme_slug ]->update['new_version'] );
     673                        } else {
     674                                $theme_update_needed = '';
     675                        }
     676
     677                        $info['wp-themes']['fields'][ sanitize_key( $theme->Name ) ] = array(
     678                                'label' => sprintf(
     679                                // translators: %1$s: Theme name. %2$s: Theme slug.
     680                                        __( '%1$s (%2$s)' ),
     681                                        // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
     682                                        $theme->Name,
     683                                        $theme_slug
     684                                ),
     685                                'value' => $theme_version_string . $theme_update_needed,
     686                        );
     687                }
     688
     689                // Add more filesystem checks
     690                if ( defined( 'WPMU_PLUGIN_DIR' ) && is_dir( WPMU_PLUGIN_DIR ) ) {
     691                        $info['wp-filesystem']['fields']['mu_plugin_directory'] = array(
     692                                'label' => __( 'The Must Use Plugins directory' ),
     693                                'value' => ( wp_is_writable( WPMU_PLUGIN_DIR ) ? __( 'Writable' ) : __( 'Not writable' ) ),
     694                        );
     695                }
     696
     697                /**
     698                 * Add or modify new debug sections.
     699                 *
     700                 * Plugin or themes may wish to introduce their own debug information without creating additional admin pages for this
     701                 * kind of information as it is rarely needed, they can then utilize this filter to introduce their own sections.
     702                 *
     703                 * Array keys added by core are all prefixed with `wp-`, plugins and themes are encouraged to use their own slug as
     704                 * a prefix, both for consistency as well as avoiding key collisions.
     705                 *
     706                 * @since 5.2.0
     707                 *
     708                 * @param array $args {
     709                 *     The debug information to be added to the core information page.
     710                 *
     711                 *     @type string  $label        The title for this section of the debug output.
     712                 *     @type string  $description  Optional. A description for your information section which may contain basic HTML
     713                 *                                 markup: `em`, `strong` and `a` for linking to documentation or putting emphasis.
     714                 *     @type boolean $show_count   Optional. If set to `true` the amount of fields will be included in the title for
     715                 *                                 this section.
     716                 *     @type boolean $private      Optional. If set to `true` the section and all associated fields will be excluded
     717                 *                                 from the copy-paste text area.
     718                 *     @type array   $fields {
     719                 *         An associative array containing the data to be displayed.
     720                 *
     721                 *         @type string  $label    The label for this piece of information.
     722                 *         @type string  $value    The output that is of interest for this field.
     723                 *         @type boolean $private  Optional. If set to `true` the field will not be included in the copy-paste text area
     724                 *                                 on top of the page, allowing you to show, for example, API keys here.
     725                 *     }
     726                 * }
     727                 */
     728                $info = apply_filters( 'debug_information', $info );
     729
     730                if ( ! empty( $locale ) ) {
     731                        // Change the language used for translations
     732                        if ( function_exists( 'restore_previous_locale' ) && $switched_locale ) {
     733                                restore_previous_locale();
     734                        }
     735                }
     736
     737                return $info;
     738        }
     739
     740        /**
     741         * Print the formatted variation of the information gathered for debugging, in a manner
     742         * suitable for a text area that can be instantly copied to a forum or support ticket.
     743         *
     744         * @param array $info_array
     745         *
     746         * @return void
     747         */
     748        public static function textarea_format( $info_array ) {
     749                echo "`\n";
     750
     751                foreach ( $info_array as $section => $details ) {
     752                        // Skip this section if there are no fields, or the section has been declared as private.
     753                        if ( empty( $details['fields'] ) || ( isset( $details['private'] ) && $details['private'] ) ) {
     754                                continue;
     755                        }
     756
     757                        printf(
     758                                "### %s%s ###\n\n",
     759                                $details['label'],
     760                                ( isset( $details['show_count'] ) && $details['show_count'] ? sprintf( ' (%d)', count( $details['fields'] ) ) : '' )
     761                        );
     762
     763                        foreach ( $details['fields'] as $field ) {
     764                                if ( isset( $field['private'] ) && true === $field['private'] ) {
     765                                        continue;
     766                                }
     767
     768                                $values = $field['value'];
     769                                if ( is_array( $field['value'] ) ) {
     770                                        $values = '';
     771
     772                                        foreach ( $field['value'] as $name => $value ) {
     773                                                $values .= sprintf(
     774                                                        "\n\t%s: %s",
     775                                                        $name,
     776                                                        $value
     777                                                );
     778                                        }
     779                                }
     780
     781                                printf(
     782                                        "%s: %s\n",
     783                                        $field['label'],
     784                                        $values
     785                                );
     786                        }
     787                        echo "\n";
     788                }
     789                echo '`';
     790        }
     791
     792        /**
     793         * Return the size of a directory, including all subdirectories.
     794         *
     795         * @param string $path
     796         *
     797         * @return int
     798         */
     799        public static function get_directory_size( $path ) {
     800                $size = 0;
     801
     802                foreach ( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) ) as $file ) {
     803                        $size += $file->getSize();
     804                }
     805
     806                return $size;
     807        }
     808
     809        /**
     810         * Fetch the total size of all the database tables for the active database user.
     811         *
     812         * @return int
     813         */
     814        public static function get_database_size() {
     815                global $wpdb;
     816                $size = 0;
     817                $rows = $wpdb->get_results( 'SHOW TABLE STATUS', ARRAY_A );
     818
     819                if ( $wpdb->num_rows > 0 ) {
     820                        foreach ( $rows as $row ) {
     821                                $size += $row['Data_length'] + $row['Index_length'];
     822                        }
     823                }
     824
     825                return $size;
     826        }
     827}
  • src/wp-admin/includes/class-wp-site-health-auto-updates.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2/**
     3 * Class for testing automatic updates in the WordPress code.
     4 *
     5 * @package WordPress
     6 * @subpackage Site_Health
     7 * @since 5.2.0
     8 */
     9
     10// Make sure the file is not directly accessible.
     11if ( ! defined( 'ABSPATH' ) ) {
     12        die( 'We\'re sorry, but you can not directly access this file.' );
     13}
     14
     15/**
     16 * Class Site_Health_Auto_Updates
     17 */
     18class Site_Health_Auto_Updates {
     19        /**
     20         * Health_Check_Auto_Updates constructor.
     21         *
     22         * @uses Health_Check::init()
     23         *
     24         * @return void
     25         */
     26        public function __construct() {
     27                $this->init();
     28        }
     29
     30        /**
     31         * Initiate the plugin class.
     32         *
     33         * @return void
     34         */
     35        public function init() {
     36                include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
     37        }
     38
     39        /**
     40         * Run tests to determine if auto-updates can run.
     41         *
     42         * @uses get_class_methods()
     43         * @uses substr()
     44         * @uses call_user_func()
     45         *
     46         * @return array
     47         */
     48        public function run_tests() {
     49                $tests = array();
     50
     51                foreach ( get_class_methods( $this ) as $method ) {
     52                        if ( 'test_' !== substr( $method, 0, 5 ) ) {
     53                                continue;
     54                        }
     55
     56                        $result = call_user_func( array( $this, $method ) );
     57
     58                        if ( false === $result || null === $result ) {
     59                                continue;
     60                        }
     61
     62                        $result = (object) $result;
     63
     64                        if ( empty( $result->severity ) ) {
     65                                $result->severity = 'warning';
     66                        }
     67
     68                        $tests[ $method ] = $result;
     69                }
     70
     71                return $tests;
     72        }
     73
     74        /**
     75         * Test if file modifications are possible.
     76         *
     77         * @uses defined()
     78         * @uses sprintf()
     79         * @uses esc_html__()
     80         *
     81         * @return array
     82         */
     83        function test_constant_FILE_MODS() {
     84                if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
     85                        return array(
     86                                'desc'     => sprintf(
     87                                        /* translators: %s: Name of the constant used. */
     88                                        esc_html__( 'The %s constant is defined and enabled.' ),
     89                                        '<code>DISALLOW_FILE_MODS</code>'
     90                                ),
     91                                'severity' => 'fail',
     92                        );
     93                }
     94        }
     95
     96        /**
     97         * Check if automatic updates are disabled with a constant.
     98         *
     99         * @uses defined()
     100         * @uses sprintf()
     101         * @uses esc_html__()
     102         *
     103         * @return array
     104         */
     105        function test_constant_AUTOMATIC_UPDATER_DISABLED() {
     106                if ( defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED ) {
     107                        return array(
     108                                'desc'     => sprintf(
     109                                        /* translators: %s: Name of the constant used. */
     110                                        esc_html__( 'The %s constant is defined and enabled.' ),
     111                                        '<code>AUTOMATIC_UPDATER_DISABLED</code>'
     112                                ),
     113                                'severity' => 'fail',
     114                        );
     115                }
     116        }
     117
     118        /**
     119         * Check if automatic core updates are disabled with a constant.
     120         *
     121         * @uses defined()
     122         * @uses sprintf()
     123         * @uses esc_html__()
     124         *
     125         * @return array
     126         */
     127        function test_constant_WP_AUTO_UPDATE_CORE() {
     128                if ( defined( 'WP_AUTO_UPDATE_CORE' ) && false === WP_AUTO_UPDATE_CORE ) {
     129                        return array(
     130                                'desc'     => sprintf(
     131                                        /* translators: %s: Name of the constant used. */
     132                                        esc_html__( 'The %s constant is defined and enabled.' ),
     133                                        '<code>WP_AUTO_UPDATE_CORE</code>'
     134                                ),
     135                                'severity' => 'fail',
     136                        );
     137                }
     138        }
     139
     140        /**
     141         * Check if updates are intercepted by a filter.
     142         *
     143         * @uses has_filter()
     144         * @uses sprintf()
     145         * @uses esc_html__()
     146         *
     147         * @return array
     148         */
     149        function test_wp_version_check_attached() {
     150                $cookies = wp_unslash( $_COOKIE );
     151                $timeout = 10;
     152                $headers = array(
     153                        'Cache-Control' => 'no-cache',
     154                );
     155
     156                // Include Basic auth in loopback requests.
     157                if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) {
     158                        $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
     159                }
     160
     161                $url = add_query_arg( array(
     162                        'health-check-test-wp_version_check' => true,
     163                ), admin_url() );
     164
     165                $test = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) );
     166
     167                $response = wp_remote_retrieve_body( $test );
     168
     169                if ( 'yes' !== $response ) {
     170                        return array(
     171                                'desc'     => sprintf(
     172                                        /* translators: %s: Name of the filter used. */
     173                                        esc_html__( 'A plugin has prevented updates by disabling %s.' ),
     174                                        '<code>wp_version_check()</code>'
     175                                ),
     176                                'severity' => 'fail',
     177                        );
     178                }
     179        }
     180
     181        /**
     182         * Check if automatic updates are disabled by a filter.
     183         *
     184         * @uses apply_filters()
     185         * @uses sprintf()
     186         * @uses esc_html__()
     187         *
     188         * @return array
     189         */
     190        function test_filters_automatic_updater_disabled() {
     191                if ( apply_filters( 'automatic_updater_disabled', false ) ) {
     192                        return array(
     193                                'desc'     => sprintf(
     194                                        /* translators: %s: Name of the filter used. */
     195                                        esc_html__( 'The %s filter is enabled.' ),
     196                                        '<code>automatic_updater_disabled</code>'
     197                                ),
     198                                'severity' => 'fail',
     199                        );
     200                }
     201        }
     202
     203        /**
     204         * Check if automatic updates have tried to run, but failed, previously.
     205         *
     206         * @uses get_site_option()
     207         * @uses esc_html__()
     208         * @uses sprintf()
     209         *
     210         * @return array|bool
     211         */
     212        function test_if_failed_update() {
     213                $failed = get_site_option( 'auto_core_update_failed' );
     214
     215                if ( ! $failed ) {
     216                        return false;
     217                }
     218
     219                if ( ! empty( $failed['critical'] ) ) {
     220                        $desc  = esc_html__( 'A previous automatic background update ended with a critical failure, so updates are now disabled.' );
     221                        $desc .= ' ' . esc_html__( 'You would have received an email because of this.' );
     222                        $desc .= ' ' . esc_html__( "When you've been able to update using the \"Update Now\" button on Dashboard > Updates, we'll clear this error for future update attempts." );
     223                        $desc .= ' ' . sprintf(
     224                                /* translators: %s: Code of error shown. */
     225                                esc_html__( 'The error code was %s.' ),
     226                                '<code>' . $failed['error_code'] . '</code>'
     227                        );
     228                        return array(
     229                                'desc'     => $desc,
     230                                'severity' => 'warning',
     231                        );
     232                }
     233
     234                $desc = esc_html__( 'A previous automatic background update could not occur.' );
     235                if ( empty( $failed['retry'] ) ) {
     236                        $desc .= ' ' . esc_html__( 'You would have received an email because of this.' );
     237                }
     238
     239                $desc .= ' ' . esc_html__( "We'll try again with the next release." );
     240                $desc .= ' ' . sprintf(
     241                        /* translators: %s: Code of error shown. */
     242                        esc_html__( 'The error code was %s.' ),
     243                        '<code>' . $failed['error_code'] . '</code>'
     244                );
     245                return array(
     246                        'desc'     => $desc,
     247                        'severity' => 'warning',
     248                );
     249        }
     250
     251        /**
     252         * Check if WordPress is controlled by a VCS (Git, Subversion etc).
     253         *
     254         * @uses dirname()
     255         * @uses array_unique()
     256         * @uses is_dir()
     257         * @uses rtrim()
     258         * @uses apply_filters()
     259         * @uses sprintf()
     260         * @uses esc_html__()
     261         *
     262         * @param string $context The path to check from.
     263         *
     264         * @return array
     265         */
     266        function _test_is_vcs_checkout( $context ) {
     267                $context_dirs = array( ABSPATH );
     268                $vcs_dirs     = array( '.svn', '.git', '.hg', '.bzr' );
     269                $check_dirs   = array();
     270
     271                foreach ( $context_dirs as $context_dir ) {
     272                        // Walk up from $context_dir to the root.
     273                        do {
     274                                $check_dirs[] = $context_dir;
     275
     276                                // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here.
     277                                if ( dirname( $context_dir ) == $context_dir ) {
     278                                        break;
     279                                }
     280
     281                                // Continue one level at a time.
     282                        } while ( $context_dir = dirname( $context_dir ) );
     283                }
     284
     285                $check_dirs = array_unique( $check_dirs );
     286
     287                // Search all directories we've found for evidence of version control.
     288                foreach ( $vcs_dirs as $vcs_dir ) {
     289                        foreach ( $check_dirs as $check_dir ) {
     290                                // phpcs:ignore
     291                                if ( $checkout = @is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" ) ) {
     292                                        break 2;
     293                                }
     294                        }
     295                }
     296
     297                if ( $checkout && ! apply_filters( 'automatic_updates_is_vcs_checkout', true, $context ) ) {
     298                        return array(
     299                                'desc'     => sprintf(
     300                                        // translators: %1$s: Folder name. %2$s: Version control directory. %3$s: Filter name.
     301                                        esc_html__( 'The folder %1$s was detected as being under version control (%2$s), but the %3$s filter is allowing updates.' ),
     302                                        '<code>' . $check_dir . '</code>',
     303                                        "<code>$vcs_dir</code>",
     304                                        '<code>automatic_updates_is_vcs_checkout</code>'
     305                                ),
     306                                'severity' => 'info',
     307                        );
     308                }
     309
     310                if ( $checkout ) {
     311                        return array(
     312                                'desc'     => sprintf(
     313                                        // translators: %1$s: Folder name. %2$s: Version control directory.
     314                                        esc_html__( 'The folder %1$s was detected as being under version control (%2$s).' ),
     315                                        '<code>' . $check_dir . '</code>',
     316                                        "<code>$vcs_dir</code>"
     317                                ),
     318                                'severity' => 'fail',
     319                        );
     320                }
     321
     322                return array(
     323                        'desc'     => esc_html__( 'No version control systems were detected.' ),
     324                        'severity' => 'pass',
     325                );
     326        }
     327
     328        /**
     329         * Check if the absolute path is under Version Control.
     330         *
     331         * @uses Health_Check_Auto_Updates::_test_is_vcs_checkout()
     332         *
     333         * @return array
     334         */
     335        function test_vcs_ABSPATH() {
     336                $result = $this->_test_is_vcs_checkout( ABSPATH );
     337                return $result;
     338        }
     339
     340        /**
     341         * Check if we can access files without providing credentials.
     342         *
     343         * @uses Automatic_Upgrader_Skin
     344         * @uses Automatic_Upgrader_Skin::request_filesystem_credentials()
     345         * @uses esc_html__()
     346         *
     347         * @return array
     348         */
     349        function test_check_wp_filesystem_method() {
     350                $skin    = new Automatic_Upgrader_Skin;
     351                $success = $skin->request_filesystem_credentials( false, ABSPATH );
     352
     353                if ( ! $success ) {
     354                        $desc  = esc_html__( 'Your installation of WordPress prompts for FTP credentials to perform updates.' );
     355                        $desc .= ' ' . esc_html__( '(Your site is performing updates over FTP due to file ownership. Talk to your hosting company.)' );
     356
     357                        return array(
     358                                'desc'     => $desc,
     359                                'severity' => 'fail',
     360                        );
     361                }
     362
     363                return array(
     364                        'desc'     => esc_html__( "Your installation of WordPress doesn't require FTP credentials to perform updates." ),
     365                        'severity' => 'pass',
     366                );
     367        }
     368
     369        /**
     370         * Check if core files are writeable by the web user/group.
     371         *
     372         * @global $wp_filesystem
     373         *
     374         * @uses Automatic_Upgrader_Skin
     375         * @uses Automatic_Upgrader_Skin::request_filesystem_credentials()
     376         * @uses WP_Filesystem
     377         * @uses WP_Filesystem::method
     378         * @uses get_core_checksums()
     379         * @uses strpos()
     380         * @uses sprintf()
     381         * @uses esc_html__()
     382         * @uses array_keys()
     383         * @uses substr()
     384         * @uses file_exists()
     385         * @uses is_writable()
     386         * @uses count()
     387         * @uses array_slice()
     388         * @uses implode()
     389         *
     390         * @return array|bool
     391         */
     392        function test_all_files_writable() {
     393                global $wp_filesystem;
     394                include ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
     395
     396                $skin    = new Automatic_Upgrader_Skin;
     397                $success = $skin->request_filesystem_credentials( false, ABSPATH );
     398
     399                if ( ! $success ) {
     400                        return false;
     401                }
     402
     403                WP_Filesystem();
     404
     405                if ( 'direct' != $wp_filesystem->method ) {
     406                        return false;
     407                }
     408
     409                $checksums = get_core_checksums( $wp_version, 'en_US' );
     410                $dev       = ( false !== strpos( $wp_version, '-' ) );
     411                // Get the last stable version's files and test against that
     412                if ( ! $checksums && $dev ) {
     413                        $checksums = get_core_checksums( (float) $wp_version - 0.1, 'en_US' );
     414                }
     415
     416                // There aren't always checksums for development releases, so just skip the test if we still can't find any
     417                if ( ! $checksums && $dev ) {
     418                        return false;
     419                }
     420
     421                if ( ! $checksums ) {
     422                        $desc = sprintf(
     423                                // translators: %s: WordPress version
     424                                esc_html__( "Couldn't retrieve a list of the checksums for WordPress %s." ),
     425                                $wp_version
     426                        );
     427                        $desc .= ' ' . esc_html__( 'This could mean that connections are failing to WordPress.org.' );
     428                        return array(
     429                                'desc'     => $desc,
     430                                'severity' => 'warning',
     431                        );
     432                }
     433
     434                $unwritable_files = array();
     435                foreach ( array_keys( $checksums ) as $file ) {
     436                        if ( 'wp-content' == substr( $file, 0, 10 ) ) {
     437                                continue;
     438                        }
     439                        if ( ! file_exists( ABSPATH . '/' . $file ) ) {
     440                                continue;
     441                        }
     442                        if ( ! is_writable( ABSPATH . '/' . $file ) ) {
     443                                $unwritable_files[] = $file;
     444                        }
     445                }
     446
     447                if ( $unwritable_files ) {
     448                        if ( count( $unwritable_files ) > 20 ) {
     449                                $unwritable_files   = array_slice( $unwritable_files, 0, 20 );
     450                                $unwritable_files[] = '...';
     451                        }
     452                        return array(
     453                                'desc'     => esc_html__( 'Some files are not writable by WordPress:' ) . ' <ul><li>' . implode( '</li><li>', $unwritable_files ) . '</li></ul>',
     454                                'severity' => 'fail',
     455                        );
     456                } else {
     457                        return array(
     458                                'desc'     => esc_html__( 'All of your WordPress files are writable.' ),
     459                                'severity' => 'pass',
     460                        );
     461                }
     462        }
     463
     464        /**
     465         * Check if the install is using a development branch and can use nightly packages.
     466         *
     467         * @uses strpos()
     468         * @uses defined()
     469         * @uses sprintf()
     470         * @uses esc_html__()
     471         * @uses apply_filters()
     472         *
     473         * @return array|bool
     474         */
     475        function test_accepts_dev_updates() {
     476                include ABSPATH . WPINC . '/version.php'; // $wp_version; // x.y.z
     477                // Only for dev versions
     478                if ( false === strpos( $wp_version, '-' ) ) {
     479                        return false;
     480                }
     481
     482                if ( defined( 'WP_AUTO_UPDATE_CORE' ) && ( 'minor' === WP_AUTO_UPDATE_CORE || false === WP_AUTO_UPDATE_CORE ) ) {
     483                        return array(
     484                                'desc'     => sprintf(
     485                                        /* translators: %s: Name of the constant used. */
     486                                        esc_html__( 'WordPress development updates are blocked by the %s constant.' ),
     487                                        '<code>WP_AUTO_UPDATE_CORE</code>'
     488                                ),
     489                                'severity' => 'fail',
     490                        );
     491                }
     492
     493                if ( ! apply_filters( 'allow_dev_auto_core_updates', $wp_version ) ) {
     494                        return array(
     495                                'desc'     => sprintf(
     496                                        /* translators: %s: Name of the filter used. */
     497                                        esc_html__( 'WordPress development updates are blocked by the %s filter.' ),
     498                                        '<code>allow_dev_auto_core_updates</code>'
     499                                ),
     500                                'severity' => 'fail',
     501                        );
     502                }
     503        }
     504
     505        /**
     506         * Check if the site supports automatic minor updates.
     507         *
     508         * @uses defined()
     509         * @uses sprintf()
     510         * @uses esc_html__()
     511         * @uses apply_filters()
     512         *
     513         * @return array
     514         */
     515        function test_accepts_minor_updates() {
     516                if ( defined( 'WP_AUTO_UPDATE_CORE' ) && false === WP_AUTO_UPDATE_CORE ) {
     517                        return array(
     518                                'desc'     => sprintf(
     519                                        /* translators: %s: Name of the constant used. */
     520                                        esc_html__( 'WordPress security and maintenance releases are blocked by %s.' ),
     521                                        "<code>define( 'WP_AUTO_UPDATE_CORE', false );</code>"
     522                                ),
     523                                'severity' => 'fail',
     524                        );
     525                }
     526
     527                if ( ! apply_filters( 'allow_minor_auto_core_updates', true ) ) {
     528                        return array(
     529                                'desc'     => sprintf(
     530                                        /* translators: %s: Name of the filter used. */
     531                                        esc_html__( 'WordPress security and maintenance releases are blocked by the %s filter.' ),
     532                                        '<code>allow_minor_auto_core_updates</code>'
     533                                ),
     534                                'severity' => 'fail',
     535                        );
     536                }
     537        }
     538}
  • src/wp-admin/includes/class-wp-site-health.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2/**
     3 * Class for looking up a sites health based on a users WordPress environment.
     4 *
     5 * @package WordPress
     6 * @subpackage Site_Health
     7 * @since 5.2.0
     8 */
     9
     10// Make sure the file is not directly accessible.
     11if ( ! defined( 'ABSPATH' ) ) {
     12        die( 'We\'re sorry, but you can not directly access this file.' );
     13}
     14
     15class WP_Site_Health {
     16        private $php_min_version_check = '5.2.4';
     17        private $php_supported_version_check = '5.6';
     18        private $php_rec_version_check = '7.3';
     19
     20        private $mysql_min_version_check;
     21        private $mysql_rec_version_check;
     22
     23        public  $mariadb                        = false;
     24        private $mysql_server_version           = null;
     25        private $health_check_mysql_rec_version = null;
     26
     27        public $schedules;
     28        public $crons;
     29        public $last_missed_cron = null;
     30
     31        public function __construct() {
     32                $this->init();
     33        }
     34
     35        public function init() {
     36                $this->prepare_sql_data();
     37
     38                add_action( 'wp_ajax_health-check-site-status', array( $this, 'site_status' ) );
     39
     40                add_action( 'wp_ajax_health-check-site-status-result', array( $this, 'site_status_result' ) );
     41
     42                add_action( 'wp_loaded', array( $this, 'check_wp_version_check_exists' ) );
     43
     44                add_action( 'admin_enqueue_scripts', array( $this, 'enqueues' ) );
     45        }
     46
     47        public function enqueues() {
     48                $screen = get_current_screen();
     49                if ( 'site-health' !== $screen->id ) {
     50                        return;
     51                }
     52
     53                $health_check_js_variables = array(
     54                        'screen' => $screen->id,
     55                        'string'      => array(
     56                                'please_wait'                        => esc_html__( 'Please wait...' ),
     57                                'copied'                             => esc_html__( 'Copied' ),
     58                                'running_tests'                      => esc_html__( 'Currently being tested...' ),
     59                                'site_health_complete'               => esc_html__( 'All site health tests have finished running.' ),
     60                                'site_info_show_copy'                => esc_html__( 'Show options for copying this information' ),
     61                                'site_info_hide_copy'                => esc_html__( 'Hide options for copying this information' ),
     62                                // translators: %s: The percentage score for the tests.
     63                                'site_health_complete_screen_reader' => esc_html__( 'All site health tests have finished running. Your site scored %s, and the results are now available on the page.' ),
     64                                'site_info_copied'                   => esc_html__( 'Site information has been added to your clipboard.' ),
     65                        ),
     66                        'nonce'       => array(
     67                                'site_status'                 => wp_create_nonce( 'health-check-site-status' ),
     68                                'site_status_result'          => wp_create_nonce( 'health-check-site-status-result' ),
     69                        ),
     70                        'site_status' => array(
     71                                'direct' => array(),
     72                                'async'  => array(),
     73                                'issues' => array(
     74                                        'good'        => 0,
     75                                        'recommended' => 0,
     76                                        'critical'    => 0,
     77                                ),
     78                        ),
     79                );
     80
     81                $issue_counts = get_transient( 'health-check-site-status-result' );
     82
     83                if ( false !== $issue_counts ) {
     84                        $issue_counts = json_decode( $issue_counts );
     85
     86                        $health_check_js_variables['site_status']['issues'] = $issue_counts;
     87                }
     88
     89                if ( 'site-health' === $screen->id && ! isset( $_GET['tab'] ) ) {
     90                        $tests = WP_Site_Health::get_tests();
     91                        foreach ( $tests['direct'] as $test ) {
     92                                $test_function = sprintf(
     93                                        'get_test_%s',
     94                                        $test['test']
     95                                );
     96
     97                                if ( method_exists( $this, $test_function ) && is_callable( array( $this, $test_function ) ) ) {
     98                                        $health_check_js_variables['site_status']['direct'][] = call_user_func( array( $this, $test_function ) );
     99                                } else {
     100                                        $health_check_js_variables['site_status']['direct'][] = call_user_func( $test['test'] );
     101                                }
     102                        }
     103
     104                        foreach ( $tests['async'] as $test ) {
     105                                $health_check_js_variables['site_status']['async'][] = array(
     106                                        'test'      => $test['test'],
     107                                        'completed' => false,
     108                                );
     109                        }
     110                }
     111
     112                wp_localize_script( 'site-health', 'SiteHealth', $health_check_js_variables );
     113        }
     114
     115        private function prepare_sql_data() {
     116                global $wpdb;
     117
     118                if ( method_exists( $wpdb, 'db_version' ) ) {
     119                        if ( $wpdb->use_mysqli ) {
     120                                // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_get_server_info
     121                                $mysql_server_type = mysqli_get_server_info( $wpdb->dbh );
     122                        } else {
     123                                // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysql_get_server_info
     124                                $mysql_server_type = mysql_get_server_info( $wpdb->dbh );
     125                        }
     126
     127                        $this->mysql_server_version = $wpdb->get_var( 'SELECT VERSION()' );
     128                }
     129
     130                $this->health_check_mysql_rec_version = '5.6';
     131
     132                if ( stristr( $mysql_server_type, 'mariadb' ) ) {
     133                        $this->mariadb                        = true;
     134                        $this->health_check_mysql_rec_version = '10.0';
     135                }
     136
     137                $this->mysql_min_version_check = version_compare( '5.0', $this->mysql_server_version, '<=' );
     138                $this->mysql_rec_version_check = version_compare( $this->health_check_mysql_rec_version, $this->mysql_server_version, '<=' );
     139        }
     140
     141        public function check_wp_version_check_exists() {
     142                if ( ! is_admin() || ! is_user_logged_in() || ! current_user_can( 'manage_options' ) || ! isset( $_GET['health-check-test-wp_version_check'] ) ) {
     143                        return;
     144                }
     145
     146                echo ( has_filter( 'wp_version_check', 'wp_version_check' ) ? 'yes' : 'no' );
     147
     148                die();
     149        }
     150
     151        public function site_status_result() {
     152                check_ajax_referer( 'health-check-site-status-result' );
     153
     154                if ( ! current_user_can( 'manage_options' ) ) {
     155                        wp_send_json_error();
     156                }
     157
     158                set_transient( 'health-check-site-status-result', wp_json_encode( $_POST['counts'] ) );
     159        }
     160
     161        public function site_status() {
     162                check_ajax_referer( 'health-check-site-status' );
     163
     164                if ( ! current_user_can( 'manage_options' ) ) {
     165                        wp_send_json_error();
     166                }
     167
     168                $function = sprintf(
     169                        'json_test_%s',
     170                        $_POST['feature']
     171                );
     172
     173                if ( ! method_exists( $this, $function ) || ! is_callable( array( $this, $function ) ) ) {
     174                        return;
     175                }
     176
     177                $call = call_user_func( array( $this, $function ) );
     178        }
     179
     180        /**
     181         * Tests for WordPress version and outputs it.
     182         *
     183         * @return array
     184         */
     185        public function get_test_wordpress_version() {
     186                $result = array(
     187                        'label'       => '',
     188                        'status'      => '',
     189                        'badge'       => array(
     190                                'label' => 'Security',
     191                                'color' => 'red',
     192                        ),
     193                        'description' => '',
     194                        'actions'     => '',
     195                        'test'        => 'wordpress_version',
     196                );
     197
     198                $core_current_version = get_bloginfo( 'version' );
     199                $core_updates         = get_core_updates();
     200
     201                if ( ! is_array( $core_updates ) ) {
     202                        $result['status'] = 'recommended';
     203
     204                        $result['label'] = sprintf(
     205                                // translators: %s: Your current version of WordPress.
     206                                esc_html__( 'WordPress version %s' ),
     207                                $core_current_version
     208                        );
     209
     210                        $result['description'] = sprintf(
     211                                '<p>%s</p>',
     212                                esc_html__( 'We were unable to check if any new versions of WordPress are available.' )
     213                        );
     214
     215                        $result['actions'] = sprintf(
     216                                '<a href="%s">%s</a>',
     217                                esc_url( admin_url( 'update-core.php?force-check=1' ) ),
     218                                esc_html__( 'Check for updates manually' )
     219                        );
     220                } else {
     221                        foreach ( $core_updates as $core => $update ) {
     222                                if ( 'upgrade' === $update->response ) {
     223                                        $current_version = explode( '.', $core_current_version );
     224                                        $new_version     = explode( '.', $update->version );
     225
     226                                        $current_major = $current_version[0] . '.' . $current_version[1];
     227                                        $new_major     = $new_version[0] . '.' . $new_version[1];
     228
     229                                        $result['label'] = sprintf(
     230                                                // translators: %s: The latest version of WordPress available.
     231                                                esc_html__( 'WordPress update available (%s)' ),
     232                                                $update->version
     233                                        );
     234
     235                                        $result['actions'] = sprintf(
     236                                                '<a href="%s">%s</a>',
     237                                                esc_url( admin_url( 'update-core.php' ) ),
     238                                                esc_html__( 'Install the latest version of WordPress' )
     239                                        );
     240
     241                                        if ( $current_major !== $new_major ) {
     242                                                // This is a major version mismatch.
     243                                                $result['status']      = 'recommended';
     244                                                $result['description'] = sprintf(
     245                                                        '<p>%s</p>',
     246                                                        esc_html__( 'A new version of WordPress is available.' )
     247                                                );
     248                                        } else {
     249                                                // This is a minor version, sometimes considered more critical.
     250                                                $result['status']      = 'critical';
     251                                                $result['description'] = sprintf(
     252                                                        '<p>%s</p>',
     253                                                        esc_html__( 'A new minor update is available for your site. Because minor updates often address security, it\'s important to install them.' )
     254                                                );
     255                                        }
     256                                } else {
     257                                        $result['status'] = 'good';
     258                                        $result['label']  = sprintf(
     259                                                // translators: %s: The current version of WordPress installed on this site.
     260                                                esc_html__( 'Your WordPress version is up to date (%s)' ),
     261                                                $core_current_version
     262                                        );
     263
     264                                        $result['description'] = sprintf(
     265                                                '<p>%s</p>',
     266                                                esc_html__( 'You are currently running the latest version of WordPress available, keep it up!' )
     267                                        );
     268                                }
     269                        }
     270                }
     271
     272                return $result;
     273        }
     274
     275        public function test_wordpress_version() {
     276                $check = $this->get_test_wordpress_version();
     277
     278                printf( '<span class="%s"></span> %s', esc_attr( $check['status'] ), esc_html( $check['label'] ) );
     279        }
     280
     281        public function json_wordpress_version() {
     282                wp_send_json_success( $this->get_test_wordpress_version() );
     283        }
     284
     285        public function get_test_plugin_version() {
     286                $result = array(
     287                        'label'       => esc_html__( 'Your plugins are up to date' ),
     288                        'status'      => 'good',
     289                        'badge'       => array(
     290                                'label' => 'Security',
     291                                'color' => 'red',
     292                        ),
     293                        'description' => sprintf(
     294                                '<p>%s</p>',
     295                                esc_html__( 'Plugins extend your site\'s functionality with things like contact forms, ecommerce and much more. That means they have deep access to your site, so it\'s vital to keep them up to date.' )
     296                        ),
     297                        'actions'     => '',
     298                        'test'        => 'plugin_version',
     299                );
     300
     301                $plugins        = get_plugins();
     302                $plugin_updates = get_plugin_updates();
     303
     304                $show_unused_plugins  = true;
     305                $plugins_have_updates = false;
     306                $plugins_active       = 0;
     307                $plugins_total        = 0;
     308                $plugins_needs_update = 0;
     309
     310                foreach ( $plugins as $plugin_path => $plugin ) {
     311                        $plugins_total++;
     312
     313                        if ( is_plugin_active( $plugin_path ) ) {
     314                                $plugins_active++;
     315                        }
     316
     317                        $plugin_version = $plugin['Version'];
     318
     319                        if ( array_key_exists( $plugin_path, $plugin_updates ) ) {
     320                                $plugins_needs_update++;
     321                                $plugins_have_updates = true;
     322                        }
     323                }
     324
     325                if ( $plugins_needs_update > 0 ) {
     326                        $result['status'] = 'critical';
     327
     328                        $result['label'] = esc_html__( 'You have plugins waiting to be updated' );
     329
     330                        $result['description'] .= sprintf(
     331                                '<p>%s</p>',
     332                                sprintf(
     333                                        // translators: %d: The amount of outdated plugins.
     334                                        esc_html( _n(
     335                                                'Your site has %d plugin waiting to be updated.',
     336                                                'Your site has %d plugins waiting for updates.',
     337                                                $plugins_needs_update
     338                                        ) ),
     339                                        $plugins_needs_update
     340                                )
     341                        );
     342                } else {
     343                        $result['description'] .= sprintf(
     344                                '<p>%s</p>',
     345                                sprintf(
     346                                        // translators: %d: The amount of plugins.
     347                                        esc_html( _n(
     348                                                'Your site has %d active plugin, and it is up to date.',
     349                                                'Your site has %d active plugins, and they are all up to date.',
     350                                                $plugins_active
     351                                        ) ),
     352                                        $plugins_active
     353                                )
     354                        );
     355                }
     356
     357                if ( ( $plugins_total > $plugins_active ) && $show_unused_plugins ) {
     358                        $unused_plugins = $plugins_total - $plugins_active;
     359
     360                        $result['status'] = 'recommended';
     361
     362                        $result['label'] = esc_html__( 'Inactive plugins should be removed' );
     363
     364                        $result['description'] .= sprintf(
     365                                '<p>%s</p>',
     366                                sprintf(
     367                                        // translators: %d: The amount of inactive plugins.
     368                                        esc_html( _n(
     369                                                'Your site has %d inactive plugin. Inactive plugins are tempting targets for attackers. if you\'re not going to use a plugin, we recommend you remove it.',
     370                                                'Your site has %d inactive plugins. Inactive plugins are tempting targets for attackers. if you\'re not going to use a plugin, we recommend you remove it.',
     371                                                $unused_plugins
     372                                        ) ),
     373                                        $unused_plugins
     374                                )
     375                        );
     376                }
     377
     378                return $result;
     379        }
     380
     381        public function get_test_theme_version() {
     382                $result = array(
     383                        'label'       => esc_html__( 'Your themes are up to date' ),
     384                        'status'      => 'good',
     385                        'badge'       => array(
     386                                'label' => 'Security',
     387                                'color' => 'red',
     388                        ),
     389                        'description' => sprintf(
     390                                '<p>%s</p>',
     391                                esc_html__( 'Themes add your site\'s look and feel. It\'s important to keep them up to date -- to stay consistent with your brand and keep your site secure.' )
     392                        ),
     393                        'actions'     => '',
     394                        'test'        => 'theme_version',
     395                );
     396
     397                $theme_updates = get_theme_updates();
     398
     399                $themes_total        = 0;
     400                $themes_need_updates = 0;
     401                $themes_inactive     = 0;
     402
     403                // This value is changed during processing to determine how many themes are considered a reasonable amount.
     404                $allowed_theme_count = 1;
     405
     406                $has_default_theme   = false;
     407                $has_unused_themes   = false;
     408                $show_unused_themes  = true;
     409                $using_default_theme = false;
     410
     411                // Populate a list of all themes available in the install.
     412                $all_themes   = wp_get_themes();
     413                $active_theme = wp_get_theme();
     414
     415                foreach ( $all_themes as $theme_slug => $theme ) {
     416                        $themes_total++;
     417
     418                        if ( WP_DEFAULT_THEME === $theme_slug ) {
     419                                $has_default_theme = true;
     420
     421                                if ( $theme_slug === get_stylesheet() ) {
     422                                        $using_default_theme = true;
     423                                }
     424                        }
     425
     426                        if ( array_key_exists( $theme_slug, $theme_updates ) ) {
     427                                $themes_need_updates++;
     428                        }
     429                }
     430
     431                // If this is a child theme, increase the allowed theme count by one, to account for the parent.
     432                if ( $active_theme->parent() ) {
     433                        $allowed_theme_count++;
     434                }
     435
     436                // If there's a default theme installed, we count that as allowed as well.
     437                if ( $has_default_theme ) {
     438                        $allowed_theme_count++;
     439                }
     440
     441                if ( $themes_total > $allowed_theme_count ) {
     442                        $has_unused_themes = true;
     443                        $themes_inactive   = ( $themes_total - $allowed_theme_count );
     444                }
     445
     446                if ( $themes_need_updates > 0 ) {
     447                        $result['status'] = 'critical';
     448
     449                        $result['label'] = esc_html__( 'You have themes waiting to be updated' );
     450
     451                        $result['description'] .= sprintf(
     452                                '<p>%s</p>',
     453                                sprintf(
     454                                        // translators: %d: The amount of outdated themes.
     455                                        esc_html( _n(
     456                                                'Your site has %d theme waiting to be updated.',
     457                                                'Your site has %d themes waiting to be updated.',
     458                                                $themes_need_updates
     459                                        ) ),
     460                                        $themes_need_updates
     461                                )
     462                        );
     463                } else {
     464                        $result['description'] .= sprintf(
     465                                '<p>%s</p>',
     466                                sprintf(
     467                                        // translators: %d: The amount of themes.
     468                                        esc_html( _n(
     469                                                'Your site has %d installed theme, and it is up to date.',
     470                                                'Your site has %d installed themes, and they are all up to date.',
     471                                                $themes_total
     472                                        ) ),
     473                                        $themes_total
     474                                )
     475                        );
     476                }
     477
     478                if ( $has_unused_themes && $show_unused_themes ) {
     479
     480                        // This is a child theme, so we want to be a bit more explicit in our messages.
     481                        if ( $active_theme->parent() ) {
     482                                $result['status'] = 'recommended';
     483
     484                                $result['label'] = esc_html__( 'You should remove inactive themes' );
     485
     486                                if ( $using_default_theme ) {
     487                                        $result['description'] .= sprintf(
     488                                                '<p>%s</p>',
     489                                                sprintf(
     490                                                        // translators: %1$d: The amount of inactive themes. %2$s: The currently active theme. %3$s: The active themes parent theme.
     491                                                        esc_html( _n(
     492                                                                'Your site has %1$d inactive theme. To enhance your site’s security, we recommend you remove any themes you\'re not using. You should keep your current theme, %2$s, and %3$s, its parent theme.',
     493                                                                'Your site has %1$d inactive themes. To enhance your site’s security, we recommend you remove any themes you\'re not using. You should keep your current theme, %2$s, and %3$s, its parent theme.',
     494                                                                $themes_inactive
     495                                                        ) ),
     496                                                        $themes_inactive,
     497                                                        $active_theme->name,
     498                                                        $active_theme->parent()->name
     499                                                )
     500                                        );
     501                                } else {
     502                                        $result['description'] .= sprintf(
     503                                                '<p>%s</p>',
     504                                                sprintf(
     505                                                        // translators: %1$d: The amount of inactive themes. %2$s: The default theme for WordPress. %3$s: The currently active theme. %4$s: The active themes parent theme.
     506                                                        esc_html( _n(
     507                                                                'Your site has %1$d inactive theme. To enhance your site’s security, we recommend you remove any themes you\'re not using. You should keep %2$s, the default WordPress theme, %3$s, your current theme and %4$s, its parent theme.',
     508                                                                'Your site has %1$d inactive themes. To enhance your site’s security, we recommend you remove any themes you\'re not using. You should keep %2$s, the default WordPress theme, %3$s, your current theme and %4$s, its parent theme.',
     509                                                                $themes_inactive
     510                                                        ) ),
     511                                                        $themes_inactive,
     512                                                        WP_DEFAULT_THEME,
     513                                                        $active_theme->name,
     514                                                        $active_theme->parent()->name
     515                                                )
     516                                        );
     517                                }
     518
     519                        } else {
     520                                $result['status'] = 'recommended';
     521
     522                                $result['label'] = esc_html__( 'You should remove inactive themes' );
     523
     524                                if ( $using_default_theme ) {
     525                                        $result['description'] .= sprintf(
     526                                                '<p>%s</p>',
     527                                                sprintf(
     528                                                        // translators: %1$d: The amount of inactive themes. %2$s: The currently active theme.
     529                                                        esc_html( _n(
     530                                                                'Your site has %1$d inactive theme, other than %2$s, your active theme. We recommend removing any unused themes to enhance your sites security.',
     531                                                                'Your site has %1$d inactive themes, other than %2$s, your active theme. We recommend removing any unused themes to enhance your sites security.',
     532                                                                $themes_inactive
     533                                                        ) ),
     534                                                        $themes_inactive,
     535                                                        $active_theme->name
     536                                                )
     537                                        );
     538                                } else {
     539                                        $result['description'] .= sprintf(
     540                                                '<p>%s</p>',
     541                                                sprintf(
     542                                                        // translators: %1$d: The amount of inactive themes. %2$s: The default theme for WordPress. %3$s: The currently active theme.
     543                                                        esc_html( _n(
     544                                                                'Your site has %1$d inactive theme, other than %2$s, the default WordPress theme, and %3$s, your active theme. We recommend removing any unused themes to enhance your sites security.',
     545                                                                'Your site has %1$d inactive themes, other than %2$s, the default WordPress theme, and %3$s, your active theme. We recommend removing any unused themes to enhance your sites security.',
     546                                                                $themes_inactive
     547                                                        ) ),
     548                                                        $themes_inactive,
     549                                                        WP_DEFAULT_THEME,
     550                                                        $active_theme->name
     551                                                )
     552                                        );
     553                                }
     554                        }
     555                }
     556
     557                if ( ! $has_default_theme ) {
     558                        $result['status'] = 'recommended';
     559
     560                        $result['label'] = esc_html__( 'Have a default theme available' );
     561
     562                        $result['description'] .= sprintf(
     563                                '<p>%s</p>',
     564                                esc_html__( 'Your site does not have any default theme. Default themes are used by WordPress automatically if anything is wrong with your normal theme.' )
     565                        );
     566                }
     567
     568                return $result;
     569        }
     570
     571        public function get_test_php_version() {
     572                $result = array(
     573                        'label'       => sprintf(
     574                                // translators: %s: The current PHP version.
     575                                esc_html__( 'PHP is up to date (%s)' ),
     576                                PHP_VERSION
     577                        ),
     578                        'status'      => 'good',
     579                        'badge'       => array(
     580                                'label' => 'Security',
     581                                'color' => 'red',
     582                        ),
     583                        'description' => sprintf(
     584                                '<p>%s</p>',
     585                                esc_html__( 'PHP is the language your web server runs. WordPress uses it to get content from the database and build your site\'s pages in real time.' )
     586                        ),
     587                        'actions'     => '',
     588                        'test'        => 'php_version',
     589                );
     590
     591                if ( ! $this->php_min_version_check ) {
     592                        $result['status'] = 'critical';
     593
     594                        $result['label'] = esc_html__( 'Your PHP version requires an update' );
     595
     596                        $result['actions'] = sprintf(
     597                                '<a href="%s">%s</a>',
     598                                esc_url(
     599                                        _x( 'https://wordpress.org/support/upgrade-php/', 'The link to the Update PHP page, which may be localized.' )
     600                                ),
     601                                esc_html__( 'Learn more about why you should update PHP' )
     602                        );
     603
     604                        $result['description'] .= sprintf(
     605                                '<p>%s</p>',
     606                                sprintf(
     607                                        // translators: %1$s: Current PHP version. %2$s: Recommended PHP version. %3$s: Minimum PHP version.
     608                                        esc_html__( 'Your version of PHP, %1$s, is very outdated and no longer getting security updates, exposing your site to attack. Please contact your host and get an upgrade to %2$s, which is the version WordPress recommends. If that\'s not possible, your site will run with version %3$s or newer.' ),
     609                                        PHP_VERSION,
     610                                        HEALTH_CHECK_PHP_REC_VERSION,
     611                                        HEALTH_CHECK_PHP_MIN_VERSION
     612                                )
     613                        );
     614                } elseif ( ! $this->php_supported_version_check ) {
     615                        $result['status'] = 'recommended';
     616
     617                        $result['label'] = esc_html__( 'Your PHP version should be updated' );
     618
     619                        $result['actions'] = sprintf(
     620                                '<a href="%s">%s</a>',
     621                                esc_url(
     622                                        _x( 'https://wordpress.org/support/upgrade-php/', 'The link to the Update PHP page, which may be localized.' )
     623                                ),
     624                                esc_html__( 'Learn more about why you should update PHP' )
     625                        );
     626
     627                        $result['description'] .= sprintf(
     628                                '<p>%s</p>',
     629                                sprintf(
     630                                        // translators: %1$s: Current PHP version. %2$s: Recommended PHP version.
     631                                        esc_html__( 'Your version of PHP, %1$s, is very outdated and no longer receiving security updates. Please contact your host for an upgrade. WordPress recommends PHP version %2$s.' ),
     632                                        PHP_VERSION,
     633                                        HEALTH_CHECK_PHP_REC_VERSION
     634                                )
     635                        );
     636                } elseif ( ! $this->php_rec_version_check ) {
     637                        $result['status'] = 'recommended';
     638
     639                        $result['label'] = esc_html__( 'We recommend that you update PHP' );
     640
     641                        $result['actions'] = sprintf(
     642                                '<a href="%s">%s</a>',
     643                                esc_url(
     644                                        _x( 'https://wordpress.org/support/upgrade-php/', 'The link to the Update PHP page, which may be localized.' )
     645                                ),
     646                                esc_html__( 'Learn more about why you should update PHP' )
     647                        );
     648
     649                        $result['description'] .= sprintf(
     650                                '<p>%s</p>',
     651                                sprintf(
     652                                        // translators: %s: Recommended PHP version
     653                                        esc_html__( 'For best performance we recommend using PHP %s or higher.' ),
     654                                        HEALTH_CHECK_PHP_REC_VERSION
     655                                )
     656                        );
     657                }
     658
     659                return $result;
     660        }
     661
     662        public function child_test_php_extension_availability( $extension = null, $function = null ) {
     663                // If no extension or function is passed, claim to fail testing, as we have nothing to test against.
     664                if ( null === $extension && null === $function ) {
     665                        return false;
     666                }
     667
     668                $available = true;
     669
     670                if ( null !== $extension && ! extension_loaded( $extension ) ) {
     671                        $available = false;
     672                }
     673                if ( null !== $function && ! function_exists( $function ) ) {
     674                        $available = false;
     675                }
     676
     677                return $available;
     678        }
     679
     680        public function get_test_php_extensions() {
     681                $result = array(
     682                        'label'       => esc_html__( 'Required and recommended modules are installed' ),
     683                        'status'      => 'good',
     684                        'badge'       => array(
     685                                'label' => 'Performance',
     686                                'color' => 'orange',
     687                        ),
     688                        'description' => sprintf(
     689                                '<p>%s</p><p>%s</p>',
     690                                esc_html__( 'PHP modules perform most of the tasks on the server that make your site run.' ),
     691                                sprintf(
     692                                        // translators: %s: Link to the hosting group page about recommended PHP modules.
     693                                        esc_html__( 'The Hosting team maintains a list of those modules, both recommended and required, in %s.' ),
     694                                        sprintf(
     695                                                '<a href="https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions">%s</a>',
     696                                                esc_html__( 'the team handbook' )
     697                                        )
     698                                )
     699                        ),
     700                        'actions'     => '',
     701                        'test'        => 'php_extensions',
     702                );
     703
     704                /*
     705                 * An array representing all the modules we wish to test for.
     706                 *
     707                 * array $modules {
     708                 *     An associated array of modules to test for.
     709                 *
     710                 *     array $module {
     711                 *         An associated array of module properties used during testing.
     712                 *         One of either `$function` or `$extension` must be provided, or they will fail by default.
     713                 *
     714                 *         string $function     Optional. A function name to test for the existence of.
     715                 *         string $extension    Optional. An extension to check if is loaded in PHP.
     716                 *         bool   $required     Is this a required feature or not.
     717                 *         string $fallback_for Optional. The module this module replaces as a fallback.
     718                 *     }
     719                 * }
     720                 */
     721                $modules = array(
     722                        'bcmath'    => array(
     723                                'function' => 'bcadd',
     724                                'required' => false,
     725                        ),
     726                        'curl'      => array(
     727                                'function' => 'curl_version',
     728                                'required' => false,
     729                        ),
     730                        'exif'      => array(
     731                                'function' => 'exif_read_data',
     732                                'required' => false,
     733                        ),
     734                        'filter'    => array(
     735                                'function' => 'filter_list',
     736                                'required' => false,
     737                        ),
     738                        'fileinfo'  => array(
     739                                'function' => 'finfo_file',
     740                                'required' => false,
     741                        ),
     742                        'mod_xml'   => array(
     743                                'extension' => 'libxml',
     744                                'required'  => false,
     745                        ),
     746                        'mysqli'    => array(
     747                                'function' => 'mysqli_connect',
     748                                'required' => false,
     749                        ),
     750                        'libsodium' => array(
     751                                'function'            => 'sodium_compare',
     752                                'required'            => false,
     753                                'php_bundled_version' => '7.2.0',
     754                        ),
     755                        'openssl'   => array(
     756                                'function' => 'openssl_encrypt',
     757                                'required' => false,
     758                        ),
     759                        'pcre'      => array(
     760                                'function' => 'preg_match',
     761                                'required' => false,
     762                        ),
     763                        'imagick'   => array(
     764                                'extension' => 'imagick',
     765                                'required'  => false,
     766                        ),
     767                        'gd'        => array(
     768                                'extension'    => 'gd',
     769                                'required'     => false,
     770                                'fallback_for' => 'imagick',
     771                        ),
     772                        'mcrypt'    => array(
     773                                'extension'    => 'mcrypt',
     774                                'required'     => false,
     775                                'fallback_for' => 'libsodium',
     776                        ),
     777                        'xmlreader' => array(
     778                                'extension'    => 'xmlreader',
     779                                'required'     => false,
     780                                'fallback_for' => 'xml',
     781                        ),
     782                        'zlib'      => array(
     783                                'extension'    => 'zlib',
     784                                'required'     => false,
     785                                'fallback_for' => 'zip',
     786                        ),
     787                );
     788
     789                $failures = array();
     790
     791                foreach ( $modules as $library => $module ) {
     792                        $extension = ( isset( $module['extension'] ) ? $module['extension'] : null );
     793                        $function  = ( isset( $module['function'] ) ? $module['function'] : null );
     794
     795                        // If this module is a fallback for another function, check if that other function passed.
     796                        if ( isset( $module['fallback_for'] ) ) {
     797                                /*
     798                                 * If that other function has a failure, mark this module as required for normal operations.
     799                                 * If that other function hasn't failed, skip this test as it's only a fallback.
     800                                 */
     801                                if ( isset( $failures[ $module['fallback_for'] ] ) ) {
     802                                        $module['required'] = true;
     803                                } else {
     804                                        continue;
     805                                }
     806                        }
     807
     808                        if ( ! $this->child_test_php_extension_availability( $extension, $function ) && ( ! isset( $module['php_bundled_version'] ) || version_compare( PHP_VERSION, $module['php_bundled_version'], '<' ) ) ) {
     809                                if ( $module['required'] ) {
     810                                        $result['status'] = 'critical';
     811                                }
     812
     813                                if ( ! $module['required'] && 'good' === $result['status'] ) {
     814                                        $result['status'] = 'recommended';
     815                                }
     816
     817                                $failures[ $library ] = sprintf(
     818                                        '<span class="%s"><span class="screen-reader-text">%s</span></span> %s',
     819                                        ( $module['required'] ? 'error' : 'warning' ),
     820                                        ( $module['required'] ? esc_html__( 'Error' ) : esc_html__( 'Warning' ) ),
     821                                        sprintf(
     822                                        // translators: %1$2: If a module is required or recommended. %2$s: The module name.
     823                                                __( 'The %1$s module, %2$s, is not installed, or has been disabled.' ),
     824                                                ( $module['required'] ? __( 'required' ) : __( 'optional' ) ),
     825                                                $library
     826                                        )
     827                                );
     828                        }
     829                }
     830
     831                if ( ! empty( $failures ) ) {
     832                        $output = '<ul>';
     833
     834                        foreach ( $failures as $failure ) {
     835                                $output .= sprintf(
     836                                        '<li>%s</li>',
     837                                        $failure
     838                                );
     839                        }
     840
     841                        $output .= '</ul>';
     842                }
     843
     844                if ( 'good' !== $result['status'] ) {
     845                        if ( 'recommended' === $result['status'] ) {
     846                                $result['label'] = esc_html__( 'One or more recommended modules are missing' );
     847                        }
     848                        if ( 'critical' === $result['status'] ) {
     849                                $result['label'] = esc_html__( 'One or more required modules are missing' );
     850                        }
     851
     852                        $result['description'] .= sprintf(
     853                                '<p>%s</p>',
     854                                $output
     855                        );
     856                }
     857
     858                return $result;
     859        }
     860
     861        public function get_test_sql_server() {
     862                $result = array(
     863                        'label'       => esc_html__( 'SQL server is up to date' ),
     864                        'status'      => 'good',
     865                        'badge'       => array(
     866                                'label' => 'Security',
     867                                'color' => 'red',
     868                        ),
     869                        'description' => sprintf(
     870                                '<p>%s</p>',
     871                                esc_html__( 'The SQL server is the database where WordPress stores all your site’s content and settings' )
     872                        ),
     873                        'actions'     => '',
     874                        'test'        => 'sql_server',
     875                );
     876
     877                $db_dropin = file_exists( WP_CONTENT_DIR . '/db.php' );
     878
     879                if ( ! $this->mysql_rec_version_check ) {
     880                        $result['status'] = 'recommended';
     881
     882                        $result['label'] = esc_html__( 'Outdated SQL server' );
     883
     884                        $result['description'] .= sprintf(
     885                                '<p>%s</p>',
     886                                sprintf(
     887                                        // translators: %1$s: The database engine in use (MySQL or MariaDB). %2$s: Database server recommended version number.
     888                                        esc_html__( 'For optimal performance and security reasons, we recommend running %1$s version %2$s or higher. Contact your web hosting company to correct this.' ),
     889                                        ( $this->mariadb ? 'MariaDB' : 'MySQL' ),
     890                                        $this->health_check_mysql_rec_version
     891                                )
     892                        );
     893                }
     894
     895                if ( ! $this->mysql_min_version_check ) {
     896                        $result['status'] = 'critical';
     897
     898                        $result['label'] = esc_html__( 'Severely outdated SQL server' );
     899
     900                        $result['description'] .= sprintf(
     901                                '<p>%s</p>',
     902                                sprintf(
     903                                        // translators: %1$s: The database engine in use (MySQL or MariaDB). %2$s: Database server minimum version number.
     904                                        esc_html__( 'WordPress requires %1$s version %2$s or higher. Contact your web hosting company to correct this.' ),
     905                                        ( $this->mariadb ? 'MariaDB' : 'MySQL' ),
     906                                        HEALTH_CHECK_MYSQL_MIN_VERSION
     907                                )
     908                        );
     909                }
     910
     911                if ( $db_dropin ) {
     912                        // translators: %s: The database engine in use (MySQL or MariaDB).
     913                        $result['description'] .= sprintf(
     914                                '<p>%s</p>',
     915                                wp_kses(
     916                                        sprintf(
     917                                                // translators: %s: The name of the database engine being used.
     918                                                __( 'You are using a <code>wp-content/db.php</code> drop-in which might mean that a %s database is not being used.' ),
     919                                                ( $this->mariadb ? 'MariaDB' : 'MySQL' )
     920                                        ),
     921                                        array(
     922                                                'code' => true,
     923                                        )
     924                                )
     925                        );
     926                }
     927
     928                return $result;
     929        }
     930
     931        public function get_test_utf8mb4_support() {
     932                global $wpdb;
     933
     934                $result = array(
     935                        'label'       => esc_html__( 'UTF8MB4 is supported' ),
     936                        'status'      => 'good',
     937                        'badge'       => array(
     938                                'label' => 'Performance',
     939                                'color' => 'orange',
     940                        ),
     941                        'description' => sprintf(
     942                                '<p>%s</p>',
     943                                esc_html__( 'UTF8MB4 is a database storage attribute that makes sure your site can store non-English text and other strings (for instance emoticons) without unexpected problems.' )
     944                        ),
     945                        'actions'     => '',
     946                        'test'        => 'utf8mb4_support',
     947                );
     948
     949                if ( ! $this->mariadb ) {
     950                        if ( version_compare( $this->mysql_server_version, '5.5.3', '<' ) ) {
     951                                $result['status'] = 'recommended';
     952
     953                                $result['label'] = esc_html__( 'UTF8MB4 requires an SQL update' );
     954
     955                                $result['description'] .= sprintf(
     956                                        '<p>%s</p>',
     957                                        sprintf(
     958                                                // translators: %1$s: Database engine name. %2$s: Version number.
     959                                                esc_html__( 'WordPress\' utf8mb4 support requires %1$s version %2$s or greater' ),
     960                                                'MySQL',
     961                                                '5.5.3'
     962                                        )
     963                                );
     964                        } else {
     965                                $result['description'] .= sprintf(
     966                                        '<p>%s</p>',
     967                                        esc_html__( 'Your MySQL version supports utf8mb4' )
     968                                );
     969                        }
     970                } else { // MariaDB introduced utf8mb4 support in 5.5.0
     971                        if ( version_compare( $this->mysql_server_version, '5.5.0', '<' ) ) {
     972                                $result['status'] = 'recommended';
     973
     974                                $result['label'] = esc_html__( 'UTF8MB4 requires an SQL update' );
     975
     976                                $result['description'] .= sprintf(
     977                                        '<p>%s</p>',
     978                                        sprintf(
     979                                                // translators: %1$s: Database engine name. %2$s: Version number.
     980                                                esc_html__( 'WordPress\' utf8mb4 support requires %1$s version %2$s or greater' ),
     981                                                'MariaDB',
     982                                                '5.5.0'
     983                                        )
     984                                );
     985                        } else {
     986                                $result['description'] .= sprintf(
     987                                        '<p>%s</p>',
     988                                        esc_html__( 'Your MariaDB version supports utf8mb4' )
     989                                );
     990                        }
     991                }
     992
     993                if ( $wpdb->use_mysqli ) {
     994                        // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_get_client_info
     995                        $mysql_client_version = mysqli_get_client_info();
     996                } else {
     997                        // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysql_get_client_info
     998                        $mysql_client_version = mysql_get_client_info();
     999                }
     1000
     1001                /*
     1002                 * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
     1003                 * mysqlnd has supported utf8mb4 since 5.0.9.
     1004                 */
     1005                if ( false !== strpos( $mysql_client_version, 'mysqlnd' ) ) {
     1006                        $mysql_client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $mysql_client_version );
     1007                        if ( version_compare( $mysql_client_version, '5.0.9', '<' ) ) {
     1008                                $result['status'] = 'recommended';
     1009
     1010                                $result['label'] = esc_html__( 'UTF8MB4 requires a newer client library' );
     1011
     1012                                $result['description'] .= sprintf(
     1013                                        '<p>%s</p>',
     1014                                        sprintf(
     1015                                                // translators: %1$s: Name of the library, %2$s: Number of version.
     1016                                                esc_html__( 'WordPress\' utf8mb4 support requires MySQL client library (%1$s) version %2$s or newer.' ),
     1017                                                'mysqlnd',
     1018                                                '5.0.9'
     1019                                        )
     1020                                );
     1021                        }
     1022                } else {
     1023                        if ( version_compare( $mysql_client_version, '5.5.3', '<' ) ) {
     1024                                $result['status'] = 'recommended';
     1025
     1026                                $result['label'] = esc_html__( 'UTF8MB4 requires a newer client library' );
     1027
     1028                                $result['description'] .= sprintf(
     1029                                        '<p>%s</p>',
     1030                                        sprintf(
     1031                                                // translators: %1$s: Name of the library, %2$s: Number of version.
     1032                                                __( 'WordPress\' utf8mb4 support requires MySQL client library (%1$s) version %2$s or newer.' ),
     1033                                                'libmysql',
     1034                                                '5.5.3'
     1035                                        )
     1036                                );
     1037                        }
     1038                }
     1039
     1040                return $result;
     1041        }
     1042
     1043        public function get_test_dotorg_communication() {
     1044                $result = array(
     1045                        'label'       => esc_html__( 'Can communicate with WordPress.org' ),
     1046                        'status'      => '',
     1047                        'badge'       => array(
     1048                                'label' => 'Security',
     1049                                'color' => 'red',
     1050                        ),
     1051                        'description' => sprintf(
     1052                                '<p>%s</p>',
     1053                                esc_html__( 'Communicating with the WordPress servers is used to check for new versions, Communicating with the WordPress servers is used to check for new versions, and to both install and update WordPress core, themes or plugins.' )
     1054                        ),
     1055                        'actions'     => '',
     1056                        'test'        => 'dotorg_communication',
     1057                );
     1058
     1059                $wp_dotorg = wp_remote_get( 'https://wordpress.org', array(
     1060                        'timeout' => 10,
     1061                ) );
     1062                if ( ! is_wp_error( $wp_dotorg ) ) {
     1063                        $result['status'] = 'good';
     1064                } else {
     1065                        $result['status'] = 'critical';
     1066
     1067                        $result['label'] = esc_html__( 'Could not reach WordPress.org' );
     1068
     1069                        $result['description'] .= sprintf(
     1070                                '<p>%s</p>',
     1071                                sprintf(
     1072                                        '<span class="error"><span class="screen-reader-text">%s</span></span> %s',
     1073                                        esc_html__( 'Error' ),
     1074                                        sprintf(
     1075                                                // translators: %1$s: The IP address WordPress.org resolves to. %2$s: The error returned by the lookup.
     1076                                                __( 'Your site is unable to reach WordPress.org at %1$s, and returned the error: %2$s' ),
     1077                                                gethostbyname( 'wordpress.org' ),
     1078                                                $wp_dotorg->get_error_message()
     1079                                        )
     1080                                )
     1081                        );
     1082                }
     1083
     1084                return $result;
     1085        }
     1086
     1087        public function json_test_dotorg_communication() {
     1088                wp_send_json_success( $this->get_test_dotorg_communication() );
     1089        }
     1090
     1091        public function get_test_is_in_debug_mode() {
     1092                $result = array(
     1093                        'label'       => esc_html__( 'Your site is not set to output debug information' ),
     1094                        'status'      => 'good',
     1095                        'badge'       => array(
     1096                                'label' => 'Security',
     1097                                'color' => 'red',
     1098                        ),
     1099                        'description' => sprintf(
     1100                                '<p>%s</p>',
     1101                                esc_html__( 'Debug mode is often enabled to gather more details about an error or site failure, but may contain sensitive information which should not be available on a publicly available website.' )
     1102                        ),
     1103                        'actions'     => '',
     1104                        'test'        => 'is_in_debug_mode',
     1105                );
     1106
     1107                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     1108                        if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
     1109                                $result['label'] = esc_html__( 'Your site is set to log errors to a potentially public file.' );
     1110
     1111                                $result['status'] = 'critical';
     1112
     1113                                $result['description'] .= sprintf(
     1114                                        '<p>%s</p>',
     1115                                        esc_html__( 'The value, WP_DEBUG_LOG, has been added to this websites configuration file. This means any errors on the site will be written to a file which is potentially available to normal users.' )
     1116                                );
     1117                        }
     1118
     1119                        if ( defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) {
     1120                                $result['label'] = esc_html__( 'Your site is set to display errors to site visitors.' );
     1121
     1122                                $result['status'] = 'critical';
     1123
     1124                                $result['description'] .= sprintf(
     1125                                        '<p>%s</p>',
     1126                                        esc_html__( 'The value, WP_DEBUG_DISPLAY, has either been added to your configuration file, or left with its default value. This will make errors display on the front end of your site.' )
     1127                                );
     1128                        }
     1129                }
     1130
     1131                return $result;
     1132        }
     1133
     1134        public function json_test_is_in_debug_mode() {
     1135                wp_send_json_success( $this->get_test_is_in_debug_mode() );
     1136        }
     1137
     1138        public function get_test_https_status() {
     1139                $result = array(
     1140                        'label'       => '',
     1141                        'status'      => '',
     1142                        'badge'       => array(
     1143                                'label' => 'Security',
     1144                                'color' => 'red',
     1145                        ),
     1146                        'description' => '',
     1147                        'actions'     => '',
     1148                        'test'        => 'https_status',
     1149                );
     1150
     1151                if ( is_ssl() ) {
     1152                        $wp_url   = get_bloginfo( 'wpurl' );
     1153                        $site_url = get_bloginfo( 'url' );
     1154
     1155                        if ( 'https' !== substr( $wp_url, 0, 5 ) || 'https' !== substr( $site_url, 0, 5 ) ) {
     1156                                $result['status'] = 'recommended';
     1157
     1158                                $result['label'] = esc_html__( 'Only parts of your site are using HTTPS' );
     1159
     1160                                $result['description'] = sprintf(
     1161                                        '<p>%s</p>',
     1162                                        sprintf(
     1163                                                // translators: %s: URL to Settings > General to change options.
     1164                                                __( 'You are accessing this website using HTTPS, but your <a href="%s">WordPress Address</a> is not set up to use HTTPS by default.' ),
     1165                                                esc_url( admin_url( 'options-general.php' ) )
     1166                                        )
     1167                                );
     1168
     1169                                $result['actions'] = sprintf(
     1170                                        '<a href="%s">%s</a>',
     1171                                        esc_url( admin_url( 'options-general.php' ) ),
     1172                                        esc_html__( 'Update your site addresses' )
     1173                                );
     1174                        } else {
     1175                                $result['status'] = 'good';
     1176
     1177                                $result['label'] = esc_html__( 'Your website is using an active HTTPS connection.' );
     1178                        }
     1179                } else {
     1180                        $result['status'] = 'recommended';
     1181
     1182                        $result['label'] = esc_html__( 'Your site does not use HTTPS' );
     1183
     1184                        $result['description'] = sprintf(
     1185                                '<p>%s</p>',
     1186                                esc_html__( 'An HTTPS connection is needed for many features on the web today, it also gains the trust of your visitors by helping to protecting their online privacy.' )
     1187                        );
     1188
     1189                        $result['actions'] = sprintf(
     1190                                '<a href="%s">%s</a>',
     1191                                esc_url(
     1192                                        // translators: Website for explaining HTTPS and why it should be used.
     1193                                        __( 'https://www.cloudflare.com/learning/security/why-use-https/' )
     1194                                ),
     1195                                esc_html__( 'Read more about why you should use HTTPS' )
     1196                        );
     1197                }
     1198
     1199                return $result;
     1200        }
     1201
     1202        public function get_test_ssl_support() {
     1203                $result = array(
     1204                        'label'       => '',
     1205                        'status'      => '',
     1206                        'badge'       => array(
     1207                                'label' => 'Security',
     1208                                'color' => 'red',
     1209                        ),
     1210                        'description' => sprintf(
     1211                                '<p>%s</p>',
     1212                                esc_html__( 'Securely communicating between servers are needed for transactions such as fetching files, conducting sales on store sites, and much more.' )
     1213                        ),
     1214                        'actions'     => '',
     1215                        'test'        => 'ssl_support',
     1216                );
     1217
     1218                $supports_https = wp_http_supports( array( 'ssl' ) );
     1219
     1220                if ( $supports_https ) {
     1221                        $result['status'] = 'good';
     1222
     1223                        $result['label'] = esc_html__( 'Your site can communicate securely with other services.' );
     1224                } else {
     1225                        $result['status'] = 'critical';
     1226
     1227                        $result['label'] = esc_html__( 'Your site is unable to communicate securely with other services.' );
     1228
     1229                        $result['description'] .= sprintf(
     1230                                '<p>%s</p>',
     1231                                esc_html__( 'Talk to your web host about OpenSSL support for PHP' )
     1232                        );
     1233                }
     1234
     1235                return $result;
     1236        }
     1237
     1238        public function get_test_scheduled_events() {
     1239                $result = array(
     1240                        'label'       => esc_html__( 'Scheduled events are running' ),
     1241                        'status'      => 'good',
     1242                        'badge'       => array(
     1243                                'label' => 'Performance',
     1244                                'color' => 'orange',
     1245                        ),
     1246                        'description' => sprintf(
     1247                                '<p>%s</p>',
     1248                                esc_html__( 'Scheduled events are what periodically looks for updates to plugins, themes and WordPress it self. It is also what makes sure scheduled posts are published on time. It may also be used by various plugins to make sure that planned actions are executed.' )
     1249                        ),
     1250                        'actions'     => '',
     1251                        'test'        => 'scheduled_events',
     1252                );
     1253
     1254                $this->wp_schedule_test_init();
     1255
     1256                if ( is_wp_error( $this->has_missed_cron() ) ) {
     1257                        $result['status'] = 'critical';
     1258
     1259                        $result['label'] = esc_html__( 'It was not possible to check your scheduled events' );
     1260
     1261                        $result['description'] = sprintf(
     1262                                '<p>%s</p>',
     1263                                sprintf(
     1264                                        // translators: %s: The error message returned while from the cron scheduler.
     1265                                        esc_html__( 'While trying to test your sites scheduled events, the following error was returned: %s' ),
     1266                                        $this->has_missed_cron()->get_error_message()
     1267                                )
     1268                        );
     1269                } else {
     1270                        if ( $this->has_missed_cron() ) {
     1271                                $result['status'] = 'recommended';
     1272
     1273                                $result['label'] = esc_html__( 'A scheduled event has failed' );
     1274
     1275                                $result['description'] = sprintf(
     1276                                        '<p>%s</p>',
     1277                                        sprintf(
     1278                                                // translators: %s: The name of the failed cron event.
     1279                                                esc_html__( 'The scheduled event, %s, failed to run. Your site still works, but this may indicate that scheduling posts or automated updates may not work as intended.' ),
     1280                                                $this->last_missed_cron
     1281                                        )
     1282                                );
     1283                        }
     1284                }
     1285
     1286                return $result;
     1287        }
     1288
     1289        public function get_test_background_updates() {
     1290                $result = array(
     1291                        'label'       => esc_html__( 'Background updates are working' ),
     1292                        'status'      => 'good',
     1293                        'badge'       => array(
     1294                                'label' => 'Security',
     1295                                'color' => 'red',
     1296                        ),
     1297                        'description' => sprintf(
     1298                                '<p>%s</p>',
     1299                                esc_html__( 'Background updates ensure that WordPress can auto-update if a security update is released for the version you are currently using.' )
     1300                        ),
     1301                        'actions'     => '',
     1302                        'test'        => 'background_updates',
     1303                );
     1304
     1305                if ( ! class_exists( 'Site_Health_Auto_Updates' ) ) {
     1306                        require_once( ABSPATH . 'wp-admin/includes/class-wp-site-health-auto-updates.php' );
     1307                }
     1308
     1309                $automatic_updates = new Site_Health_Auto_Updates();
     1310                $tests             = $automatic_updates->run_tests();
     1311
     1312                $output = '<ul>';
     1313
     1314                foreach ( $tests as $test ) {
     1315                        $severity_string = esc_html__( 'Passed' );
     1316
     1317                        if ( 'fail' === $test->severity ) {
     1318                                $result['label'] = esc_html__( 'Background updates are not working as expected' );
     1319
     1320                                $result['status'] = 'critical';
     1321
     1322                                $severity_string = esc_html__( 'Error' );
     1323                        }
     1324
     1325                        if ( 'warning' === $test->severity && 'good' === $result['status'] ) {
     1326                                $result['label'] = esc_html__( 'Background updates may not be working properly' );
     1327
     1328                                $result['status'] = 'recommended';
     1329
     1330                                $severity_string = esc_html__( 'Warning' );
     1331                        }
     1332
     1333                        $output .= sprintf(
     1334                                '<li><span class="%s"><span class="screen-reader-text">%s</span></span> %s</li>',
     1335                                esc_attr( $test->severity ),
     1336                                $severity_string,
     1337                                $test->desc
     1338                        );
     1339                }
     1340
     1341                $output .= '</ul>';
     1342
     1343                if ( 'good' !== $result['status'] ) {
     1344                        $result['description'] .= sprintf(
     1345                                '<p>%s</p>',
     1346                                $output
     1347                        );
     1348                }
     1349
     1350                return $result;
     1351        }
     1352
     1353        public function json_test_background_updates() {
     1354                wp_send_json_success( $this->get_test_background_updates() );
     1355        }
     1356
     1357        public function get_test_loopback_requests() {
     1358                $result = array(
     1359                        'label'       => esc_html__( 'Your site can perform loopback requests' ),
     1360                        'status'      => 'good',
     1361                        'badge'       => array(
     1362                                'label' => 'Performance',
     1363                                'color' => 'orange',
     1364                        ),
     1365                        'description' => sprintf(
     1366                                '<p>%s</p>',
     1367                                esc_html__( 'Loopback requests are used to run scheduled events, and are also used by the built-in editors for themes and plugins to verify code stability.' )
     1368                        ),
     1369                        'actions'     => '',
     1370                        'test'        => 'loopback_requests',
     1371                );
     1372
     1373                $check_loopback = $this->can_perform_loopback();
     1374
     1375                $result['status'] = $check_loopback->status;
     1376
     1377                if ( 'good' !== $check_loopback->status ) {
     1378                        $result['label'] = esc_html__( 'Your site could not complete a loopback request' );
     1379
     1380                        $result['description'] .= sprintf(
     1381                                '<p>%s</p>',
     1382                                $check_loopback->message
     1383                        );
     1384                }
     1385
     1386                return $result;
     1387        }
     1388
     1389        public function json_test_loopback_requests() {
     1390                wp_send_json_success( $this->get_test_loopback_requests() );
     1391        }
     1392
     1393        public function get_test_http_requests() {
     1394                $result = array(
     1395                        'label'       => esc_html__( 'HTTP requests seem to be working as expected' ),
     1396                        'status'      => 'good',
     1397                        'badge'       => array(
     1398                                'label' => 'Performance',
     1399                                'color' => 'orange',
     1400                        ),
     1401                        'description' => sprintf(
     1402                                '<p>%s</p>',
     1403                                esc_html__( 'It is possible for site maintainers to block all, or some, communication to other sites and services. If set up incorrectly, this may prevent plugins and themes from working as intended.' )
     1404                        ),
     1405                        'actions'     => '',
     1406                        'test'        => 'http_requests',
     1407                );
     1408
     1409                $blocked = false;
     1410                $hosts   = array();
     1411
     1412                if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) ) {
     1413                        $blocked = true;
     1414                }
     1415
     1416                if ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
     1417                        $hosts = explode( ',', WP_ACCESSIBLE_HOSTS );
     1418                }
     1419
     1420                if ( $blocked && 0 === sizeof( $hosts ) ) {
     1421                        $result['status'] = 'critical';
     1422
     1423                        $result['label'] = esc_html__( 'HTTP requests are blocked' );
     1424
     1425                        $result['description'] .= sprintf(
     1426                                '<p>%s</p>',
     1427                                esc_html__( 'HTTP requests have been blocked by the WP_HTTP_BLOCK_EXTERNAL constant, with no allowed hosts.' )
     1428                        );
     1429                }
     1430
     1431                if ( $blocked && 0 < sizeof( $hosts ) ) {
     1432                        $result['status'] = 'recommended';
     1433
     1434                        $result['label'] = esc_html__( 'HTTP requests are partially blocked' );
     1435
     1436                        $result['description'] .= sprintf(
     1437                                '<p>%s</p>',
     1438                                sprintf(
     1439                                        // translators: %s: List of hostnames whitelisted.
     1440                                        esc_html__( 'HTTP requests have been blocked by the WP_HTTP_BLOCK_EXTERNAL constant, with some hosts whitelisted: %s.' ),
     1441                                        implode( ',', $hosts )
     1442                                )
     1443                        );
     1444                }
     1445
     1446                return $result;
     1447        }
     1448
     1449        public function get_test_rest_availability() {
     1450                $result = array(
     1451                        'label'       => esc_html__( 'The REST API is available' ),
     1452                        'status'      => 'good',
     1453                        'badge'       => array(
     1454                                'label' => 'Performance',
     1455                                'color' => 'orange',
     1456                        ),
     1457                        'description' => sprintf(
     1458                                '<p>%s</p>',
     1459                                esc_html__( 'The REST API is one way WordPress, and other applications, communicate with the server. One example is the block editor screen, which relies on this to display, and save, your posts and pages.' )
     1460                        ),
     1461                        'actions'     => '',
     1462                        'test'        => 'rest_availability',
     1463                );
     1464
     1465                $cookies = wp_unslash( $_COOKIE );
     1466                $timeout = 10;
     1467                $headers = array(
     1468                        'Cache-Control' => 'no-cache',
     1469                        'X-WP-Nonce'    => wp_create_nonce( 'wp_rest' ),
     1470                );
     1471
     1472                // Include Basic auth in loopback requests.
     1473                if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) {
     1474                        $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
     1475                }
     1476
     1477                $url = rest_url( 'wp/v2/types/post' );
     1478
     1479                // We only need the first post to ensure this works, to make it low impact.
     1480                $url = add_query_arg( array(
     1481                        'context' => 'edit',
     1482                ), $url );
     1483
     1484                $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) );
     1485
     1486                if ( is_wp_error( $r ) ) {
     1487                        $result['status'] = 'critical';
     1488
     1489                        $result['label'] = esc_html__( 'The REST API encountered an error' );
     1490
     1491                        $result['description'] .= sprintf(
     1492                                '<p>%s</p>',
     1493                                sprintf(
     1494                                        '%s<br>%s',
     1495                                        esc_html__( 'The REST API request failed due to an error.' ),
     1496                                        sprintf(
     1497                                                // translators: %1$d: The HTTP response code. %2$s: The error message returned.
     1498                                                esc_html__( 'Error encountered: (%1$d) %2$s' ),
     1499                                                wp_remote_retrieve_response_code( $r ),
     1500                                                $r->get_error_message()
     1501                                        )
     1502                                )
     1503                        );
     1504                } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) {
     1505                        $result['status'] = 'recommended';
     1506
     1507                        $result['label'] = esc_html__( 'The REST API encountered an unexpected result' );
     1508
     1509                        $result['description'] .= sprintf(
     1510                                '<p>%s</p>',
     1511                                sprintf(
     1512                                        // translators: %1$d: The HTTP response code returned. %2$s: The error message returned.
     1513                                        esc_html__( 'The REST API call gave the following unexpected result: (%1$d) %2$s.' ),
     1514                                        wp_remote_retrieve_response_code( $r ),
     1515                                        wp_remote_retrieve_body( $r )
     1516                                )
     1517                        );
     1518                } else {
     1519                        $json = json_decode( wp_remote_retrieve_body( $r ), true );
     1520
     1521                        if ( false !== $json && ! isset( $json['capabilities'] ) ) {
     1522                                $result['status'] = 'recommended';
     1523
     1524                                $result['label'] = esc_html__( 'The REST API did not behave correctly' );
     1525
     1526                                $result['description'] .= sprintf(
     1527                                        '<p>%s</p>',
     1528                                        esc_html__( 'The REST API did not process the \'context\' query parameter correctly.' )
     1529                                );
     1530                        }
     1531                }
     1532
     1533                return $result;
     1534        }
     1535
     1536        /**
     1537         * Return a set of tests that belong to the site status page.
     1538         *
     1539         * Each site status test is defined here, they may be `direct` tests, that run on page load,
     1540         * or `async` tests which will run later down the line via JavaScript calls to improve page
     1541         * performance and hopefully also user experiences.
     1542         *
     1543         * @return array
     1544         */
     1545        public static function get_tests() {
     1546                $tests = array(
     1547                        'direct' => array(
     1548                                'wordpress_version' => array(
     1549                                        'label' => __( 'WordPress Version' ),
     1550                                        'test'  => 'wordpress_version',
     1551                                ),
     1552                                'plugin_version'    => array(
     1553                                        'label' => __( 'Plugin Versions' ),
     1554                                        'test'  => 'plugin_version',
     1555                                ),
     1556                                'theme_version'     => array(
     1557                                        'label' => __( 'Theme Versions' ),
     1558                                        'test'  => 'theme_version',
     1559                                ),
     1560                                'php_version'       => array(
     1561                                        'label' => __( 'PHP Version' ),
     1562                                        'test'  => 'php_version',
     1563                                ),
     1564                                'sql_server'        => array(
     1565                                        'label' => __( 'Database Server version' ),
     1566                                        'test'  => 'sql_server',
     1567                                ),
     1568                                'php_extensions'    => array(
     1569                                        'label' => __( 'PHP Extensions' ),
     1570                                        'test'  => 'php_extensions',
     1571                                ),
     1572                                'utf8mb4_support'   => array(
     1573                                        'label' => __( 'MySQL utf8mb4 support' ),
     1574                                        'test'  => 'utf8mb4_support',
     1575                                ),
     1576                                'https_status'      => array(
     1577                                        'label' => __( 'HTTPS status' ),
     1578                                        'test'  => 'https_status',
     1579                                ),
     1580                                'ssl_support'       => array(
     1581                                        'label' => __( 'Secure communication' ),
     1582                                        'test'  => 'ssl_support',
     1583                                ),
     1584                                'scheduled_events'  => array(
     1585                                        'label' => __( 'Scheduled events' ),
     1586                                        'test'  => 'scheduled_events',
     1587                                ),
     1588                                'http_requests'     => array(
     1589                                        'label' => __( 'HTTP Requests' ),
     1590                                        'test'  => 'http_requests',
     1591                                ),
     1592                                'debug_enabled'     => array(
     1593                                        'label' => __( 'Debugging enabled' ),
     1594                                        'test'  => 'is_in_debug_mode',
     1595                                ),
     1596                        ),
     1597                        'async'  => array(
     1598                                'dotorg_communication' => array(
     1599                                        'label' => __( 'Communication with WordPress.org' ),
     1600                                        'test'  => 'dotorg_communication',
     1601                                ),
     1602                                'background_updates'   => array(
     1603                                        'label' => __( 'Background updates' ),
     1604                                        'test'  => 'background_updates',
     1605                                ),
     1606                                'loopback_requests'    => array(
     1607                                        'label' => __( 'Loopback request' ),
     1608                                        'test'  => 'loopback_requests',
     1609                                ),
     1610                        ),
     1611                );
     1612
     1613                // Conditionally include REST rules if the function for it exists.
     1614                if ( function_exists( 'rest_url' ) ) {
     1615                        $tests['direct']['rest_availability'] = array(
     1616                                'label' => __( 'REST API availability' ),
     1617                                'test'  => 'rest_availability',
     1618                        );
     1619                }
     1620
     1621                /**
     1622                 * Add or modify which site status tests are ran on a site.
     1623                 *
     1624                 * The site health is determined by a set of tests based on best practices from
     1625                 * both the WordPress Hosting Team, but also web standards in general.
     1626                 *
     1627                 * Some sites may not have the same requirements, for example the automatic update
     1628                 * checks may be handled by a host, and are therefore disabled in core.
     1629                 * Or maybe you want to introduce a new test, is caching enabled/disabled/stale for example.
     1630                 *
     1631                 * Test may be added either as direct, or asynchronous ones. Any test that may require some time
     1632                 * to complete should run asynchronously, to avoid extended loading periods within wp-admin.
     1633                 *
     1634                 */
     1635                $tests = apply_filters( 'site_status_tests', $tests );
     1636
     1637                return $tests;
     1638        }
     1639
     1640        public static function admin_body_class( $body_class ) {
     1641                $body_class .= ' site-health';
     1642
     1643                return $body_class;
     1644        }
     1645
     1646        /**
     1647         * Initiate the class
     1648         *
     1649         * @return void
     1650         */
     1651        public function wp_schedule_test_init() {
     1652                $this->schedules = wp_get_schedules();
     1653                $this->get_cron_tasks();
     1654        }
     1655
     1656        /**
     1657         * Populate our list of cron events and store them to a class-wide variable.
     1658         *
     1659         * Derived from `get_cron_events()` in WP Crontrol (https://plugins.svn.wordpress.org/wp-crontrol)
     1660         * by John Blackburn.
     1661         *
     1662         * @return void
     1663         */
     1664        private function get_cron_tasks() {
     1665                $cron_tasks = _get_cron_array();
     1666
     1667                if ( empty( $cron_tasks ) ) {
     1668                        $this->crons = new WP_Error( 'no_tasks', __( 'No scheduled events exist on this site.' ) );
     1669                        return;
     1670                }
     1671
     1672                $this->crons = array();
     1673
     1674                foreach ( $cron_tasks as $time => $cron ) {
     1675                        foreach ( $cron as $hook => $dings ) {
     1676                                foreach ( $dings as $sig => $data ) {
     1677
     1678                                        $this->crons[ "$hook-$sig-$time" ] = (object) array(
     1679                                                'hook'     => $hook,
     1680                                                'time'     => $time,
     1681                                                'sig'      => $sig,
     1682                                                'args'     => $data['args'],
     1683                                                'schedule' => $data['schedule'],
     1684                                                'interval' => isset( $data['interval'] ) ? $data['interval'] : null,
     1685                                        );
     1686
     1687                                }
     1688                        }
     1689                }
     1690        }
     1691
     1692        /**
     1693         * Check if any scheduled tasks have been missed.
     1694         *
     1695         * Returns a boolean value of `true` if a scheduled task has been missed and ends processing.
     1696         * If the list of crons is an instance of WP_Error, return the instance instead of a boolean value.
     1697         *
     1698         * @return bool|WP_Error
     1699         */
     1700        public function has_missed_cron() {
     1701                if ( is_wp_error( $this->crons ) ) {
     1702                        return $this->crons;
     1703                }
     1704
     1705                foreach ( $this->crons as $id => $cron ) {
     1706                        if ( ( $cron->time - time() ) < 0 ) {
     1707                                $this->last_missed_cron = $cron->hook;
     1708                                return true;
     1709                        }
     1710                }
     1711
     1712                return false;
     1713        }
     1714
     1715        /**
     1716         * Run a loopback test on our site.
     1717         *
     1718         * @return object
     1719         */
     1720        function can_perform_loopback() {
     1721                $cookies = wp_unslash( $_COOKIE );
     1722                $timeout = 10;
     1723                $headers = array(
     1724                        'Cache-Control' => 'no-cache',
     1725                );
     1726
     1727                // Include Basic auth in loopback requests.
     1728                if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) {
     1729                        $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
     1730                }
     1731
     1732                $url = admin_url();
     1733
     1734                $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout' ) );
     1735
     1736                if ( is_wp_error( $r ) ) {
     1737                        return (object) array(
     1738                                'status'  => 'critical',
     1739                                'message' => sprintf(
     1740                                        '%s<br>%s',
     1741                                        esc_html__( 'The loopback request to your site failed, this means features relying on them are not currently working as expected.' ),
     1742                                        sprintf(
     1743                                                // translators: %1$d: The HTTP response code. %2$s: The error message returned.
     1744                                                esc_html__( 'Error encountered: (%1$d) %2$s' ),
     1745                                                wp_remote_retrieve_response_code( $r ),
     1746                                                $r->get_error_message()
     1747                                        )
     1748                                ),
     1749                        );
     1750                }
     1751
     1752                if ( 200 !== wp_remote_retrieve_response_code( $r ) ) {
     1753                        return (object) array(
     1754                                'status'  => 'recommended',
     1755                                'message' => sprintf(
     1756                                        // translators: %d: The HTTP response code returned.
     1757                                        esc_html__( 'The loopback request returned an unexpected http status code, %d, it was not possible to determine if this will prevent features from working as expected.' ),
     1758                                        wp_remote_retrieve_response_code( $r )
     1759                                ),
     1760                        );
     1761                }
     1762
     1763                return (object) array(
     1764                        'status'  => 'good',
     1765                        'message' => __( 'The loopback request to your site completed successfully.' ),
     1766                );
     1767        }
     1768}
  • src/wp-admin/admin-ajax.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    131131        'edit-theme-plugin-file',
    132132        'wp-privacy-export-personal-data',
    133133        'wp-privacy-erase-personal-data',
     134        'health-check-site-status',
     135        'health-check-site-status-result'
    134136);
    135137
    136138// Deprecated
  • src/wp-admin/menu.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    4646        unset( $cap );
    4747}
    4848
     49$submenu['index.php'][11] = array( __( 'Site Health'), 'manage_options', 'site-health.php' );
     50
    4951$menu[4] = array( '', 'read', 'separator1', '', 'wp-menu-separator' );
    5052
    5153// $menu[5] = Posts
  • src/wp-admin/site-health-info.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2/**
     3 * Tools Administration Screen.
     4 *
     5 * @package WordPress
     6 * @subpackage Administration
     7 */
     8
     9/** WordPress Administration Bootstrap */
     10require_once( dirname( __FILE__ ) . '/admin.php' );
     11
     12if ( ! current_user_can( 'manage_options' ) ) {
     13        wp_die( __( 'Sorry, you do not have permission to access the debug data.' ), '', array( 'reponse' => 401 ) );
     14}
     15
     16wp_enqueue_style( 'site-health' );
     17wp_enqueue_script( 'site-health' );
     18
     19if ( ! class_exists( 'WP_Debug_Data' ) ) {
     20        require_once( ABSPATH . 'wp-admin/includes/class-wp-debug-data.php' );
     21}
     22if ( ! class_exists( 'WP_Site_Health' ) ) {
     23        require_once( ABSPATH . 'wp-admin/includes/class-wp-site-health.php' );
     24}
     25
     26$health_check_site_status = new WP_Site_Health();
     27
     28add_filter( 'admin_body_class', array( 'WP_Site_Health', 'admin_body_class' ) );
     29
     30require_once( ABSPATH . 'wp-admin/admin-header.php' );
     31?>
     32
     33        <div class="wrap health-check-header">
     34                <div class="title-section">
     35                        <h1>
     36                                <?php _ex( 'Site Health', 'Menu, Section and Page Title' ); ?>
     37                        </h1>
     38
     39                        <div id="progressbar" class="loading" data-pct="0" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-valuetext="<?php esc_attr_e( 'Site tests are running, please wait a moment.' ); ?>">
     40                                <svg width="100%" height="100%" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg">
     41                                        <circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
     42                                        <circle id="bar" r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
     43                                </svg>
     44                        </div>
     45                </div>
     46
     47                <nav class="tabs-wrapper" aria-label="<?php esc_attr_e( 'Secondary menu' ); ?>">
     48                        <a href="<?php echo esc_url( admin_url( 'site-health.php' ) ); ?>" class="tab">
     49                                <?php esc_html_e( 'Status' ); ?>
     50                        </a>
     51
     52                        <a href="<?php echo esc_url( admin_url( 'site-health.php?tab=debug' ) ); ?>" class="tab active" aria-current="true">
     53                                <?php esc_html_e( 'Info' ); ?>
     54                        </a>
     55                </nav>
     56
     57                <div class="wp-clearfix"></div>
     58        </div>
     59
     60        <div class="wrap health-check-body">
     61                <?php
     62                WP_Debug_Data::check_for_updates();
     63
     64                $info = WP_Debug_Data::debug_data();
     65                ?>
     66
     67                <h2>
     68                        <?php esc_html_e( 'Site Info' ); ?>
     69                </h2>
     70
     71                <p>
     72                        <?php esc_html_e( 'You can export the information on this page so it can be easily copied and pasted in support requests such as on the WordPress.org forums, or shared with your website / theme / plugin developers.' ); ?>
     73                </p>
     74
     75                <p>
     76                        <button type="button" class="button button-link health-check-toggle-copy-section">
     77                                <?php esc_html_e( 'Show options for copying this information' ); ?>
     78                        </button>
     79                </p>
     80
     81                <div class="system-information-copy-wrapper hidden">
     82                        <textarea id="system-information-default-copy-field" rows="10"><?php WP_Debug_Data::textarea_format( $info ); ?></textarea>
     83
     84                        <?php
     85                        if ( 'en_US' !== get_locale() && version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) :
     86
     87                                $english_info = WP_Debug_Data::debug_data( 'en_US' );
     88                                ?>
     89                                <textarea id="system-information-english-copy-field" class="system-information-copy-wrapper" rows="10"><?php WP_Debug_Data::textarea_format( $english_info ); ?></textarea>
     90
     91                        <?php endif; ?>
     92
     93                        <div class="copy-button-wrapper">
     94                                <button type="button" class="button button-primary health-check-copy-field" data-copy-field="default"><?php esc_html_e( 'Copy to clipboard' ); ?></button>
     95                                <span class="copy-field-success" aria-hidden="true">Copied!</span>
     96                        </div>
     97                        <?php if ( 'en_US' !== get_locale() && version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) : ?>
     98                                <div class="copy-button-wrapper">
     99                                        <button type="button" class="button health-check-copy-field" data-copy-field="english"><?php esc_html_e( 'Copy to clipboard (English)' ); ?></button>
     100                                        <span class="copy-field-success" aria-hidden="true">Copied!</span>
     101                                </div>
     102                        <?php endif; ?>
     103                </div>
     104
     105                <dl id="health-check-debug" role="presentation" class="health-check-accordion">
     106
     107                        <?php
     108                        foreach ( $info as $section => $details ) {
     109                                if ( ! isset( $details['fields'] ) || empty( $details['fields'] ) ) {
     110                                        continue;
     111                                }
     112                                ?>
     113                                <dt role="heading" aria-level="3">
     114                                        <button aria-expanded="false" class="health-check-accordion-trigger" aria-controls="health-check-accordion-block-<?php echo esc_attr( $section ); ?>" id="health-check-accordion-heading-<?php echo esc_attr( $section ); ?>" type="button">
     115                        <span class="title">
     116                                <?php echo esc_html( $details['label'] ); ?>
     117
     118                                <?php if ( isset( $details['show_count'] ) && $details['show_count'] ) : ?>
     119                                        <?php printf( '(%d)', count( $details['fields'] ) ); ?>
     120                                <?php endif; ?>
     121                        </span>
     122                                                <span class="icon"></span>
     123                                        </button>
     124                                </dt>
     125
     126                                <dd id="health-check-accordion-block-<?php echo esc_attr( $section ); ?>" role="region" aria-labelledby="health-check-accordion-heading-<?php echo esc_attr( $section ); ?>" class="health-check-accordion-panel" hidden="hidden">
     127                                        <?php
     128                                        if ( isset( $details['description'] ) && ! empty( $details['description'] ) ) {
     129                                                printf(
     130                                                        '<p>%s</p>',
     131                                                        wp_kses( $details['description'], array(
     132                                                                'a'      => array(
     133                                                                        'href' => true,
     134                                                                ),
     135                                                                'strong' => true,
     136                                                                'em'     => true,
     137                                                        ) )
     138                                                );
     139                                        }
     140                                        ?>
     141                                        <table class="widefat striped health-check-table">
     142                                                <tbody>
     143                                                <?php
     144                                                foreach ( $details['fields'] as $field ) {
     145                                                        if ( is_array( $field['value'] ) ) {
     146                                                                $values = '';
     147                                                                foreach ( $field['value'] as $name => $value ) {
     148                                                                        $values .= sprintf(
     149                                                                                '<li>%s: %s</li>',
     150                                                                                esc_html( $name ),
     151                                                                                esc_html( $value )
     152                                                                        );
     153                                                                }
     154                                                        } else {
     155                                                                $values = esc_html( $field['value'] );
     156                                                        }
     157
     158                                                        printf(
     159                                                                '<tr><td>%s</td><td>%s</td></tr>',
     160                                                                esc_html( $field['label'] ),
     161                                                                $values
     162                                                        );
     163                                                }
     164                                                ?>
     165                                                </tbody>
     166                                        </table>
     167                                </dd>
     168                        <?php } ?>
     169                </dl>
     170        </div>
     171
     172<?php
     173include( ABSPATH . 'wp-admin/admin-footer.php' );
  • src/wp-admin/site-health.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2/**
     3 * Tools Administration Screen.
     4 *
     5 * @package WordPress
     6 * @subpackage Administration
     7 */
     8
     9if ( isset( $_GET['tab'] ) && 'debug' === $_GET['tab'] ) {
     10        require_once( dirname( __FILE__ ) . '/site-health-info.php' );
     11        return;
     12}
     13
     14/** WordPress Administration Bootstrap */
     15require_once( dirname( __FILE__ ) . '/admin.php' );
     16
     17if ( ! current_user_can( 'manage_options' ) ) {
     18        wp_die( __( 'Sorry, you do not have permission to access site health information.' ), '', array( 'reponse' => 401 ) );
     19}
     20
     21wp_enqueue_style( 'site-health' );
     22wp_enqueue_script( 'site-health' );
     23
     24if ( ! class_exists( 'WP_Site_Health' ) ) {
     25        require_once( ABSPATH . 'wp-admin/includes/class-wp-site-health.php' );
     26}
     27
     28add_filter( 'admin_body_class', array( 'WP_Site_Health', 'admin_body_class' ) );
     29
     30$health_check_site_status = new WP_Site_Health();
     31
     32require_once( ABSPATH . 'wp-admin/admin-header.php' );
     33?>
     34
     35<div class="wrap health-check-header">
     36        <div class="title-section">
     37                <h1>
     38                        <?php _ex( 'Site Health', 'Menu, Section and Page Title' ); ?>
     39                </h1>
     40
     41                <div id="progressbar" class="loading" data-pct="0" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-valuetext="<?php esc_attr_e( 'Site tests are running, please wait a moment.' ); ?>">
     42                        <svg width="100%" height="100%" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg">
     43                                <circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
     44                                <circle id="bar" r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
     45                        </svg>
     46                </div>
     47        </div>
     48
     49        <nav class="tabs-wrapper" aria-label="<?php esc_attr_e( 'Secondary menu' ); ?>">
     50                <a href="<?php echo esc_url( admin_url( 'site-health.php' ) ); ?>" class="tab active" aria-current="true">
     51                        <?php esc_html_e( 'Status' ); ?>
     52                </a>
     53
     54                <a href="<?php echo esc_url( admin_url( 'site-health.php?tab=debug' ) ); ?>" class="tab">
     55                        <?php esc_html_e( 'Info' ); ?>
     56                </a>
     57        </nav>
     58
     59        <div class="wp-clearfix"></div>
     60</div>
     61
     62<div class="wrap health-check-body">
     63        <div class="site-status-all-clear hide">
     64                <p class="icon">
     65                        <span class="dashicons dashicons-yes"></span>
     66                </p>
     67
     68                <p class="encouragement">
     69                        <?php esc_html_e( 'Great job!' ); ?>
     70                </p>
     71
     72                <p>
     73                        <?php esc_html_e( 'Everything is running smoothly here.' ); ?>
     74                </p>
     75        </div>
     76
     77        <div class="site-status-has-issues">
     78                <h2>
     79                        <?php esc_html_e( 'Site Health Status' ); ?>
     80                </h2>
     81
     82                <div class="issues-wrapper" id="health-check-issues-critical">
     83                        <h3>
     84                                <span class="issue-count">0</span> <?php esc_html_e( 'Critical issues' ); ?>
     85                        </h3>
     86
     87                        <dl id="health-check-site-status-critical" role="presentation" class="health-check-accordion issues"></dl>
     88                </div>
     89
     90                <div class="issues-wrapper" id="health-check-issues-recommended">
     91                        <h3>
     92                                <span class="issue-count">0</span> <?php esc_html_e( 'Recommended improvements' ); ?>
     93                        </h3>
     94
     95                        <dl id="health-check-site-status-recommended" role="presentation" class="health-check-accordion issues"></dl>
     96                </div>
     97        </div>
     98
     99        <div class="view-more">
     100                <button type="button" class="button button-link site-health-view-passed" aria-expanded="false">
     101                        <?php esc_html_e( 'Show passed tests' ); ?>
     102                </button>
     103        </div>
     104
     105        <div class="issues-wrapper hidden" id="health-check-issues-good">
     106                <h3>
     107                        <span class="issue-count">0</span> <?php esc_html_e( 'Items with no issues detected' ); ?>
     108                </h3>
     109
     110                <dl id="health-check-site-status-good" role="presentation" class="health-check-accordion issues"></dl>
     111        </div>
     112</div>
     113
     114<?php
     115include( ABSPATH . 'wp-admin/admin-footer.php' );
  • src/wp-includes/script-loader.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    16591659                        )
    16601660                );
    16611661
     1662                $scripts->add( 'site-health', "/wp-admin/js/site-health$suffix.js", array( 'jquery', 'wp-a11y' ), false, 1 );
     1663
    16621664                $scripts->add( 'updates', "/wp-admin/js/updates$suffix.js", array( 'jquery', 'wp-util', 'wp-a11y' ), false, 1 );
    16631665                did_action( 'init' ) && $scripts->localize(
    16641666                        'updates',
     
    19041906        $styles->add( 'site-icon', "/wp-admin/css/site-icon$suffix.css" );
    19051907        $styles->add( 'l10n', "/wp-admin/css/l10n$suffix.css" );
    19061908        $styles->add( 'code-editor', "/wp-admin/css/code-editor$suffix.css", array( 'wp-codemirror' ) );
     1909        $styles->add( 'site-health', "/wp-admin/css/site-health$suffix.css" );
    19071910
    19081911        $styles->add( 'wp-admin', false, array( 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus', 'widgets', 'site-icon', 'l10n' ) );
    19091912
  • Gruntfile.js

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    263263                                        [ WORKING_DIR + 'wp-admin/js/tags-box.js' ]: [ './src/js/_enqueues/admin/tags-box.js' ],
    264264                                        [ WORKING_DIR + 'wp-admin/js/tags-suggest.js' ]: [ './src/js/_enqueues/admin/tags-suggest.js' ],
    265265                                        [ WORKING_DIR + 'wp-admin/js/tags.js' ]: [ './src/js/_enqueues/admin/tags.js' ],
     266                                        [ WORKING_DIR + 'wp-admin/js/site-health.js' ]: [ './src/js/_enqueues/admin/site-health.js' ],
    266267                                        [ WORKING_DIR + 'wp-admin/js/theme-plugin-editor.js' ]: [ './src/js/_enqueues/wp/theme-plugin-editor.js' ],
    267268                                        [ WORKING_DIR + 'wp-admin/js/theme.js' ]: [ './src/js/_enqueues/wp/theme.js' ],
    268269                                        [ WORKING_DIR + 'wp-admin/js/updates.js' ]: [ './src/js/_enqueues/wp/updates.js' ],