diff options
Diffstat (limited to 'srcs/wordpress/wp-admin/js/updates.js')
| -rw-r--r-- | srcs/wordpress/wp-admin/js/updates.js | 2465 |
1 files changed, 2465 insertions, 0 deletions
diff --git a/srcs/wordpress/wp-admin/js/updates.js b/srcs/wordpress/wp-admin/js/updates.js new file mode 100644 index 0000000..ee5bf3c --- /dev/null +++ b/srcs/wordpress/wp-admin/js/updates.js @@ -0,0 +1,2465 @@ +/** + * Functions for ajaxified updates, deletions and installs inside the WordPress admin. + * + * @version 4.2.0 + * @output wp-admin/js/updates.js + */ + +/* global pagenow */ + +/** + * @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 {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. + * @param {Array} settings.plugins.inactive Base names of inactive plugins. + * @param {Array} settings.plugins.upgrade Base names of plugins with updates available. + * @param {Array} settings.plugins.recently_activated Base names of recently activated plugins. + * @param {object=} settings.themes Plugin/theme status information or null. + * @param {number} settings.themes.all Amount of all themes. + * @param {number} settings.themes.upgrade Amount of themes with updates available. + * @param {number} settings.themes.disabled Amount of disabled themes. + * @param {object=} settings.totals Combined information for available update counts. + * @param {number} settings.totals.count Holds the amount of available updates. + */ +(function( $, wp, settings ) { + var $document = $( document ); + + wp = wp || {}; + + /** + * The WP Updates object. + * + * @since 4.2.0 + * + * @namespace wp.updates + */ + wp.updates = {}; + + /** + * User nonce for ajax calls. + * + * @since 4.2.0 + * + * @type {string} + */ + 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 + * + * @type {string} + */ + wp.updates.searchTerm = ''; + + /** + * Whether filesystem credentials need to be requested from the user. + * + * @since 4.2.0 + * + * @type {bool} + */ + wp.updates.shouldRequestFilesystemCredentials = false; + + /** + * Filesystem credentials to be packaged along with the request. + * + * @since 4.2.0 + * @since 4.6.0 Added `available` property to indicate whether credentials have been provided. + * + * @type {Object} + * @property {Object} filesystemCredentials.ftp Holds FTP credentials. + * @property {string} filesystemCredentials.ftp.host FTP host. Default empty string. + * @property {string} filesystemCredentials.ftp.username FTP user name. Default empty string. + * @property {string} filesystemCredentials.ftp.password FTP password. Default empty string. + * @property {string} filesystemCredentials.ftp.connectionType Type of FTP connection. 'ssh', 'ftp', or 'ftps'. + * Default empty string. + * @property {Object} filesystemCredentials.ssh Holds SSH credentials. + * @property {string} filesystemCredentials.ssh.publicKey The public key. Default empty string. + * @property {string} filesystemCredentials.ssh.privateKey The private key. Default empty string. + * @property {string} filesystemCredentials.fsNonce Filesystem credentials form nonce. + * @property {bool} filesystemCredentials.available Whether filesystem credentials have been provided. + * Default 'false'. + */ + wp.updates.filesystemCredentials = { + ftp: { + host: '', + username: '', + password: '', + connectionType: '' + }, + ssh: { + publicKey: '', + privateKey: '' + }, + fsNonce: '', + available: false + }; + + /** + * Whether we're waiting for an Ajax request to complete. + * + * @since 4.2.0 + * @since 4.6.0 More accurately named `ajaxLocked`. + * + * @type {bool} + */ + wp.updates.ajaxLocked = false; + + /** + * Admin notice template. + * + * @since 4.6.0 + * + * @type {function} + */ + wp.updates.adminNotice = wp.template( 'wp-updates-admin-notice' ); + + /** + * Update queue. + * + * If the user tries to update a plugin while an update is + * already happening, it can be placed in this queue to perform later. + * + * @since 4.2.0 + * @since 4.6.0 More accurately named `queue`. + * + * @type {Array.object} + */ + wp.updates.queue = []; + + /** + * Holds a jQuery reference to return focus to when exiting the request credentials modal. + * + * @since 4.2.0 + * + * @type {jQuery} + */ + wp.updates.$elToReturnFocusToFromCredentialsModal = undefined; + + /** + * Adds or updates an admin notice. + * + * @since 4.6.0 + * + * @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. + * @param {string=} data.message Optional. The message displayed in the notice. + * @param {number=} data.successes Optional. The amount of successful operations. + * @param {number=} data.errors Optional. The amount of failed operations. + * @param {Array=} data.errorMessages Optional. Error messages of failed operations. + * + */ + wp.updates.addAdminNotice = function( data ) { + var $notice = $( data.selector ), + $headerEnd = $( '.wp-header-end' ), + $adminNotice; + + delete data.selector; + $adminNotice = wp.updates.adminNotice( data ); + + // Check if this admin notice already exists. + if ( ! $notice.length ) { + $notice = $( '#' + data.id ); + } + + if ( $notice.length ) { + $notice.replaceWith( $adminNotice ); + } else if ( $headerEnd.length ) { + $headerEnd.after( $adminNotice ); + } else { + if ( 'customize' === pagenow ) { + $( '.customize-themes-notifications' ).append( $adminNotice ); + } else { + $( '.wrap' ).find( '> h1' ).after( $adminNotice ); + } + } + + $document.trigger( 'wp-updates-notice-added' ); + }; + + /** + * Handles Ajax requests to WordPress. + * + * @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. + * @return {$.promise} A jQuery promise that represents the request, + * decorated with an abort() method. + */ + wp.updates.ajax = function( action, data ) { + var options = {}; + + if ( wp.updates.ajaxLocked ) { + wp.updates.queue.push( { + action: action, + data: data + } ); + + // Return a Deferred object so callbacks can always be registered. + return $.Deferred(); + } + + wp.updates.ajaxLocked = true; + + if ( data.success ) { + options.success = data.success; + delete data.success; + } + + if ( data.error ) { + options.error = data.error; + delete data.error; + } + + options.data = _.extend( data, { + action: action, + _ajax_nonce: wp.updates.ajaxNonce, + _fs_nonce: wp.updates.filesystemCredentials.fsNonce, + username: wp.updates.filesystemCredentials.ftp.username, + password: wp.updates.filesystemCredentials.ftp.password, + hostname: wp.updates.filesystemCredentials.ftp.hostname, + connection_type: wp.updates.filesystemCredentials.ftp.connectionType, + public_key: wp.updates.filesystemCredentials.ssh.publicKey, + private_key: wp.updates.filesystemCredentials.ssh.privateKey + } ); + + return wp.ajax.send( options ).always( wp.updates.ajaxAlways ); + }; + + /** + * Actions performed after every Ajax request. + * + * @since 4.6.0 + * + * @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 ) { + if ( ! response.errorCode || 'unable_to_connect_to_filesystem' !== response.errorCode ) { + wp.updates.ajaxLocked = false; + wp.updates.queueChecker(); + } + + if ( 'undefined' !== typeof response.debug && window.console && window.console.log ) { + _.map( response.debug, function( message ) { + // Remove all HTML tags and write a message to the console. + window.console.log( wp.sanitize.stripTagsAndEncodeText( message ) ); + } ); + } + }; + + /** + * Refreshes update counts everywhere on the screen. + * + * @since 4.7.0 + */ + wp.updates.refreshCount = function() { + var $adminBarUpdates = $( '#wp-admin-bar-updates' ), + $dashboardNavMenuUpdateCount = $( 'a[href="update-core.php"] .update-plugins' ), + $pluginsNavMenuUpdateCount = $( 'a[href="plugins.php"] .update-plugins' ), + $appearanceNavMenuUpdateCount = $( 'a[href="themes.php"] .update-plugins' ), + itemCount; + + $adminBarUpdates.find( '.ab-item' ).removeAttr( 'title' ); + $adminBarUpdates.find( '.ab-label' ).text( settings.totals.counts.total ); + + // Remove the update count from the toolbar if it's zero. + if ( 0 === settings.totals.counts.total ) { + $adminBarUpdates.find( '.ab-label' ).parents( 'li' ).remove(); + } + + // Update the "Updates" menu item. + $dashboardNavMenuUpdateCount.each( function( index, element ) { + element.className = element.className.replace( /count-\d+/, 'count-' + settings.totals.counts.total ); + } ); + if ( settings.totals.counts.total > 0 ) { + $dashboardNavMenuUpdateCount.find( '.update-count' ).text( settings.totals.counts.total ); + } else { + $dashboardNavMenuUpdateCount.remove(); + } + + // Update the "Plugins" menu item. + $pluginsNavMenuUpdateCount.each( function( index, element ) { + element.className = element.className.replace( /count-\d+/, 'count-' + settings.totals.counts.plugins ); + } ); + if ( settings.totals.counts.total > 0 ) { + $pluginsNavMenuUpdateCount.find( '.plugin-count' ).text( settings.totals.counts.plugins ); + } else { + $pluginsNavMenuUpdateCount.remove(); + } + + // Update the "Appearance" menu item. + $appearanceNavMenuUpdateCount.each( function( index, element ) { + element.className = element.className.replace( /count-\d+/, 'count-' + settings.totals.counts.themes ); + } ); + if ( settings.totals.counts.total > 0 ) { + $appearanceNavMenuUpdateCount.find( '.theme-count' ).text( settings.totals.counts.themes ); + } else { + $appearanceNavMenuUpdateCount.remove(); + } + + // Update list table filter navigation. + if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) { + itemCount = settings.totals.counts.plugins; + } else if ( 'themes' === pagenow || 'themes-network' === pagenow ) { + itemCount = settings.totals.counts.themes; + } + + if ( itemCount > 0 ) { + $( '.subsubsub .upgrade .count' ).text( '(' + itemCount + ')' ); + } else { + $( '.subsubsub .upgrade' ).remove(); + $( '.subsubsub li:last' ).html( function() { return $( this ).children(); } ); + } + }; + + /** + * Decrements the update counts throughout the various menus. + * + * This includes the toolbar, the "Updates" menu item and the menu items + * for plugins and themes. + * + * @since 3.9.0 + * + * @param {string} type The type of item that was updated or deleted. + * Can be 'plugin', 'theme'. + */ + wp.updates.decrementCount = function( type ) { + settings.totals.counts.total = Math.max( --settings.totals.counts.total, 0 ); + + if ( 'plugin' === type ) { + settings.totals.counts.plugins = Math.max( --settings.totals.counts.plugins, 0 ); + } else if ( 'theme' === type ) { + settings.totals.counts.themes = Math.max( --settings.totals.counts.themes, 0 ); + } + + wp.updates.refreshCount( type ); + }; + + /** + * Sends an Ajax request to the server to update a plugin. + * + * @since 4.2.0 + * @since 4.6.0 More accurately named `updatePlugin`. + * + * @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 + * @param {updatePluginError=} args.error Optional. Error callback. Default: wp.updates.updatePluginError + * @return {$.promise} A jQuery promise that represents the request, + * decorated with an abort() method. + */ + wp.updates.updatePlugin = function( args ) { + var $updateRow, $card, $message, message; + + args = _.extend( { + success: wp.updates.updatePluginSuccess, + error: wp.updates.updatePluginError + }, args ); + + 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() ); + } 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' ) ); + + // Remove previous error messages, if any. + $card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove(); + } + + if ( $message.html() !== wp.updates.l10n.updating ) { + $message.data( 'originaltext', $message.html() ); + } + + $message + .attr( 'aria-label', message ) + .text( wp.updates.l10n.updating ); + + $document.trigger( 'wp-plugin-updating', args ); + + return wp.updates.ajax( 'update-plugin', args ); + }; + + /** + * Updates the UI appropriately after a successful plugin update. + * + * @since 4.2.0 + * @since 4.6.0 More accurately named `updatePluginSuccess`. + * + * @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. + * @param {string} response.oldVersion Old version of the plugin. + * @param {string} response.newVersion New version of the plugin. + */ + wp.updates.updatePluginSuccess = function( response ) { + var $pluginRow, $updateMessage, newText; + + if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) { + $pluginRow = $( 'tr[data-plugin="' + response.plugin + '"]' ) + .removeClass( 'update' ) + .addClass( 'updated' ); + $updateMessage = $pluginRow.find( '.update-message' ) + .removeClass( 'updating-message notice-warning' ) + .addClass( 'updated-message notice-success' ).find( 'p' ); + + // 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 ); + } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { + $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' ) + .removeClass( 'updating-message' ) + .addClass( 'button-disabled updated-message' ); + } + + $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' ); + + wp.updates.decrementCount( 'plugin' ); + + $document.trigger( 'wp-plugin-update-success', response ); + }; + + /** + * Updates the UI appropriately after a failed plugin update. + * + * @since 4.2.0 + * @since 4.6.0 More accurately named `updatePluginError`. + * + * @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. + * @param {string} response.errorCode Error code for the error that occurred. + * @param {string} response.errorMessage The error that occurred. + */ + wp.updates.updatePluginError = function( response ) { + var $card, $message, errorMessage; + + if ( ! wp.updates.isValidResponse( response, 'update' ) ) { + return; + } + + if ( wp.updates.maybeHandleCredentialError( response, 'update-plugin' ) ) { + return; + } + + errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage ); + + if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) { + if ( response.plugin ) { + $message = $( 'tr[data-plugin="' + response.plugin + '"]' ).find( '.update-message' ); + } else { + $message = $( 'tr[data-slug="' + response.slug + '"]' ).find( '.update-message' ); + } + $message.removeClass( 'updating-message notice-warning' ).addClass( 'notice-error' ).find( 'p' ).html( errorMessage ); + + if ( response.pluginName ) { + $message.find( 'p' ) + .attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) ); + } else { + $message.find( 'p' ).removeAttr( 'aria-label' ); + } + } else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) { + $card = $( '.plugin-card-' + response.slug ) + .addClass( 'plugin-card-update-failed' ) + .append( wp.updates.adminNotice( { + className: 'update-message notice-error notice-alt is-dismissible', + message: errorMessage + } ) ); + + $card.find( '.update-now' ) + .text( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' ); + + if ( response.pluginName ) { + $card.find( '.update-now' ) + .attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) ); + } else { + $card.find( '.update-now' ).removeAttr( 'aria-label' ); + } + + $card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() { + + // Use same delay as the total duration of the notice fadeTo + slideUp animation. + setTimeout( function() { + $card + .removeClass( 'plugin-card-update-failed' ) + .find( '.column-name a' ).focus(); + + $card.find( '.update-now' ) + .attr( 'aria-label', false ) + .text( wp.updates.l10n.updateNow ); + }, 200 ); + } ); + } + + wp.a11y.speak( errorMessage, 'assertive' ); + + $document.trigger( 'wp-plugin-update-error', response ); + }; + + /** + * Sends an Ajax request to the server to install a plugin. + * + * @since 4.6.0 + * + * @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 + * @return {$.promise} A jQuery promise that represents the request, + * decorated with an abort() method. + */ + wp.updates.installPlugin = function( args ) { + var $card = $( '.plugin-card-' + args.slug ), + $message = $card.find( '.install-now' ); + + args = _.extend( { + success: wp.updates.installPluginSuccess, + error: wp.updates.installPluginError + }, args ); + + if ( 'import' === pagenow ) { + $message = $( '[data-slug="' + args.slug + '"]' ); + } + + if ( $message.html() !== wp.updates.l10n.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' ); + + // Remove previous error messages, if any. + $card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove(); + + $document.trigger( 'wp-plugin-installing', args ); + + return wp.updates.ajax( 'install-plugin', args ); + }; + + /** + * Updates the UI appropriately after a successful plugin install. + * + * @since 4.6.0 + * + * @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. + */ + wp.updates.installPluginSuccess = function( response ) { + var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' ); + + $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' ); + + $document.trigger( 'wp-plugin-install-success', response ); + + if ( response.activateUrl ) { + 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 ); + }, 1000 ); + } + }; + + /** + * Updates the UI appropriately after a failed plugin install. + * + * @since 4.6.0 + * + * @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.installPluginError = function( response ) { + var $card = $( '.plugin-card-' + response.slug ), + $button = $card.find( '.install-now' ), + errorMessage; + + if ( ! wp.updates.isValidResponse( response, 'install' ) ) { + return; + } + + if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) { + return; + } + + errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ); + + $card + .addClass( 'plugin-card-update-failed' ) + .append( '<div class="notice notice-error notice-alt is-dismissible"><p>' + errorMessage + '</p></div>' ); + + $card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() { + + // Use same delay as the total duration of the notice fadeTo + slideUp animation. + setTimeout( function() { + $card + .removeClass( 'plugin-card-update-failed' ) + .find( '.column-name a' ).focus(); + }, 200 ); + } ); + + $button + .removeClass( 'updating-message' ).addClass( 'button-disabled' ) + .attr( 'aria-label', wp.updates.l10n.pluginInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) ) + .text( wp.updates.l10n.installFailedShort ); + + wp.a11y.speak( errorMessage, 'assertive' ); + + $document.trigger( 'wp-plugin-install-error', response ); + }; + + /** + * Updates the UI appropriately after a successful importer install. + * + * @since 4.6.0 + * + * @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. + */ + wp.updates.installImporterSuccess = function( response ) { + wp.updates.addAdminNotice( { + id: 'install-success', + className: 'notice-success is-dismissible', + message: wp.updates.l10n.importerInstalledMsg.replace( '%s', response.activateUrl + '&from=import' ) + } ); + + $( '[data-slug="' + response.slug + '"]' ) + .removeClass( 'install-now updating-message' ) + .addClass( 'activate-now' ) + .attr({ + 'href': response.activateUrl + '&from=import', + 'aria-label': wp.updates.l10n.activateImporterLabel.replace( '%s', response.pluginName ) + }) + .text( wp.updates.l10n.activateImporter ); + + wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' ); + + $document.trigger( 'wp-importer-install-success', response ); + }; + + /** + * Updates the UI appropriately after a failed importer install. + * + * @since 4.6.0 + * + * @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 ), + $installLink = $( '[data-slug="' + response.slug + '"]' ), + pluginName = $installLink.data( 'name' ); + + if ( ! wp.updates.isValidResponse( response, 'install' ) ) { + return; + } + + if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) { + return; + } + + wp.updates.addAdminNotice( { + id: response.errorCode, + className: 'notice-error is-dismissible', + message: errorMessage + } ); + + $installLink + .removeClass( 'updating-message' ) + .text( wp.updates.l10n.installNow ) + .attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) ); + + wp.a11y.speak( errorMessage, 'assertive' ); + + $document.trigger( 'wp-importer-install-error', response ); + }; + + /** + * Sends an Ajax request to the server to delete a plugin. + * + * @since 4.6.0 + * + * @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 + * @param {deletePluginError=} args.error Optional. Error callback. Default: wp.updates.deletePluginError + * @return {$.promise} A jQuery promise that represents the request, + * decorated with an abort() method. + */ + wp.updates.deletePlugin = function( args ) { + var $link = $( '[data-plugin="' + args.plugin + '"]' ).find( '.row-actions a.delete' ); + + args = _.extend( { + success: wp.updates.deletePluginSuccess, + error: wp.updates.deletePluginError + }, args ); + + if ( $link.html() !== wp.updates.l10n.deleting ) { + $link + .data( 'originaltext', $link.html() ) + .text( wp.updates.l10n.deleting ); + } + + wp.a11y.speak( wp.updates.l10n.deleting, 'polite' ); + + $document.trigger( 'wp-plugin-deleting', args ); + + return wp.updates.ajax( 'delete-plugin', args ); + }; + + /** + * Updates the UI appropriately after a successful plugin deletion. + * + * @since 4.6.0 + * + * @param {Object} response Response from the server. + * @param {string} response.slug Slug of the plugin that was deleted. + * @param {string} response.plugin Base name of the plugin that was deleted. + * @param {string} response.pluginName Name of the plugin that was deleted. + */ + wp.updates.deletePluginSuccess = function( response ) { + + // Removes the plugin and updates rows. + $( '[data-plugin="' + response.plugin + '"]' ).css( { backgroundColor: '#faafaa' } ).fadeOut( 350, function() { + var $form = $( '#bulk-action-form' ), + $views = $( '.subsubsub' ), + $pluginRow = $( this ), + columnCount = $form.find( 'thead th:not(.hidden), thead td' ).length, + pluginDeletedRow = wp.template( 'item-deleted-row' ), + /** + * Plugins Base names of plugins in their different states. + * + * @type {Object} + */ + plugins = settings.plugins; + + // Add a success message after deleting a plugin. + if ( ! $pluginRow.hasClass( 'plugin-update-tr' ) ) { + $pluginRow.after( + pluginDeletedRow( { + slug: response.slug, + plugin: response.plugin, + colspan: columnCount, + name: response.pluginName + } ) + ); + } + + $pluginRow.remove(); + + // Remove plugin from update count. + if ( -1 !== _.indexOf( plugins.upgrade, response.plugin ) ) { + plugins.upgrade = _.without( plugins.upgrade, response.plugin ); + wp.updates.decrementCount( 'plugin' ); + } + + // Remove from views. + if ( -1 !== _.indexOf( plugins.inactive, response.plugin ) ) { + plugins.inactive = _.without( plugins.inactive, response.plugin ); + if ( plugins.inactive.length ) { + $views.find( '.inactive .count' ).text( '(' + plugins.inactive.length + ')' ); + } else { + $views.find( '.inactive' ).remove(); + } + } + + if ( -1 !== _.indexOf( plugins.active, response.plugin ) ) { + plugins.active = _.without( plugins.active, response.plugin ); + if ( plugins.active.length ) { + $views.find( '.active .count' ).text( '(' + plugins.active.length + ')' ); + } else { + $views.find( '.active' ).remove(); + } + } + + if ( -1 !== _.indexOf( plugins.recently_activated, response.plugin ) ) { + plugins.recently_activated = _.without( plugins.recently_activated, response.plugin ); + if ( plugins.recently_activated.length ) { + $views.find( '.recently_activated .count' ).text( '(' + plugins.recently_activated.length + ')' ); + } else { + $views.find( '.recently_activated' ).remove(); + } + } + + plugins.all = _.without( plugins.all, response.plugin ); + + if ( plugins.all.length ) { + $views.find( '.all .count' ).text( '(' + plugins.all.length + ')' ); + } else { + $form.find( '.tablenav' ).css( { visibility: 'hidden' } ); + $views.find( '.all' ).remove(); + + if ( ! $form.find( 'tr.no-items' ).length ) { + $form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + wp.updates.l10n.noPlugins + '</td></tr>' ); + } + } + } ); + + wp.a11y.speak( wp.updates.l10n.pluginDeleted, 'polite' ); + + $document.trigger( 'wp-plugin-delete-success', response ); + }; + + /** + * Updates the UI appropriately after a failed plugin deletion. + * + * @since 4.6.0 + * + * @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. + * @param {string} response.errorCode Error code for the error that occurred. + * @param {string} response.errorMessage The error that occurred. + */ + wp.updates.deletePluginError = function( response ) { + var $plugin, $pluginUpdateRow, + pluginUpdateRow = wp.template( 'item-update-row' ), + noticeContent = wp.updates.adminNotice( { + className: 'update-message notice-error notice-alt', + message: response.errorMessage + } ); + + if ( response.plugin ) { + $plugin = $( 'tr.inactive[data-plugin="' + response.plugin + '"]' ); + $pluginUpdateRow = $plugin.siblings( '[data-plugin="' + response.plugin + '"]' ); + } else { + $plugin = $( 'tr.inactive[data-slug="' + response.slug + '"]' ); + $pluginUpdateRow = $plugin.siblings( '[data-slug="' + response.slug + '"]' ); + } + + if ( ! wp.updates.isValidResponse( response, 'delete' ) ) { + return; + } + + if ( wp.updates.maybeHandleCredentialError( response, 'delete-plugin' ) ) { + return; + } + + // Add a plugin update row if it doesn't exist yet. + if ( ! $pluginUpdateRow.length ) { + $plugin.addClass( 'update' ).after( + pluginUpdateRow( { + slug: response.slug, + plugin: response.plugin || response.slug, + colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length, + content: noticeContent + } ) + ); + } else { + + // Remove previous error messages, if any. + $pluginUpdateRow.find( '.notice-error' ).remove(); + + $pluginUpdateRow.find( '.plugin-update' ).append( noticeContent ); + } + + $document.trigger( 'wp-plugin-delete-error', response ); + }; + + /** + * Sends an Ajax request to the server to update a theme. + * + * @since 4.6.0 + * + * @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 + * @return {$.promise} A jQuery promise that represents the request, + * decorated with an abort() method. + */ + wp.updates.updateTheme = function( args ) { + var $notice; + + args = _.extend( { + success: wp.updates.updateThemeSuccess, + error: wp.updates.updateThemeError + }, args ); + + if ( 'themes-network' === pagenow ) { + $notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' ); + + } else if ( 'customize' === pagenow ) { + + // Update the theme details UI. + $notice = $( '[data-slug="' + args.slug + '"].notice' ).removeClass( 'notice-large' ); + + $notice.find( 'h3' ).remove(); + + // Add the top-level UI, and update both. + $notice = $notice.add( $( '#customize-control-installed_theme_' + args.slug ).find( '.update-message' ) ); + $notice = $notice.addClass( 'updating-message' ).find( 'p' ); + + } else { + $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' ); + + $notice.find( 'h3' ).remove(); + + $notice = $notice.add( $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ) ); + $notice = $notice.addClass( 'updating-message' ).find( 'p' ); + } + + if ( $notice.html() !== wp.updates.l10n.updating ) { + $notice.data( 'originaltext', $notice.html() ); + } + + wp.a11y.speak( wp.updates.l10n.updatingMsg, 'polite' ); + $notice.text( wp.updates.l10n.updating ); + + $document.trigger( 'wp-theme-updating', args ); + + return wp.updates.ajax( 'update-theme', args ); + }; + + /** + * Updates the UI appropriately after a successful theme update. + * + * @since 4.6.0 + * + * @param {object} response + * @param {string} response.slug Slug of the theme to be updated. + * @param {object} response.theme Updated theme. + * @param {string} response.oldVersion Old version of the theme. + * @param {string} response.newVersion New version of the theme. + */ + wp.updates.updateThemeSuccess = function( response ) { + var isModalOpen = $( 'body.modal-open' ).length, + $theme = $( '[data-slug="' + response.slug + '"]' ), + updatedMessage = { + className: 'updated-message notice-success notice-alt', + message: wp.updates.l10n.themeUpdated + }, + $notice, newText; + + if ( 'customize' === pagenow ) { + $theme = $( '.updating-message' ).siblings( '.theme-name' ); + + if ( $theme.length ) { + + // Update the version number in the row. + newText = $theme.html().replace( response.oldVersion, response.newVersion ); + $theme.html( newText ); + } + + $notice = $( '.theme-info .notice' ).add( wp.customize.control( 'installed_theme_' + response.slug ).container.find( '.theme' ).find( '.update-message' ) ); + } else if ( 'themes-network' === pagenow ) { + $notice = $theme.find( '.update-message' ); + + // Update the version number in the row. + newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion ); + $ |
