2019-11-02 10:38:58 +01:00
/ * *
* Interactions used by the Site Health modules in WordPress .
*
* @ output wp - admin / js / site - health . js
* /
/* global ajaxurl, ClipboardJS, SiteHealth, wp */
jQuery ( document ) . ready ( function ( $ ) {
var _ _ = wp . i18n . _ _ ,
_n = wp . i18n . _n ,
2020-09-15 14:29:22 +02:00
sprintf = wp . i18n . sprintf ,
clipboard = new ClipboardJS ( '.site-health-copy-buttons .copy-button' ) ,
isDebugTab = $ ( '.health-check-body.health-check-debug-tab' ) . length ,
pathsSizesSection = $ ( '#health-check-accordion-block-wp-paths-sizes' ) ,
successTimeout ;
2019-11-02 10:38:58 +01:00
// Debug information copy section.
clipboard . on ( 'success' , function ( e ) {
2020-09-15 14:29:22 +02:00
var triggerElement = $ ( e . trigger ) ,
successElement = $ ( '.success' , triggerElement . closest ( 'div' ) ) ;
// Clear the selection and move focus back to the trigger.
e . clearSelection ( ) ;
// Handle ClipboardJS focus bug, see https://github.com/zenorocha/clipboard.js/issues/680
triggerElement . focus ( ) ;
// Show success visual feedback.
clearTimeout ( successTimeout ) ;
successElement . removeClass ( 'hidden' ) ;
// Hide success visual feedback after 3 seconds since last success.
successTimeout = setTimeout ( function ( ) {
successElement . addClass ( 'hidden' ) ;
// Remove the visually hidden textarea so that it isn't perceived by assistive technologies.
if ( clipboard . clipboardAction . fakeElem && clipboard . clipboardAction . removeFake ) {
clipboard . clipboardAction . removeFake ( ) ;
}
} , 3000 ) ;
2019-11-02 10:38:58 +01:00
2020-09-15 14:29:22 +02:00
// Handle success audible feedback.
wp . a11y . speak ( _ _ ( 'Site information has been copied to your clipboard.' ) ) ;
2019-11-02 10:38:58 +01:00
} ) ;
// Accordion handling in various areas.
$ ( '.health-check-accordion' ) . on ( 'click' , '.health-check-accordion-trigger' , function ( ) {
var isExpanded = ( 'true' === $ ( this ) . attr ( 'aria-expanded' ) ) ;
if ( isExpanded ) {
$ ( this ) . attr ( 'aria-expanded' , 'false' ) ;
$ ( '#' + $ ( this ) . attr ( 'aria-controls' ) ) . attr ( 'hidden' , true ) ;
} else {
$ ( this ) . attr ( 'aria-expanded' , 'true' ) ;
$ ( '#' + $ ( this ) . attr ( 'aria-controls' ) ) . attr ( 'hidden' , false ) ;
}
} ) ;
// Site Health test handling.
$ ( '.site-health-view-passed' ) . on ( 'click' , function ( ) {
var goodIssuesWrapper = $ ( '#health-check-issues-good' ) ;
goodIssuesWrapper . toggleClass ( 'hidden' ) ;
$ ( this ) . attr ( 'aria-expanded' , ! goodIssuesWrapper . hasClass ( 'hidden' ) ) ;
} ) ;
2020-12-10 14:06:04 +01:00
/ * *
* Validates the Site Health test result format .
*
* @ since 5.6 . 0
*
* @ param { Object } issue
*
* @ return { boolean }
* /
function validateIssueData ( issue ) {
// Expected minimum format of a valid SiteHealth test response.
var minimumExpected = {
test : 'string' ,
label : 'string' ,
description : 'string'
} ,
passed = true ,
key , value , subKey , subValue ;
// If the issue passed is not an object, return a `false` state early.
if ( 'object' !== typeof ( issue ) ) {
return false ;
}
// Loop over expected data and match the data types.
for ( key in minimumExpected ) {
value = minimumExpected [ key ] ;
if ( 'object' === typeof ( value ) ) {
for ( subKey in value ) {
subValue = value [ subKey ] ;
if ( 'undefined' === typeof ( issue [ key ] ) ||
'undefined' === typeof ( issue [ key ] [ subKey ] ) ||
subValue !== typeof ( issue [ key ] [ subKey ] )
) {
passed = false ;
}
}
} else {
if ( 'undefined' === typeof ( issue [ key ] ) ||
value !== typeof ( issue [ key ] )
) {
passed = false ;
}
}
}
return passed ;
}
2019-11-02 10:38:58 +01:00
/ * *
2020-09-15 14:29:22 +02:00
* Appends a new issue to the issue list .
2019-11-02 10:38:58 +01:00
*
* @ since 5.2 . 0
*
* @ param { Object } issue The issue data .
* /
2020-09-15 14:29:22 +02:00
function appendIssue ( issue ) {
2019-11-02 10:38:58 +01:00
var template = wp . template ( 'health-check-issue' ) ,
issueWrapper = $ ( '#health-check-issues-' + issue . status ) ,
heading ,
count ;
2020-12-10 14:06:04 +01:00
/ *
* Validate the issue data format before using it .
* If the output is invalid , discard it .
* /
if ( ! validateIssueData ( issue ) ) {
return false ;
}
2019-11-02 10:38:58 +01:00
SiteHealth . site _status . issues [ issue . status ] ++ ;
count = SiteHealth . site _status . issues [ issue . status ] ;
2020-12-10 14:06:04 +01:00
// If no test name is supplied, append a placeholder for markup references.
if ( typeof issue . test === 'undefined' ) {
issue . test = issue . status + count ;
}
2019-11-02 10:38:58 +01:00
if ( 'critical' === issue . status ) {
2020-09-15 14:29:22 +02:00
heading = sprintf (
_n ( '%s critical issue' , '%s critical issues' , count ) ,
'<span class="issue-count">' + count + '</span>'
) ;
2019-11-02 10:38:58 +01:00
} else if ( 'recommended' === issue . status ) {
2020-09-15 14:29:22 +02:00
heading = sprintf (
_n ( '%s recommended improvement' , '%s recommended improvements' , count ) ,
'<span class="issue-count">' + count + '</span>'
) ;
2019-11-02 10:38:58 +01:00
} else if ( 'good' === issue . status ) {
2020-09-15 14:29:22 +02:00
heading = sprintf (
_n ( '%s item with no issues detected' , '%s items with no issues detected' , count ) ,
'<span class="issue-count">' + count + '</span>'
) ;
2019-11-02 10:38:58 +01:00
}
if ( heading ) {
$ ( '.site-health-issue-count-title' , issueWrapper ) . html ( heading ) ;
}
$ ( '.issues' , '#health-check-issues-' + issue . status ) . append ( template ( issue ) ) ;
}
/ * *
2020-09-15 14:29:22 +02:00
* Updates site health status indicator as asynchronous tests are run and returned .
2019-11-02 10:38:58 +01:00
*
* @ since 5.2 . 0
* /
2020-09-15 14:29:22 +02:00
function recalculateProgression ( ) {
2019-11-02 10:38:58 +01:00
var r , c , pct ;
var $progress = $ ( '.site-health-progress' ) ;
2019-11-15 22:59:44 +01:00
var $wrapper = $progress . closest ( '.site-health-progress-wrapper' ) ;
var $progressLabel = $ ( '.site-health-progress-label' , $wrapper ) ;
2019-11-02 10:38:58 +01:00
var $circle = $ ( '.site-health-progress svg #bar' ) ;
2020-09-15 14:29:22 +02:00
var totalTests = parseInt ( SiteHealth . site _status . issues . good , 0 ) +
2020-12-10 14:06:04 +01:00
parseInt ( SiteHealth . site _status . issues . recommended , 0 ) +
( parseInt ( SiteHealth . site _status . issues . critical , 0 ) * 1.5 ) ;
2020-09-15 14:29:22 +02:00
var failedTests = ( parseInt ( SiteHealth . site _status . issues . recommended , 0 ) * 0.5 ) +
2020-12-10 14:06:04 +01:00
( parseInt ( SiteHealth . site _status . issues . critical , 0 ) * 1.5 ) ;
2019-11-02 10:38:58 +01:00
var val = 100 - Math . ceil ( ( failedTests / totalTests ) * 100 ) ;
if ( 0 === totalTests ) {
$progress . addClass ( 'hidden' ) ;
return ;
}
2019-11-15 22:59:44 +01:00
$wrapper . removeClass ( 'loading' ) ;
2019-11-02 10:38:58 +01:00
r = $circle . attr ( 'r' ) ;
c = Math . PI * ( r * 2 ) ;
if ( 0 > val ) {
val = 0 ;
}
if ( 100 < val ) {
val = 100 ;
}
pct = ( ( 100 - val ) / 100 ) * c ;
$circle . css ( { strokeDashoffset : pct } ) ;
if ( 1 > parseInt ( SiteHealth . site _status . issues . critical , 0 ) ) {
$ ( '#health-check-issues-critical' ) . addClass ( 'hidden' ) ;
}
if ( 1 > parseInt ( SiteHealth . site _status . issues . recommended , 0 ) ) {
$ ( '#health-check-issues-recommended' ) . addClass ( 'hidden' ) ;
}
2019-11-15 22:59:44 +01:00
if ( 80 <= val && 0 === parseInt ( SiteHealth . site _status . issues . critical , 0 ) ) {
$wrapper . addClass ( 'green' ) . removeClass ( 'orange' ) ;
2019-11-02 10:38:58 +01:00
2019-11-15 22:59:44 +01:00
$progressLabel . text ( _ _ ( 'Good' ) ) ;
wp . a11y . speak ( _ _ ( 'All site health tests have finished running. Your site is looking good, and the results are now available on the page.' ) ) ;
} else {
$wrapper . addClass ( 'orange' ) . removeClass ( 'green' ) ;
2019-11-02 10:38:58 +01:00
2019-11-15 22:59:44 +01:00
$progressLabel . text ( _ _ ( 'Should be improved' ) ) ;
wp . a11y . speak ( _ _ ( 'All site health tests have finished running. There are items that should be addressed, and the results are now available on the page.' ) ) ;
2019-11-02 10:38:58 +01:00
}
if ( ! isDebugTab ) {
$ . post (
ajaxurl ,
{
'action' : 'health-check-site-status-result' ,
'_wpnonce' : SiteHealth . nonce . site _status _result ,
'counts' : SiteHealth . site _status . issues
}
) ;
2019-11-15 22:59:44 +01:00
if ( 100 === val ) {
$ ( '.site-status-all-clear' ) . removeClass ( 'hide' ) ;
$ ( '.site-status-has-issues' ) . addClass ( 'hide' ) ;
}
2019-11-02 10:38:58 +01:00
}
}
/ * *
2020-09-15 14:29:22 +02:00
* Queues the next asynchronous test when we ' re ready to run it .
2019-11-02 10:38:58 +01:00
*
* @ since 5.2 . 0
* /
function maybeRunNextAsyncTest ( ) {
var doCalculation = true ;
if ( 1 <= SiteHealth . site _status . async . length ) {
$ . each ( SiteHealth . site _status . async , function ( ) {
var data = {
'action' : 'health-check-' + this . test . replace ( '_' , '-' ) ,
'_wpnonce' : SiteHealth . nonce . site _status
} ;
if ( this . completed ) {
return true ;
}
doCalculation = false ;
this . completed = true ;
2020-12-10 14:06:04 +01:00
if ( 'undefined' !== typeof ( this . has _rest ) && this . has _rest ) {
wp . apiRequest ( {
url : wp . url . addQueryArgs ( this . test , { _locale : 'user' } ) ,
headers : this . headers
} )
. done ( function ( response ) {
/** This filter is documented in wp-admin/includes/class-wp-site-health.php */
appendIssue ( wp . hooks . applyFilters ( 'site_status_test_result' , response ) ) ;
} )
. fail ( function ( response ) {
var description ;
if ( 'undefined' !== typeof ( response . responseJSON ) && 'undefined' !== typeof ( response . responseJSON . message ) ) {
description = response . responseJSON . message ;
} else {
description = _ _ ( 'No details available' ) ;
}
addFailedSiteHealthCheckNotice ( this . url , description ) ;
} )
. always ( function ( ) {
maybeRunNextAsyncTest ( ) ;
} ) ;
} else {
$ . post (
ajaxurl ,
data
) . done ( function ( response ) {
2019-11-15 22:59:44 +01:00
/** This filter is documented in wp-admin/includes/class-wp-site-health.php */
2020-09-15 14:29:22 +02:00
appendIssue ( wp . hooks . applyFilters ( 'site_status_test_result' , response . data ) ) ;
2020-12-10 14:06:04 +01:00
} ) . fail ( function ( response ) {
var description ;
if ( 'undefined' !== typeof ( response . responseJSON ) && 'undefined' !== typeof ( response . responseJSON . message ) ) {
description = response . responseJSON . message ;
} else {
description = _ _ ( 'No details available' ) ;
}
addFailedSiteHealthCheckNotice ( this . url , description ) ;
} ) . always ( function ( ) {
2019-11-02 10:38:58 +01:00
maybeRunNextAsyncTest ( ) ;
2020-12-10 14:06:04 +01:00
} ) ;
}
2019-11-02 10:38:58 +01:00
return false ;
} ) ;
}
if ( doCalculation ) {
2020-09-15 14:29:22 +02:00
recalculateProgression ( ) ;
2019-11-02 10:38:58 +01:00
}
}
2020-12-10 14:06:04 +01:00
/ * *
* Add the details of a failed asynchronous test to the list of test results .
*
* @ since 5.6 . 0
* /
function addFailedSiteHealthCheckNotice ( url , description ) {
var issue ;
issue = {
'status' : 'recommended' ,
'label' : _ _ ( 'A test is unavailable' ) ,
'badge' : {
'color' : 'red' ,
'label' : _ _ ( 'Unavailable' )
} ,
'description' : '<p>' + url + '</p><p>' + description + '</p>' ,
'actions' : ''
} ;
/** This filter is documented in wp-admin/includes/class-wp-site-health.php */
appendIssue ( wp . hooks . applyFilters ( 'site_status_test_result' , issue ) ) ;
}
2019-11-02 10:38:58 +01:00
if ( 'undefined' !== typeof SiteHealth && ! isDebugTab ) {
if ( 0 === SiteHealth . site _status . direct . length && 0 === SiteHealth . site _status . async . length ) {
2020-09-15 14:29:22 +02:00
recalculateProgression ( ) ;
2019-11-02 10:38:58 +01:00
} else {
SiteHealth . site _status . issues = {
'good' : 0 ,
'recommended' : 0 ,
'critical' : 0
} ;
}
if ( 0 < SiteHealth . site _status . direct . length ) {
$ . each ( SiteHealth . site _status . direct , function ( ) {
2020-09-15 14:29:22 +02:00
appendIssue ( this ) ;
2019-11-02 10:38:58 +01:00
} ) ;
}
if ( 0 < SiteHealth . site _status . async . length ) {
2020-12-10 14:06:04 +01:00
maybeRunNextAsyncTest ( ) ;
2019-11-02 10:38:58 +01:00
} else {
2020-09-15 14:29:22 +02:00
recalculateProgression ( ) ;
2019-11-02 10:38:58 +01:00
}
}
function getDirectorySizes ( ) {
var timestamp = ( new Date ( ) . getTime ( ) ) ;
// After 3 seconds announce that we're still waiting for directory sizes.
var timeout = window . setTimeout ( function ( ) {
wp . a11y . speak ( _ _ ( 'Please wait...' ) ) ;
} , 3000 ) ;
2020-12-10 14:06:04 +01:00
wp . apiRequest ( {
path : '/wp-site-health/v1/directory-sizes'
2019-11-02 10:38:58 +01:00
} ) . done ( function ( response ) {
2020-12-10 14:06:04 +01:00
updateDirSizes ( response || { } ) ;
2019-11-02 10:38:58 +01:00
} ) . always ( function ( ) {
var delay = ( new Date ( ) . getTime ( ) ) - timestamp ;
$ ( '.health-check-wp-paths-sizes.spinner' ) . css ( 'visibility' , 'hidden' ) ;
2020-09-15 14:29:22 +02:00
recalculateProgression ( ) ;
2019-11-02 10:38:58 +01:00
2020-12-10 14:06:04 +01:00
if ( delay > 3000 ) {
2020-05-06 17:23:38 +02:00
/ *
* We have announced that we ' re waiting .
* Announce that we ' re ready after giving at least 3 seconds
* for the first announcement to be read out , or the two may collide .
* /
2019-11-02 10:38:58 +01:00
if ( delay > 6000 ) {
delay = 0 ;
} else {
delay = 6500 - delay ;
}
window . setTimeout ( function ( ) {
wp . a11y . speak ( _ _ ( 'All site health tests have finished running.' ) ) ;
} , delay ) ;
} else {
// Cancel the announcement.
window . clearTimeout ( timeout ) ;
}
$ ( document ) . trigger ( 'site-health-info-dirsizes-done' ) ;
} ) ;
}
function updateDirSizes ( data ) {
var copyButton = $ ( 'button.button.copy-button' ) ;
2020-05-06 17:23:38 +02:00
var clipboardText = copyButton . attr ( 'data-clipboard-text' ) ;
2019-11-02 10:38:58 +01:00
$ . each ( data , function ( name , value ) {
var text = value . debug || value . size ;
if ( typeof text !== 'undefined' ) {
2020-05-06 17:23:38 +02:00
clipboardText = clipboardText . replace ( name + ': loading...' , name + ': ' + text ) ;
2019-11-02 10:38:58 +01:00
}
} ) ;
2020-05-06 17:23:38 +02:00
copyButton . attr ( 'data-clipboard-text' , clipboardText ) ;
2019-11-02 10:38:58 +01:00
pathsSizesSection . find ( 'td[class]' ) . each ( function ( i , element ) {
var td = $ ( element ) ;
var name = td . attr ( 'class' ) ;
if ( data . hasOwnProperty ( name ) && data [ name ] . size ) {
td . text ( data [ name ] . size ) ;
}
} ) ;
}
if ( isDebugTab ) {
if ( pathsSizesSection . length ) {
getDirectorySizes ( ) ;
} else {
2020-09-15 14:29:22 +02:00
recalculateProgression ( ) ;
2019-11-02 10:38:58 +01:00
}
}
} ) ;