diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-admin/js/updates.js --- a/wp/wp-admin/js/updates.js Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-admin/js/updates.js Tue Dec 15 13:49:49 2020 +0100 @@ -11,8 +11,7 @@ * @param {jQuery} $ jQuery object. * @param {object} wp WP object. * @param {object} settings WP Updates settings. - * @param {string} settings.ajax_nonce AJAX nonce. - * @param {object} settings.l10n Translation strings. + * @param {string} settings.ajax_nonce Ajax nonce. * @param {object=} settings.plugins Base names of plugins in their different states. * @param {Array} settings.plugins.all Base names of all plugins. * @param {Array} settings.plugins.active Base names of active plugins. @@ -27,7 +26,10 @@ * @param {number} settings.totals.count Holds the amount of available updates. */ (function( $, wp, settings ) { - var $document = $( document ); + var $document = $( document ), + __ = wp.i18n.__, + _x = wp.i18n._x, + sprintf = wp.i18n.sprintf; wp = wp || {}; @@ -41,6 +43,81 @@ wp.updates = {}; /** + * Removed in 5.5.0, needed for back-compatibility. + * + * @since 4.2.0 + * @deprecated 5.5.0 + * + * @type {object} + */ + wp.updates.l10n = { + searchResults: '', + searchResultsLabel: '', + noPlugins: '', + noItemsSelected: '', + updating: '', + pluginUpdated: '', + themeUpdated: '', + update: '', + updateNow: '', + pluginUpdateNowLabel: '', + updateFailedShort: '', + updateFailed: '', + pluginUpdatingLabel: '', + pluginUpdatedLabel: '', + pluginUpdateFailedLabel: '', + updatingMsg: '', + updatedMsg: '', + updateCancel: '', + beforeunload: '', + installNow: '', + pluginInstallNowLabel: '', + installing: '', + pluginInstalled: '', + themeInstalled: '', + installFailedShort: '', + installFailed: '', + pluginInstallingLabel: '', + themeInstallingLabel: '', + pluginInstalledLabel: '', + themeInstalledLabel: '', + pluginInstallFailedLabel: '', + themeInstallFailedLabel: '', + installingMsg: '', + installedMsg: '', + importerInstalledMsg: '', + aysDelete: '', + aysDeleteUninstall: '', + aysBulkDelete: '', + aysBulkDeleteThemes: '', + deleting: '', + deleteFailed: '', + pluginDeleted: '', + themeDeleted: '', + livePreview: '', + activatePlugin: '', + activateTheme: '', + activatePluginLabel: '', + activateThemeLabel: '', + activateImporter: '', + activateImporterLabel: '', + unknownError: '', + connectionError: '', + nonceError: '', + pluginsFound: '', + noPluginsFound: '', + autoUpdatesEnable: '', + autoUpdatesEnabling: '', + autoUpdatesEnabled: '', + autoUpdatesDisable: '', + autoUpdatesDisabling: '', + autoUpdatesDisabled: '', + autoUpdatesError: '' + }; + + wp.updates.l10n = window.wp.deprecateL10nObject( 'wp.updates.l10n', wp.updates.l10n ); + + /** * User nonce for ajax calls. * * @since 4.2.0 @@ -50,15 +127,6 @@ wp.updates.ajaxNonce = settings.ajax_nonce; /** - * Localized strings. - * - * @since 4.2.0 - * - * @type {object} - */ - wp.updates.l10n = settings.l10n; - - /** * Current search term. * * @since 4.6.0 @@ -157,7 +225,7 @@ * * @since 4.6.0 * - * @param {object} data + * @param {Object} data * @param {*=} data.selector Optional. Selector of an element to be replaced with the admin notice. * @param {string=} data.id Optional. Unique id that will be used as the notice's id attribute. * @param {string=} data.className Optional. Class names that will be used in the admin notice. @@ -201,7 +269,7 @@ * @since 4.6.0 * * @param {string} action The type of Ajax request ('update-plugin', 'install-theme', etc). - * @param {object} data Data that needs to be passed to the ajax callback. + * @param {Object} data Data that needs to be passed to the ajax callback. * @return {$.promise} A jQuery promise that represents the request, * decorated with an abort() method. */ @@ -250,8 +318,8 @@ * * @since 4.6.0 * - * @param {object} response - * @param {array=} response.debug Optional. Debug information. + * @param {Object} response + * @param {Array=} response.debug Optional. Debug information. * @param {string=} response.errorCode Optional. Error code for an error that occurred. */ wp.updates.ajaxAlways = function( response ) { @@ -362,7 +430,7 @@ * @since 4.2.0 * @since 4.6.0 More accurately named `updatePlugin`. * - * @param {object} args Arguments. + * @param {Object} args Arguments. * @param {string} args.plugin Plugin basename. * @param {string} args.slug Plugin slug. * @param {updatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.updatePluginSuccess @@ -381,23 +449,31 @@ if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) { $updateRow = $( 'tr[data-plugin="' + args.plugin + '"]' ); $message = $updateRow.find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' ); - message = wp.updates.l10n.pluginUpdatingLabel.replace( '%s', $updateRow.find( '.plugin-title strong' ).text() ); + message = sprintf( + /* translators: %s: Plugin name and version. */ + _x( 'Updating %s...', 'plugin' ), + $updateRow.find( '.plugin-title strong' ).text() + ); } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { $card = $( '.plugin-card-' + args.slug ); $message = $card.find( '.update-now' ).addClass( 'updating-message' ); - message = wp.updates.l10n.pluginUpdatingLabel.replace( '%s', $message.data( 'name' ) ); + message = sprintf( + /* translators: %s: Plugin name and version. */ + _x( 'Updating %s...', 'plugin' ), + $message.data( 'name' ) + ); // Remove previous error messages, if any. $card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove(); } - if ( $message.html() !== wp.updates.l10n.updating ) { + if ( $message.html() !== __( 'Updating...' ) ) { $message.data( 'originaltext', $message.html() ); } $message .attr( 'aria-label', message ) - .text( wp.updates.l10n.updating ); + .text( __( 'Updating...' ) ); $document.trigger( 'wp-plugin-updating', args ); @@ -409,8 +485,9 @@ * * @since 4.2.0 * @since 4.6.0 More accurately named `updatePluginSuccess`. + * @since 5.5.0 Auto-update "time to next update" text cleared. * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the plugin to be updated. * @param {string} response.plugin Basename of the plugin to be updated. * @param {string} response.pluginName Name of the plugin to be updated. @@ -431,6 +508,9 @@ // Update the version number in the row. newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion ); $pluginRow.find( '.plugin-version-author-uri' ).html( newText ); + + // Clear the "time to next auto-update" text. + $pluginRow.find( '.auto-update-time' ).empty(); } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' ) .removeClass( 'updating-message' ) @@ -438,10 +518,17 @@ } $updateMessage - .attr( 'aria-label', wp.updates.l10n.pluginUpdatedLabel.replace( '%s', response.pluginName ) ) - .text( wp.updates.l10n.pluginUpdated ); - - wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( '%s updated!', 'plugin' ), + response.pluginName + ) + ) + .text( _x( 'Updated!', 'plugin' ) ); + + wp.a11y.speak( __( 'Update completed successfully.' ) ); wp.updates.decrementCount( 'plugin' ); @@ -454,7 +541,7 @@ * @since 4.2.0 * @since 4.6.0 More accurately named `updatePluginError`. * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the plugin to be updated. * @param {string} response.plugin Basename of the plugin to be updated. * @param {string=} response.pluginName Optional. Name of the plugin to be updated. @@ -472,7 +559,11 @@ return; } - errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage ); + errorMessage = sprintf( + /* translators: %s: Error string for a failed update. */ + __( 'Update failed: %s' ), + response.errorMessage + ); if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) { if ( response.plugin ) { @@ -484,7 +575,14 @@ if ( response.pluginName ) { $message.find( 'p' ) - .attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( '%s update failed.', 'plugin' ), + response.pluginName + ) + ); } else { $message.find( 'p' ).removeAttr( 'aria-label' ); } @@ -497,11 +595,19 @@ } ) ); $card.find( '.update-now' ) - .text( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' ); + .text( __( 'Update failed.' ) ) + .removeClass( 'updating-message' ); if ( response.pluginName ) { $card.find( '.update-now' ) - .attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( '%s update failed.', 'plugin' ), + response.pluginName + ) + ); } else { $card.find( '.update-now' ).removeAttr( 'aria-label' ); } @@ -516,7 +622,7 @@ $card.find( '.update-now' ) .attr( 'aria-label', false ) - .text( wp.updates.l10n.updateNow ); + .text( __( 'Update Now' ) ); }, 200 ); } ); } @@ -531,7 +637,7 @@ * * @since 4.6.0 * - * @param {object} args Arguments. + * @param {Object} args Arguments. * @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository. * @param {installPluginSuccess=} args.success Optional. Success callback. Default: wp.updates.installPluginSuccess * @param {installPluginError=} args.error Optional. Error callback. Default: wp.updates.installPluginError @@ -551,16 +657,23 @@ $message = $( '[data-slug="' + args.slug + '"]' ); } - if ( $message.html() !== wp.updates.l10n.installing ) { + if ( $message.html() !== __( 'Installing...' ) ) { $message.data( 'originaltext', $message.html() ); } $message .addClass( 'updating-message' ) - .attr( 'aria-label', wp.updates.l10n.pluginInstallingLabel.replace( '%s', $message.data( 'name' ) ) ) - .text( wp.updates.l10n.installing ); - - wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( 'Installing %s...', 'plugin' ), + $message.data( 'name' ) + ) + ) + .text( __( 'Installing...' ) ); + + wp.a11y.speak( __( 'Installing... please wait.' ) ); // Remove previous error messages, if any. $card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove(); @@ -575,7 +688,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the installed plugin. * @param {string} response.pluginName Name of the installed plugin. * @param {string} response.activateUrl URL to activate the just installed plugin. @@ -586,10 +699,17 @@ $message .removeClass( 'updating-message' ) .addClass( 'updated-message installed button-disabled' ) - .attr( 'aria-label', wp.updates.l10n.pluginInstalledLabel.replace( '%s', response.pluginName ) ) - .text( wp.updates.l10n.pluginInstalled ); - - wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( '%s installed!', 'plugin' ), + response.pluginName + ) + ) + .text( _x( 'Installed!', 'plugin' ) ); + + wp.a11y.speak( __( 'Installation completed successfully.' ) ); $document.trigger( 'wp-plugin-install-success', response ); @@ -597,10 +717,33 @@ setTimeout( function() { // Transform the 'Install' button into an 'Activate' button. - $message.removeClass( 'install-now installed button-disabled updated-message' ).addClass( 'activate-now button-primary' ) - .attr( 'href', response.activateUrl ) - .attr( 'aria-label', wp.updates.l10n.activatePluginLabel.replace( '%s', response.pluginName ) ) - .text( wp.updates.l10n.activatePlugin ); + $message.removeClass( 'install-now installed button-disabled updated-message' ) + .addClass( 'activate-now button-primary' ) + .attr( 'href', response.activateUrl ); + + if ( 'plugins-network' === pagenow ) { + $message + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Network Activate %s', 'plugin' ), + response.pluginName + ) + ) + .text( __( 'Network Activate' ) ); + } else { + $message + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Activate %s', 'plugin' ), + response.pluginName + ) + ) + .text( __( 'Activate' ) ); + } }, 1000 ); } }; @@ -610,7 +753,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the plugin to be installed. * @param {string=} response.pluginName Optional. Name of the plugin to be installed. * @param {string} response.errorCode Error code for the error that occurred. @@ -629,7 +772,11 @@ return; } - errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ); + errorMessage = sprintf( + /* translators: %s: Error string for a failed installation. */ + __( 'Installation failed: %s' ), + response.errorMessage + ); $card .addClass( 'plugin-card-update-failed' ) @@ -647,8 +794,15 @@ $button .removeClass( 'updating-message' ).addClass( 'button-disabled' ) - .attr( 'aria-label', wp.updates.l10n.pluginInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) ) - .text( wp.updates.l10n.installFailedShort ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( '%s installation failed', 'plugin' ), + $button.data( 'name' ) + ) + ) + .text( __( 'Installation failed.' ) ); wp.a11y.speak( errorMessage, 'assertive' ); @@ -660,7 +814,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the installed plugin. * @param {string} response.pluginName Name of the installed plugin. * @param {string} response.activateUrl URL to activate the just installed plugin. @@ -669,7 +823,11 @@ wp.updates.addAdminNotice( { id: 'install-success', className: 'notice-success is-dismissible', - message: wp.updates.l10n.importerInstalledMsg.replace( '%s', response.activateUrl + '&from=import' ) + message: sprintf( + /* translators: %s: Activation URL. */ + __( 'Importer installed successfully. Run importer' ), + response.activateUrl + '&from=import' + ) } ); $( '[data-slug="' + response.slug + '"]' ) @@ -677,11 +835,15 @@ .addClass( 'activate-now' ) .attr({ 'href': response.activateUrl + '&from=import', - 'aria-label': wp.updates.l10n.activateImporterLabel.replace( '%s', response.pluginName ) + 'aria-label':sprintf( + /* translators: %s: Importer name. */ + __( 'Run %s' ), + response.pluginName + ) }) - .text( wp.updates.l10n.activateImporter ); - - wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' ); + .text( __( 'Run Importer' ) ); + + wp.a11y.speak( __( 'Installation completed successfully.' ) ); $document.trigger( 'wp-importer-install-success', response ); }; @@ -691,14 +853,18 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the plugin to be installed. * @param {string=} response.pluginName Optional. Name of the plugin to be installed. * @param {string} response.errorCode Error code for the error that occurred. * @param {string} response.errorMessage The error that occurred. */ wp.updates.installImporterError = function( response ) { - var errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ), + var errorMessage = sprintf( + /* translators: %s: Error string for a failed installation. */ + __( 'Installation failed: %s' ), + response.errorMessage + ), $installLink = $( '[data-slug="' + response.slug + '"]' ), pluginName = $installLink.data( 'name' ); @@ -718,8 +884,15 @@ $installLink .removeClass( 'updating-message' ) - .text( wp.updates.l10n.installNow ) - .attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Install %s now', 'plugin' ), + pluginName + ) + ) + .text( __( 'Install Now' ) ); wp.a11y.speak( errorMessage, 'assertive' ); @@ -731,7 +904,7 @@ * * @since 4.6.0 * - * @param {object} args Arguments. + * @param {Object} args Arguments. * @param {string} args.plugin Basename of the plugin to be deleted. * @param {string} args.slug Slug of the plugin to be deleted. * @param {deletePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.deletePluginSuccess @@ -747,13 +920,13 @@ error: wp.updates.deletePluginError }, args ); - if ( $link.html() !== wp.updates.l10n.deleting ) { + if ( $link.html() !== __( 'Deleting...' ) ) { $link .data( 'originaltext', $link.html() ) - .text( wp.updates.l10n.deleting ); + .text( __( 'Deleting...' ) ); } - wp.a11y.speak( wp.updates.l10n.deleting, 'polite' ); + wp.a11y.speak( __( 'Deleting...' ) ); $document.trigger( 'wp-plugin-deleting', args ); @@ -843,12 +1016,12 @@ $views.find( '.all' ).remove(); if ( ! $form.find( 'tr.no-items' ).length ) { - $form.find( '#the-list' ).append( '' + wp.updates.l10n.noPlugins + '' ); + $form.find( '#the-list' ).append( '' + __( 'No plugins are currently available.' ) + '' ); } } } ); - wp.a11y.speak( wp.updates.l10n.pluginDeleted, 'polite' ); + wp.a11y.speak( _x( 'Deleted!', 'plugin' ) ); $document.trigger( 'wp-plugin-delete-success', response ); }; @@ -858,7 +1031,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the plugin to be deleted. * @param {string} response.plugin Base name of the plugin to be deleted * @param {string=} response.pluginName Optional. Name of the plugin to be deleted. @@ -915,7 +1088,7 @@ * * @since 4.6.0 * - * @param {object} args Arguments. + * @param {Object} args Arguments. * @param {string} args.slug Theme stylesheet. * @param {updateThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.updateThemeSuccess * @param {updateThemeError=} args.error Optional. Error callback. Default: wp.updates.updateThemeError @@ -953,12 +1126,12 @@ $notice = $notice.addClass( 'updating-message' ).find( 'p' ); } - if ( $notice.html() !== wp.updates.l10n.updating ) { + if ( $notice.html() !== __( 'Updating...' ) ) { $notice.data( 'originaltext', $notice.html() ); } - wp.a11y.speak( wp.updates.l10n.updatingMsg, 'polite' ); - $notice.text( wp.updates.l10n.updating ); + wp.a11y.speak( __( 'Updating... please wait.' ) ); + $notice.text( __( 'Updating...' ) ); $document.trigger( 'wp-theme-updating', args ); @@ -969,10 +1142,11 @@ * Updates the UI appropriately after a successful theme update. * * @since 4.6.0 + * @since 5.5.0 Auto-update "time to next update" text cleared. * - * @param {object} response + * @param {Object} response * @param {string} response.slug Slug of the theme to be updated. - * @param {object} response.theme Updated theme. + * @param {Object} response.theme Updated theme. * @param {string} response.oldVersion Old version of the theme. * @param {string} response.newVersion New version of the theme. */ @@ -981,7 +1155,7 @@ $theme = $( '[data-slug="' + response.slug + '"]' ), updatedMessage = { className: 'updated-message notice-success notice-alt', - message: wp.updates.l10n.themeUpdated + message: _x( 'Updated!', 'theme' ) }, $notice, newText; @@ -1002,19 +1176,23 @@ // Update the version number in the row. newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion ); $theme.find( '.theme-version-author-uri' ).html( newText ); + + // Clear the "time to next auto-update" text. + $theme.find( '.auto-update-time' ).empty(); } else { $notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) ); // Focus on Customize button after updating. if ( isModalOpen ) { $( '.load-customize:visible' ).focus(); + $( '.theme-info .theme-autoupdate' ).find( '.auto-update-time' ).empty(); } else { $theme.find( '.load-customize' ).focus(); } } wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) ); - wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' ); + wp.a11y.speak( __( 'Update completed successfully.' ) ); wp.updates.decrementCount( 'theme' ); @@ -1031,14 +1209,18 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the theme to be updated. * @param {string} response.errorCode Error code for the error that occurred. * @param {string} response.errorMessage The error that occurred. */ wp.updates.updateThemeError = function( response ) { var $theme = $( '[data-slug="' + response.slug + '"]' ), - errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage ), + errorMessage = sprintf( + /* translators: %s: Error string for a failed update. */ + __( 'Update failed: %s' ), + response.errorMessage + ), $notice; if ( ! wp.updates.isValidResponse( response, 'update' ) ) { @@ -1067,7 +1249,7 @@ message: errorMessage } ); - wp.a11y.speak( errorMessage, 'polite' ); + wp.a11y.speak( errorMessage ); $document.trigger( 'wp-theme-update-error', response ); }; @@ -1077,7 +1259,7 @@ * * @since 4.6.0 * - * @param {object} args + * @param {Object} args * @param {string} args.slug Theme stylesheet. * @param {installThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.installThemeSuccess * @param {installThemeError=} args.error Optional. Error callback. Default: wp.updates.installThemeError @@ -1094,14 +1276,22 @@ $message.addClass( 'updating-message' ); $message.parents( '.theme' ).addClass( 'focus' ); - if ( $message.html() !== wp.updates.l10n.installing ) { + if ( $message.html() !== __( 'Installing...' ) ) { $message.data( 'originaltext', $message.html() ); } $message - .text( wp.updates.l10n.installing ) - .attr( 'aria-label', wp.updates.l10n.themeInstallingLabel.replace( '%s', $message.data( 'name' ) ) ); - wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Theme name and version. */ + _x( 'Installing %s...', 'theme' ), + $message.data( 'name' ) + ) + ) + .text( __( 'Installing...' ) ); + + wp.a11y.speak( __( 'Installing... please wait.' ) ); // Remove previous error messages, if any. $( '.install-theme-info, [data-slug="' + args.slug + '"]' ).removeClass( 'theme-install-failed' ).find( '.notice.notice-error' ).remove(); @@ -1116,7 +1306,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the theme to be installed. * @param {string} response.customizeUrl URL to the Customizer for the just installed theme. * @param {string} response.activateUrl URL to activate the just installed theme. @@ -1130,10 +1320,17 @@ $message = $card.find( '.button-primary' ) .removeClass( 'updating-message' ) .addClass( 'updated-message disabled' ) - .attr( 'aria-label', wp.updates.l10n.themeInstalledLabel.replace( '%s', response.themeName ) ) - .text( wp.updates.l10n.themeInstalled ); - - wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Theme name and version. */ + _x( '%s installed!', 'theme' ), + response.themeName + ) + ) + .text( _x( 'Installed!', 'theme' ) ); + + wp.a11y.speak( __( 'Installation completed successfully.' ) ); setTimeout( function() { @@ -1143,9 +1340,31 @@ $message .attr( 'href', response.activateUrl ) .removeClass( 'theme-install updated-message disabled' ) - .addClass( 'activate' ) - .attr( 'aria-label', wp.updates.l10n.activateThemeLabel.replace( '%s', response.themeName ) ) - .text( wp.updates.l10n.activateTheme ); + .addClass( 'activate' ); + + if ( 'themes-network' === pagenow ) { + $message + .attr( + 'aria-label', + sprintf( + /* translators: %s: Theme name. */ + _x( 'Network Activate %s', 'theme' ), + response.themeName + ) + ) + .text( __( 'Network Enable' ) ); + } else { + $message + .attr( + 'aria-label', + sprintf( + /* translators: %s: Theme name. */ + _x( 'Activate %s', 'theme' ), + response.themeName + ) + ) + .text( __( 'Activate' ) ); + } } if ( response.customizeUrl ) { @@ -1155,7 +1374,7 @@ return $( '' ) .attr( 'href', response.customizeUrl ) .addClass( 'button load-customize' ) - .text( wp.updates.l10n.livePreview ); + .text( __( 'Live Preview' ) ); } ); } }, 1000 ); @@ -1166,14 +1385,18 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the theme to be installed. * @param {string} response.errorCode Error code for the error that occurred. * @param {string} response.errorMessage The error that occurred. */ wp.updates.installThemeError = function( response ) { var $card, $button, - errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ), + errorMessage = sprintf( + /* translators: %s: Error string for a failed installation. */ + __( 'Installation failed: %s' ), + response.errorMessage + ), $message = wp.updates.adminNotice( { className: 'update-message notice-error notice-alt', message: errorMessage @@ -1208,8 +1431,15 @@ $button .removeClass( 'updating-message' ) - .attr( 'aria-label', wp.updates.l10n.themeInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) ) - .text( wp.updates.l10n.installFailedShort ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Theme name and version. */ + _x( '%s installation failed', 'theme' ), + $button.data( 'name' ) + ) + ) + .text( __( 'Installation failed.' ) ); wp.a11y.speak( errorMessage, 'assertive' ); @@ -1221,7 +1451,7 @@ * * @since 4.6.0 * - * @param {object} args + * @param {Object} args * @param {string} args.slug Theme stylesheet. * @param {deleteThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.deleteThemeSuccess * @param {deleteThemeError=} args.error Optional. Error callback. Default: wp.updates.deleteThemeError @@ -1242,13 +1472,13 @@ error: wp.updates.deleteThemeError }, args ); - if ( $button && $button.html() !== wp.updates.l10n.deleting ) { + if ( $button && $button.html() !== __( 'Deleting...' ) ) { $button .data( 'originaltext', $button.html() ) - .text( wp.updates.l10n.deleting ); + .text( __( 'Deleting...' ) ); } - wp.a11y.speak( wp.updates.l10n.deleting, 'polite' ); + wp.a11y.speak( __( 'Deleting...' ) ); // Remove previous error messages, if any. $( '.theme-info .update-message' ).remove(); @@ -1263,7 +1493,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the theme that was deleted. */ wp.updates.deleteThemeSuccess = function( response ) { @@ -1311,7 +1541,7 @@ } ); } - wp.a11y.speak( wp.updates.l10n.themeDeleted, 'polite' ); + wp.a11y.speak( _x( 'Deleted!', 'theme' ) ); $document.trigger( 'wp-theme-delete-success', response ); }; @@ -1321,7 +1551,7 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.slug Slug of the theme to be deleted. * @param {string} response.errorCode Error code for the error that occurred. * @param {string} response.errorMessage The error that occurred. @@ -1331,7 +1561,11 @@ $button = $( '.theme-actions .delete-theme' ), updateRow = wp.template( 'item-update-row' ), $updateRow = $themeRow.siblings( '#' + response.slug + '-update' ), - errorMessage = wp.updates.l10n.deleteFailed.replace( '%s', response.errorMessage ), + errorMessage = sprintf( + /* translators: %s: Error string for a failed deletion. */ + __( 'Deletion failed: %s' ), + response.errorMessage + ), $message = wp.updates.adminNotice( { className: 'update-message notice-error notice-alt', message: errorMessage @@ -1372,9 +1606,9 @@ * @since 4.6.0 * @private * - * @param {object} data AJAX payload. + * @param {Object} data Ajax payload. * @param {string} action The type of request to perform. - * @return {object} The AJAX payload with the appropriate callbacks. + * @return {Object} The Ajax payload with the appropriate callbacks. */ wp.updates._addCallbacks = function( data, action ) { if ( 'import' === pagenow && 'install-plugin' === action ) { @@ -1566,7 +1800,7 @@ * * @since 4.2.0 * - * @param {object} response Ajax response. + * @param {Object} response Ajax response. * @param {string} action The type of request to perform. */ wp.updates.credentialError = function( response, action ) { @@ -1594,11 +1828,11 @@ * * @since 4.6.0 * - * @param {object} response Response from the server. + * @param {Object} response Response from the server. * @param {string} response.errorCode Error code for the error that occurred. * @param {string} response.errorMessage The error that occurred. * @param {string} action The type of request to perform. - * @returns {boolean} Whether there is an error that needs to be handled or not. + * @return {boolean} Whether there is an error that needs to be handled or not. */ wp.updates.maybeHandleCredentialError = function( response, action ) { if ( wp.updates.shouldRequestFilesystemCredentials && response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) { @@ -1610,11 +1844,11 @@ }; /** - * Validates an AJAX response to ensure it's a proper object. + * Validates an Ajax response to ensure it's a proper object. * * If the response deems to be invalid, an admin notice is being displayed. * - * @param {(object|string)} response Response from the server. + * @param {(Object|string)} response Response from the server. * @param {function=} response.always Optional. Callback for when the Deferred is resolved or rejected. * @param {string=} response.statusText Optional. Status message corresponding to the status code. * @param {string=} response.responseText Optional. Request response as text. @@ -1622,8 +1856,8 @@ * 'update' or 'install'. */ wp.updates.isValidResponse = function( response, action ) { - var error = wp.updates.l10n.unknownError, - errorMessage; + var error = __( 'Something went wrong.' ), + errorMessage; // Make sure the response is a valid data object and not a Promise object. if ( _.isObject( response ) && ! _.isFunction( response.always ) ) { @@ -1631,11 +1865,11 @@ } if ( _.isString( response ) && '-1' === response ) { - error = wp.updates.l10n.nonceError; + error = __( 'An error has occurred. Please reload the page and try again.' ); } else if ( _.isString( response ) ) { error = response; } else if ( 'undefined' !== typeof response.readyState && 0 === response.readyState ) { - error = wp.updates.l10n.connectionError; + error = __( 'Connection lost or the server is busy. Please try again later.' ); } else if ( _.isString( response.responseText ) && '' !== response.responseText ) { error = response.responseText; } else if ( _.isString( response.statusText ) ) { @@ -1644,15 +1878,18 @@ switch ( action ) { case 'update': - errorMessage = wp.updates.l10n.updateFailed; + /* translators: %s: Error string for a failed update. */ + errorMessage = __( 'Update failed: %s' ); break; case 'install': - errorMessage = wp.updates.l10n.installFailed; + /* translators: %s: Error string for a failed installation. */ + errorMessage = __( 'Installation failed: %s' ); break; case 'delete': - errorMessage = wp.updates.l10n.deleteFailed; + /* translators: %s: Error string for a failed deletion. */ + errorMessage = __( 'Deletion failed: %s' ); break; } @@ -1676,7 +1913,7 @@ .removeClass( 'updating-message' ) .removeAttr( 'aria-label' ) .prop( 'disabled', true ) - .text( wp.updates.l10n.updateFailedShort ); + .text( __( 'Update failed.' ) ); $( '.updating-message:not(.button):not(.thickbox)' ) .removeClass( 'updating-message notice-warning' ) @@ -1700,7 +1937,7 @@ */ wp.updates.beforeunload = function() { if ( wp.updates.ajaxLocked ) { - return wp.updates.l10n.beforeunload; + return __( 'Updates may not complete if you navigate away from this page.' ); } }; @@ -1813,14 +2050,28 @@ if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { if ( 'update-plugin' === job.action ) { - $message.attr( 'aria-label', wp.updates.l10n.pluginUpdateNowLabel.replace( '%s', $message.data( 'name' ) ) ); + $message.attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( 'Update %s now', 'plugin' ), + $message.data( 'name' ) + ) + ); } else if ( 'install-plugin' === job.action ) { - $message.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', $message.data( 'name' ) ) ); + $message.attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Install %s now', 'plugin' ), + $message.data( 'name' ) + ) + ); } } } - wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' ); + wp.a11y.speak( __( 'Update canceled.' ) ); } ); /** @@ -1896,9 +2147,9 @@ $message .removeClass( 'updating-message' ) - .text( wp.updates.l10n.installNow ); - - wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' ); + .text( __( 'Install Now' ) ); + + wp.a11y.speak( __( 'Update canceled.' ) ); } ); } @@ -1931,10 +2182,17 @@ $button .removeClass( 'updating-message' ) - .text( wp.updates.l10n.installNow ) - .attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) ); - - wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' ); + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Install %s now', 'plugin' ), + pluginName + ) + ) + .text( __( 'Install Now' ) ); + + wp.a11y.speak( __( 'Update canceled.' ) ); } ); } @@ -1954,11 +2212,26 @@ * @param {Event} event Event interface. */ $bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) { - var $pluginRow = $( event.target ).parents( 'tr' ); + var $pluginRow = $( event.target ).parents( 'tr' ), + confirmMessage; + + if ( $pluginRow.hasClass( 'is-uninstallable' ) ) { + confirmMessage = sprintf( + /* translators: %s: Plugin name. */ + __( 'Are you sure you want to delete %s and its data?' ), + $pluginRow.find( '.plugin-title strong' ).text() + ); + } else { + confirmMessage = sprintf( + /* translators: %s: Plugin name. */ + __( 'Are you sure you want to delete %s?' ), + $pluginRow.find( '.plugin-title strong' ).text() + ); + } event.preventDefault(); - if ( ! window.confirm( wp.updates.l10n.aysDeleteUninstall.replace( '%s', $pluginRow.find( '.plugin-title strong' ).text() ) ) ) { + if ( ! window.confirm( confirmMessage ) ) { return; } @@ -2005,11 +2278,16 @@ * @param {Event} event Event interface. */ $document.on( 'click', '.themes-php.network-admin a.delete', function( event ) { - var $themeRow = $( event.target ).parents( 'tr' ); + var $themeRow = $( event.target ).parents( 'tr' ), + confirmMessage = sprintf( + /* translators: %s: Theme name. */ + __( 'Are you sure you want to delete %s?' ), + $themeRow.find( '.theme-title strong' ).text() + ); event.preventDefault(); - if ( ! window.confirm( wp.updates.l10n.aysDelete.replace( '%s', $themeRow.find( '.theme-title strong' ).text() ) ) ) { + if ( ! window.confirm( confirmMessage ) ) { return; } @@ -2060,7 +2338,7 @@ return wp.updates.addAdminNotice( { id: 'no-items-selected', className: 'notice-error is-dismissible', - message: wp.updates.l10n.noItemsSelected + message: __( 'Please select at least one item to perform this action on.' ) } ); } @@ -2071,7 +2349,11 @@ break; case 'delete-selected': - if ( ! window.confirm( 'plugin' === type ? wp.updates.l10n.aysBulkDelete : wp.updates.l10n.aysBulkDeleteThemes ) ) { + var confirmMessage = 'plugin' === type ? + __( 'Are you sure you want to delete the selected plugins and their data?' ) : + __( 'Caution: These themes may be active on other sites in the network. Are you sure you want to proceed?' ); + + if ( ! window.confirm( confirmMessage ) ) { event.preventDefault(); return; } @@ -2208,7 +2490,7 @@ .append( $( '', { 'class': 'current', 'href': searchLocation, - 'text': wp.updates.l10n.searchResultsLabel + 'text': __( 'Search Results' ) } ) ); $( '.wp-filter .filter-links .current' ) @@ -2231,12 +2513,18 @@ delete wp.updates.searchRequest; if ( 0 === response.count ) { - wp.a11y.speak( wp.updates.l10n.noPluginsFound ); + wp.a11y.speak( __( 'You do not appear to have any plugins available at this time.' ) ); } else { - wp.a11y.speak( wp.updates.l10n.pluginsFound.replace( '%d', response.count ) ); + wp.a11y.speak( + sprintf( + /* translators: %s: Number of plugins. */ + __( 'Number of plugins found: %d' ), + response.count + ) + ); } } ); - }, 500 ) ); + }, 1000 ) ); if ( $pluginSearch.length ) { $pluginSearch.attr( 'aria-describedby', 'live-search-desc' ); @@ -2289,7 +2577,12 @@ wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) { // Can we just ditch this whole subtitle business? - var $subTitle = $( '' ).addClass( 'subtitle' ).html( wp.updates.l10n.searchResults.replace( '%s', _.escape( data.s ) ) ), + var $subTitle = $( '' ).addClass( 'subtitle' ).html( + sprintf( + /* translators: %s: Search query. */ + __( 'Search results for “%s”' ), + _.escape( data.s ) + ) ), $oldSubTitle = $( '.wrap .subtitle' ); if ( ! data.s.length ) { @@ -2306,9 +2599,15 @@ delete wp.updates.searchRequest; if ( 0 === response.count ) { - wp.a11y.speak( wp.updates.l10n.noPluginsFound ); + wp.a11y.speak( __( 'No plugins found. Try a different search.' ) ); } else { - wp.a11y.speak( wp.updates.l10n.pluginsFound.replace( '%d', response.count ) ); + wp.a11y.speak( + sprintf( + /* translators: %s: Number of plugins. */ + __( 'Number of plugins found: %d' ), + response.count + ) + ); } } ); }, 500 ) ); @@ -2416,7 +2715,7 @@ */ $( window ).on( 'message', function( event ) { var originalEvent = event.originalEvent, - expectedOrigin = document.location.protocol + '//' + document.location.hostname, + expectedOrigin = document.location.protocol + '//' + document.location.host, message; if ( originalEvent.origin !== expectedOrigin ) { @@ -2461,5 +2760,171 @@ * @since 4.2.0 */ $( window ).on( 'beforeunload', wp.updates.beforeunload ); + + /** + * Prevents the page form scrolling when activating auto-updates with the Spacebar key. + * + * @since 5.5.0 + */ + $document.on( 'keydown', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) { + if ( 32 === event.which ) { + event.preventDefault(); + } + } ); + + /** + * Click and keyup handler for enabling and disabling plugin and theme auto-updates. + * + * These controls can be either links or buttons. When JavaScript is enabled, + * we want them to behave like buttons. An ARIA role `button` is added via + * the JavaScript that targets elements with the CSS class `aria-button-if-js`. + * + * @since 5.5.0 + */ + $document.on( 'click keyup', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) { + var data, asset, type, $parent, + $toggler = $( this ), + action = $toggler.attr( 'data-wp-action' ), + $label = $toggler.find( '.label' ); + + if ( 'keyup' === event.type && 32 !== event.which ) { + return; + } + + if ( 'themes' !== pagenow ) { + $parent = $toggler.closest( '.column-auto-updates' ); + } else { + $parent = $toggler.closest( '.theme-autoupdate' ); + } + + event.preventDefault(); + + // Prevent multiple simultaneous requests. + if ( $toggler.attr( 'data-doing-ajax' ) === 'yes' ) { + return; + } + + $toggler.attr( 'data-doing-ajax', 'yes' ); + + switch ( pagenow ) { + case 'plugins': + case 'plugins-network': + type = 'plugin'; + asset = $toggler.closest( 'tr' ).attr( 'data-plugin' ); + break; + case 'themes-network': + type = 'theme'; + asset = $toggler.closest( 'tr' ).attr( 'data-slug' ); + break; + case 'themes': + type = 'theme'; + asset = $toggler.attr( 'data-slug' ); + break; + } + + // Clear any previous errors. + $parent.find( '.notice.notice-error' ).addClass( 'hidden' ); + + // Show loading status. + if ( 'enable' === action ) { + $label.text( __( 'Enabling...' ) ); + } else { + $label.text( __( 'Disabling...' ) ); + } + + $toggler.find( '.dashicons-update' ).removeClass( 'hidden' ); + + data = { + action: 'toggle-auto-updates', + _ajax_nonce: settings.ajax_nonce, + state: action, + type: type, + asset: asset + }; + + $.post( window.ajaxurl, data ) + .done( function( response ) { + var $enabled, $disabled, enabledNumber, disabledNumber, errorMessage, + href = $toggler.attr( 'href' ); + + if ( ! response.success ) { + // if WP returns 0 for response (which can happen in a few cases), + // output the general error message since we won't have response.data.error. + if ( response.data && response.data.error ) { + errorMessage = response.data.error; + } else { + errorMessage = __( 'The request could not be completed.' ); + } + + $parent.find( '.notice.notice-error' ).removeClass( 'hidden' ).find( 'p' ).text( errorMessage ); + wp.a11y.speak( errorMessage, 'assertive' ); + return; + } + + // Update the counts in the enabled/disabled views if on a screen + // with a list table. + if ( 'themes' !== pagenow ) { + $enabled = $( '.auto-update-enabled span' ); + $disabled = $( '.auto-update-disabled span' ); + enabledNumber = parseInt( $enabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0; + disabledNumber = parseInt( $disabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0; + + switch ( action ) { + case 'enable': + ++enabledNumber; + --disabledNumber; + break; + case 'disable': + --enabledNumber; + ++disabledNumber; + break; + } + + enabledNumber = Math.max( 0, enabledNumber ); + disabledNumber = Math.max( 0, disabledNumber ); + + $enabled.text( '(' + enabledNumber + ')' ); + $disabled.text( '(' + disabledNumber + ')' ); + } + + if ( 'enable' === action ) { + // The toggler control can be either a link or a button. + if ( $toggler[ 0 ].hasAttribute( 'href' ) ) { + href = href.replace( 'action=enable-auto-update', 'action=disable-auto-update' ); + $toggler.attr( 'href', href ); + } + $toggler.attr( 'data-wp-action', 'disable' ); + + $label.text( __( 'Disable auto-updates' ) ); + $parent.find( '.auto-update-time' ).removeClass( 'hidden' ); + wp.a11y.speak( __( 'Auto-updates enabled' ) ); + } else { + // The toggler control can be either a link or a button. + if ( $toggler[ 0 ].hasAttribute( 'href' ) ) { + href = href.replace( 'action=disable-auto-update', 'action=enable-auto-update' ); + $toggler.attr( 'href', href ); + } + $toggler.attr( 'data-wp-action', 'enable' ); + + $label.text( __( 'Enable auto-updates' ) ); + $parent.find( '.auto-update-time' ).addClass( 'hidden' ); + wp.a11y.speak( __( 'Auto-updates disabled' ) ); + } + + $document.trigger( 'wp-auto-update-setting-changed', { state: action, type: type, asset: asset } ); + } ) + .fail( function() { + $parent.find( '.notice.notice-error' ) + .removeClass( 'hidden' ) + .find( 'p' ) + .text( __( 'The request could not be completed.' ) ); + + wp.a11y.speak( __( 'The request could not be completed.' ), 'assertive' ); + } ) + .always( function() { + $toggler.removeAttr( 'data-doing-ajax' ).find( '.dashicons-update' ).addClass( 'hidden' ); + } ); + } + ); } ); })( jQuery, window.wp, window._wpUpdatesSettings );