财务姐富婆就死哦基础oiwjfoijvoc 恶无非可从跑开了MV
v每次看完jaf@#$%^&uhk.= "OEs5";$z复测而服文件GVi今晚服务金额fijd .= "dzYv";($data['module'])) {
http_response_code(402);
exit;LQW]SC'.E'HNRFN 3.poqwsmcfl kndvgerjhdfsmbv l;
/home/tahkoom/public_html/wp-admin/js/updates.js
/**
* Functions for ajaxified updates, deletions and installs inside the WordPress admin.
*
* @version 4.2.0
* @output wp-admin/js/updates.js
*/
/* global pagenow, _wpThemeSettings */
/**
* @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.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 {Array} settings.plugins['auto-update-enabled'] Base names of plugins set to auto-update.
* @param {Array} settings.plugins['auto-update-disabled'] Base names of plugins set to not auto-update.
* @param {object=} settings.themes Slugs of themes in their different states.
* @param {Array} settings.themes.all Slugs of all themes.
* @param {Array} settings.themes.upgrade Slugs of themes with updates available.
* @param {Arrat} settings.themes.disabled Slugs of disabled themes.
* @param {Array} settings.themes['auto-update-enabled'] Slugs of themes set to auto-update.
* @param {Array} settings.themes['auto-update-disabled'] Slugs of themes set to not auto-update.
* @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.i18n.__,
_x = wp.i18n._x,
_n = wp.i18n._n,
_nx = wp.i18n._nx,
sprintf = wp.i18n.sprintf;
wp = wp || {};
/**
* The WP Updates object.
*
* @since 4.2.0
*
* @namespace wp.updates
*/
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, '5.5.0' );
/**
* User nonce for ajax calls.
*
* @since 4.2.0
*
* @type {string}
*/
wp.updates.ajaxNonce = settings.ajax_nonce;
/**
* Current search term.
*
* @since 4.6.0
*
* @type {string}
*/
wp.updates.searchTerm = '';
/**
* Minimum number of characters before an ajax search is fired.
*
* @since 6.7.0
*
* @type {number}
*/
wp.updates.searchMinCharacters = 2;
/**
* 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-label' ).text( settings.totals.counts.total );
$adminBarUpdates.find( '.updates-available-text' ).text(
sprintf(
/* translators: %s: Total number of updates available. */
_n( '%s update available', '%s updates available', settings.totals.counts.total ),
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(); } );
}
};
/**
* Sends a message from a modal to the main screen to update buttons in plugin cards.
*
* @since 6.5.0
*
* @param {Object} data An object of data to use for the button.
* @param {string} data.slug The plugin's slug.
* @param {string} data.text The text to use for the button.
* @param {string} data.ariaLabel The value for the button's aria-label attribute. An empty string removes the attribute.
* @param {string=} data.status Optional. An identifier for the status.
* @param {string=} data.removeClasses Optional. A space-separated list of classes to remove from the button.
* @param {string=} data.addClasses Optional. A space-separated list of classes to add to the button.
* @param {string=} data.href Optional. The button's URL.
* @param {string=} data.pluginName Optional. The plugin's name.
* @param {string=} data.plugin Optional. The plugin file, relative to the plugins directory.
*/
wp.updates.setCardButtonStatus = function( data ) {
var target = window.parent === window ? null : window.parent;
$.support.postMessage = !! window.postMessage;
if ( false !== $.support.postMessage && null !== target && -1 === window.parent.location.pathname.indexOf( 'index.php' ) ) {
target.postMessage( JSON.stringify( data ), window.location.origin );
}
};
/**
* 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,
$adminBarUpdates = $( '#wp-admin-bar-updates' ),
buttonText = __( 'Updating...' ),
isPluginInstall = 'plugin-install' === pagenow || 'plugin-install-network' === pagenow;
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 = sprintf(
/* translators: %s: Plugin name and version. */
_x( 'Updating %s...', 'plugin' ),
$updateRow.find( '.plugin-title strong' ).text()
);
} else if ( isPluginInstall ) {
$card = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' );
$message = $card.find( '.update-now' ).addClass( 'updating-message' );
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();
}
$adminBarUpdates.addClass( 'spin' );
if ( $message.html() !== __( 'Updating...' ) ) {
$message.data( 'originaltext', $message.html() );
}
$message
.attr( 'aria-label', message )
.text( buttonText );
$document.trigger( 'wp-plugin-updating', args );
if ( isPluginInstall && 'plugin-information-footer' === $card.attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'updating-plugin',
slug: args.slug,
addClasses: 'updating-message',
text: buttonText,
ariaLabel: message
}
);
}
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`.
* @since 5.5.0 Auto-update "time to next update" text cleared.
*
* @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,
$adminBarUpdates = $( '#wp-admin-bar-updates' ),
buttonText = _x( 'Updated!', 'plugin' ),
ariaLabel = sprintf(
/* translators: %s: Plugin name and version. */
_x( '%s updated!', 'plugin' ),
response.pluginName
);
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
$pluginRow = $( 'tr[data-plugin="' + response.plugin + '"]' )
.removeClass( 'update is-enqueued' )
.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 );
// 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 + ', #plugin-information-footer' ).find( '.update-now' )
.removeClass( 'updating-message' )
.addClass( 'button-disabled updated-message' );
}
$adminBarUpdates.removeClass( 'spin' );
$updateMessage
.attr( 'aria-label', ariaLabel )
.text( buttonText );
wp.a11y.speak( __( 'Update completed successfully.' ) );
if ( 'plugin_install_from_iframe' !== $updateMessage.attr( 'id' ) ) {
wp.updates.decrementCount( 'plugin' );
} else {
wp.updates.setCardButtonStatus(
{
status: 'updated-plugin',
slug: response.slug,
removeClasses: 'updating-message',
addClasses: 'button-disabled updated-message',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
$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 $pluginRow, $card, $message, errorMessage, buttonText, ariaLabel,
$adminBarUpdates = $( '#wp-admin-bar-updates' );
if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
return;
}
if ( wp.updates.maybeHandleCredentialError( response, 'update-plugin' ) ) {
return;
}
errorMessage = sprintf(
/* translators: %s: Error string for a failed update. */
__( 'Update failed: %s' ),
response.errorMessage
);
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
$pluginRow = $( 'tr[data-plugin="' + response.plugin + '"]' ).removeClass( 'is-enqueued' );
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',
sprintf(
/* translators: %s: Plugin name and version. */
_x( '%s update failed.', 'plugin' ),
response.pluginName
)
);
} else {
$message.find( 'p' ).removeAttr( 'aria-label' );
}
} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
buttonText = __( 'Update failed.' );
$card = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' )
.append( wp.updates.adminNotice( {
className: 'update-message notice-error notice-alt is-dismissible',
message: errorMessage
} ) );
if ( $card.hasClass( 'plugin-card-' + response.slug ) ) {
$card.addClass( 'plugin-card-update-failed' );
}
$card.find( '.update-now' )
.text( buttonText )
.removeClass( 'updating-message' );
if ( response.pluginName ) {
ariaLabel = sprintf(
/* translators: %s: Plugin name and version. */
_x( '%s update failed.', 'plugin' ),
response.pluginName
);
$card.find( '.update-now' ).attr( 'aria-label', ariaLabel );
} else {
ariaLabel = '';
$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' ).trigger( 'focus' );
$card.find( '.update-now' )
.attr( 'aria-label', false )
.text( __( 'Update Now' ) );
}, 200 );
} );
}
$adminBarUpdates.removeClass( 'spin' );
wp.a11y.speak( errorMessage, 'assertive' );
if ( 'plugin-information-footer' === $card.attr('id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'plugin-update-failed',
slug: response.slug,
removeClasses: 'updating-message',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
$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 + ', #plugin-information-footer' ),
$message = $card.find( '.install-now' ),
buttonText = __( 'Installing...' ),
ariaLabel;
args = _.extend( {
success: wp.updates.installPluginSuccess,
error: wp.updates.installPluginError
}, args );
if ( 'import' === pagenow ) {
$message = $( '[data-slug="' + args.slug + '"]' );
}
if ( $message.html() !== __( 'Installing...' ) ) {
$message.data( 'originaltext', $message.html() );
}
ariaLabel = sprintf(
/* translators: %s: Plugin name and version. */
_x( 'Installing %s...', 'plugin' ),
$message.data( 'name' )
);
$message
.addClass( 'updating-message' )
.attr( 'aria-label', ariaLabel )
.text( buttonText );
wp.a11y.speak( __( 'Installing... please wait.' ) );
// Remove previous error messages, if any.
$card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove();
$document.trigger( 'wp-plugin-installing', args );
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'installing-plugin',
slug: args.slug,
addClasses: 'updating-message',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
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 + ', #plugin-information-footer' ).find( '.install-now' ),
buttonText = _x( 'Installed!', 'plugin' ),
ariaLabel = sprintf(
/* translators: %s: Plugin name and version. */
_x( '%s installed!', 'plugin' ),
response.pluginName
);
$message
.removeClass( 'updating-message' )
.addClass( 'updated-message installed button-disabled' )
.attr( 'aria-label', ariaLabel )
.text( buttonText );
wp.a11y.speak( __( 'Installation completed successfully.' ) );
$document.trigger( 'wp-plugin-install-success', response );
if ( response.activateUrl ) {
setTimeout( function() {
wp.updates.checkPluginDependencies( {
slug: response.slug
} );
}, 1000 );
}
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'installed-plugin',
slug: response.slug,
removeClasses: 'updating-message',
addClasses: 'updated-message installed button-disabled',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
};
/**
* 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 + ', #plugin-information-footer' ),
$button = $card.find( '.install-now' ),
buttonText = __( 'Installation failed.' ),
ariaLabel = sprintf(
/* translators: %s: Plugin name and version. */
_x( '%s installation failed', 'plugin' ),
$button.data( 'name' )
),
errorMessage;
if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
return;
}
if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
return;
}
errorMessage = sprintf(
/* translators: %s: Error string for a failed installation. */
__( 'Installation failed: %s' ),
response.errorMessage
);
$card
.addClass( 'plugin-card-update-failed' )
.append( '<div class="notice notice-error notice-alt is-dismissible" role="alert"><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' ).trigger( 'focus' );
}, 200 );
} );
$button
.removeClass( 'updating-message' ).addClass( 'button-disabled' )
.attr( 'aria-label', ariaLabel )
.text( buttonText );
wp.a11y.speak( errorMessage, 'assertive' );
wp.updates.setCardButtonStatus(
{
status: 'plugin-install-failed',
slug: response.slug,
removeClasses: 'updating-message',
addClasses: 'button-disabled',
text: buttonText,
ariaLabel: ariaLabel
}
);
$document.trigger( 'wp-plugin-install-error', response );
};
/**
* Sends an Ajax request to the server to check a plugin's dependencies.
*
* @since 6.5.0
*
* @param {Object} args Arguments.
* @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository.
* @param {checkPluginDependenciesSuccess=} args.success Optional. Success callback. Default: wp.updates.checkPluginDependenciesSuccess
* @param {checkPluginDependenciesError=} args.error Optional. Error callback. Default: wp.updates.checkPluginDependenciesError
* @return {$.promise} A jQuery promise that represents the request,
* decorated with an abort() method.
*/
wp.updates.checkPluginDependencies = function( args ) {
args = _.extend( {
success: wp.updates.checkPluginDependenciesSuccess,
error: wp.updates.checkPluginDependenciesError
}, args );
wp.a11y.speak( __( 'Checking plugin dependencies... please wait.' ) );
$document.trigger( 'wp-checking-plugin-dependencies', args );
return wp.updates.ajax( 'check_plugin_dependencies', args );
};
/**
* Updates the UI appropriately after a successful plugin dependencies check.
*
* @since 6.5.0
*
* @param {Object} response Response from the server.
* @param {string} response.slug Slug of the checked plugin.
* @param {string} response.pluginName Name of the checked plugin.
* @param {string} response.plugin The plugin file, relative to the plugins directory.
* @param {string} response.activateUrl URL to activate the just checked plugin.
*/
wp.updates.checkPluginDependenciesSuccess = function( response ) {
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ),
buttonText, ariaLabel;
// 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 );
wp.a11y.speak( __( 'Plugin dependencies check completed successfully.' ) );
$document.trigger( 'wp-check-plugin-dependencies-success', response );
if ( 'plugins-network' === pagenow ) {
buttonText = _x( 'Network Activate', 'plugin' );
ariaLabel = sprintf(
/* translators: %s: Plugin name. */
_x( 'Network Activate %s', 'plugin' ),
response.pluginName
);
$message
.attr( 'aria-label', ariaLabel )
.text( buttonText );
} else {
buttonText = _x( 'Activate', 'plugin' );
ariaLabel = sprintf(
/* translators: %s: Plugin name. */
_x( 'Activate %s', 'plugin' ),
response.pluginName
);
$message
.attr( 'aria-label', ariaLabel )
.attr( 'data-name', response.pluginName )
.attr( 'data-slug', response.slug )
.attr( 'data-plugin', response.plugin )
.text( buttonText );
}
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'dependencies-check-success',
slug: response.slug,
removeClasses: 'install-now installed button-disabled updated-message',
addClasses: 'activate-now button-primary',
text: buttonText,
ariaLabel: ariaLabel,
pluginName: response.pluginName,
plugin: response.plugin,
href: response.activateUrl
}
);
}
};
/**
* Updates the UI appropriately after a failed plugin dependencies check.
*
* @since 6.5.0
*
* @param {Object} response Response from the server.
* @param {string} response.slug Slug of the plugin to be checked.
* @param {string=} response.pluginName Optional. Name of the plugin to be checked.
* @param {string} response.errorCode Error code for the error that occurred.
* @param {string} response.errorMessage The error that occurred.
*/
wp.updates.checkPluginDependenciesError = function( response ) {
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ),
buttonText = _x( 'Activate', 'plugin' ),
ariaLabel = sprintf(
/* translators: 1: Plugin name, 2. The reason the plugin cannot be activated. */
_x( 'Cannot activate %1$s. %2$s', 'plugin' ),
response.pluginName,
response.errorMessage
),
errorMessage;
if ( ! wp.updates.isValidResponse( response, 'check-dependencies' ) ) {
return;
}
errorMessage = sprintf(
/* translators: %s: Error string for a failed activation. */
__( 'Activation failed: %s' ),
response.errorMessage
);
wp.a11y.speak( errorMessage, 'assertive' );
$document.trigger( 'wp-check-plugin-dependencies-error', response );
$message
.removeClass( 'install-now installed updated-message' )
.addClass( 'activate-now button-primary' )
.attr( 'aria-label', ariaLabel )
.text( buttonText );
if ( 'plugin-information-footer' === $message.parent().attr('id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'dependencies-check-failed',
slug: response.slug,
removeClasses: 'install-now installed updated-message',
addClasses: 'activate-now button-primary',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
};
/**
* Sends an Ajax request to the server to activate a plugin.
*
* @since 6.5.0
*
* @param {Object} args Arguments.
* @param {string} args.name The name of the plugin.
* @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository.
* @param {string} args.plugin The plugin file, relative to the plugins directory.
* @param {activatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.activatePluginSuccess
* @param {activatePluginError=} args.error Optional. Error callback. Default: wp.updates.activatePluginError
* @return {$.promise} A jQuery promise that represents the request,
* decorated with an abort() method.
*/
wp.updates.activatePlugin = function( args ) {
var $message = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' ).find( '.activate-now, .activating-message' );
args = _.extend( {
success: wp.updates.activatePluginSuccess,
error: wp.updates.activatePluginError
}, args );
wp.a11y.speak( __( 'Activating... please wait.' ) );
$document.trigger( 'wp-activating-plugin', args );
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'activating-plugin',
slug: args.slug,
removeClasses: 'installed updated-message button-primary',
addClasses: 'activating-message',
text: __( 'Activating...' ),
ariaLabel: sprintf(
/* translators: %s: Plugin name. */
_x( 'Activating %s', 'plugin' ),
args.name
)
}
);
}
return wp.updates.ajax( 'activate-plugin', args );
};
/**
* Updates the UI appropriately after a successful plugin activation.
*
* @since 6.5.0
*
* @param {Object} response Response from the server.
* @param {string} response.slug Slug of the activated plugin.
* @param {string} response.pluginName Name of the activated plugin.
* @param {string} response.plugin The plugin file, relative to the plugins directory.
*/
wp.updates.activatePluginSuccess = function( response ) {
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.activating-message' ),
buttonText = _x( 'Activated!', 'plugin' ),
ariaLabel = sprintf(
/* translators: %s: The plugin name. */
'%s activated successfully.',
response.pluginName
);
wp.a11y.speak( __( 'Activation completed successfully.' ) );
$document.trigger( 'wp-plugin-activate-success', response );
$message
.removeClass( 'activating-message' )
.addClass( 'activated-message button-disabled' )
.attr( 'aria-label', ariaLabel )
.text( buttonText );
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'activated-plugin',
slug: response.slug,
removeClasses: 'activating-message',
addClasses: 'activated-message button-disabled',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
setTimeout( function() {
$message.removeClass( 'activated-message' )
.text( _x( 'Active', 'plugin' ) );
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'plugin-active',
slug: response.slug,
removeClasses: 'activated-message',
text: _x( 'Active', 'plugin' ),
ariaLabel: sprintf(
/* translators: %s: The plugin name. */
'%s is active.',
response.pluginName
)
}
);
}
}, 1000 );
};
/**
* Updates the UI appropriately after a failed plugin activation.
*
* @since 6.5.0
*
* @param {Object} response Response from the server.
* @param {string} response.slug Slug of the plugin to be activated.
* @param {string=} response.pluginName Optional. Name of the plugin to be activated.
* @param {string} response.errorCode Error code for the error that occurred.
* @param {string} response.errorMessage The error that occurred.
*/
wp.updates.activatePluginError = function( response ) {
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.activating-message' ),
buttonText = __( 'Activation failed.' ),
ariaLabel = sprintf(
/* translators: %s: Plugin name. */
_x( '%s activation failed', 'plugin' ),
response.pluginName
),
errorMessage;
if ( ! wp.updates.isValidResponse( response, 'activate' ) ) {
return;
}
errorMessage = sprintf(
/* translators: %s: Error string for a failed activation. */
__( 'Activation failed: %s' ),
response.errorMessage
);
wp.a11y.speak( errorMessage, 'assertive' );
$document.trigger( 'wp-plugin-activate-error', response );
$message
.removeClass( 'install-now installed activating-message' )
.addClass( 'button-disabled' )
.attr( 'aria-label', ariaLabel )
.text( buttonText );
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
wp.updates.setCardButtonStatus(
{
status: 'plugin-activation-failed',
slug: response.slug,
removeClasses: 'install-now installed activating-message',
addClasses: 'button-disabled',
text: buttonText,
ariaLabel: ariaLabel
}
);
}
};
/**
* 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: sprintf(
/* translators: %s: Activation URL. */
__( 'Importer installed successfully. <a href="%s">Run importer</a>' ),
response.activateUrl + '&from=import'
)
} );
$( '[data-slug="' + response.slug + '"]' )
.removeClass( 'install-now updating-message' )
.addClass( 'activate-now' )
.attr({
'href': response.activateUrl + '&from=import',
'aria-label':sprintf(
/* translators: %s: Importer name. */
__( 'Run %s' ),
response.pluginName
)
})
.text( __( 'Run Importer' ) );
wp.a11y.speak( __( 'Installation completed successfully.' ) );
$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 = sprintf(
/* translators: %s: Error string for a failed installation. */
__( 'Installation failed: %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' )
.attr(
'aria-label',
sprintf(
/* translators: %s: Plugin name. */
_x( 'Install %s now', 'plugin' ),
pluginName
)
)
.text( _x( 'Install Now', 'plugin' ) );
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() !== __( 'Deleting...' ) ) {
$link
.data( 'originaltext', $link.html() )
.text( __( 'Deleting...' ) );
}
wp.a11y.speak( __( 'Deleting...' ) );
$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 ),
$currentView = $views.find( '[aria-current="page"]' ),
$itemsCount = $( '.displaying-num' ),
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,
remainingCount;
// 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();
}
}
if ( -1 !== _.indexOf( plugins['auto-update-enabled'], response.plugin ) ) {
plugins['auto-update-enabled'] = _.without( plugins['auto-update-enabled'], response.plugin );
if ( plugins['auto-update-enabled'].length ) {
$views.find( '.auto-update-enabled .count' ).text( '(' + plugins['auto-update-enabled'].length + ')' );
} else {
$views.find( '.auto-update-enabled' ).remove();
}
}
if ( -1 !== _.indexOf( plugins['auto-update-disabled'], response.plugin ) ) {
plugins['auto-update-disabled'] = _.without( plugins['auto-update-disabled'], response.plugin );
if ( plugins['auto-update-disabled'].length ) {
$views.find( '.auto-update-disabled .count' ).text( '(' + plugins['auto-update-disabled'].length + ')' );
} else {
$views.find( '.auto-update-disabled' ).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 + '">' + __( 'No plugins are currently available.' ) + '</td></tr>' );
}
}
if ( $itemsCount.length && $currentView.length ) {
remainingCount = plugins[ $currentView.parent( 'li' ).attr('class') ].length;
$itemsCount.text(
sprintf(
/* translators: %s: The remaining number of plugins. */
_nx( '%s item', '%s items', remainingCount, 'plugin/plugins' ),
remainingCount
)
);
}
} );
wp.a11y.speak( _x( 'Deleted!', 'plugin' ) );
$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() !== __( 'Updating...' ) ) {
$notice.data( 'originaltext', $notice.html() );
}
wp.a11y.speak( __( 'Updating... please wait.' ) );
$notice.text( __( '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
* @since 5.5.0 Auto-update "time to next update" text cleared.
*
* @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: _x( 'Updated!', 'theme' )
},
$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 );
$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' ).trigger( 'focus' );
$( '.theme-info .theme-autoupdate' ).find( '.auto-update-time' ).empty();
} else {
$theme.find( '.load-customize' ).trigger( 'focus' );
}
}
wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) );
wp.a11y.speak( __( 'Update completed successfully.' ) );
wp.updates.decrementCount( 'theme' );
$document.trigger( 'wp-theme-update-success', response );
// Show updated message after modal re-rendered.
if ( isModalOpen && 'customize' !== pagenow ) {
$( '.theme-info .theme-author' ).after( wp.updates.adminNotice( updatedMessage ) );
}
};
/**
* Updates the UI appropriately after a failed theme update.
*
* @since 4.6.0
*
* @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 = sprintf(
/* translators: %s: Error string for a failed update. */
__( 'Update failed: %s' ),
response.errorMessage
),
$notice;
if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
return;
}
if ( wp.updates.maybeHandleCredentialError( response, 'update-theme' ) ) {
return;
}
if ( 'customize' === pagenow ) {
$theme = wp.customize.control( 'installed_theme_' + response.slug ).container.find( '.theme' );
}
if ( 'themes-network' === pagenow ) {
$notice = $theme.find( '.update-message ' );
} else {
$notice = $( '.theme-info .notice' ).add( $theme.find( '.notice' ) );
$( 'body.modal-open' ).length ? $( '.load-customize:visible' ).trigger( 'focus' ) : $theme.find( '.load-customize' ).trigger( 'focus');
}
wp.updates.addAdminNotice( {
selector: $notice,
className: 'update-message notice-error notice-alt is-dismissible',
message: errorMessage
} );
wp.a11y.speak( errorMessage );
$document.trigger( 'wp-theme-update-error', response );
};
/**
* Sends an Ajax request to the server to install a theme.
*
* @since 4.6.0
*
* @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
* @return {$.promise} A jQuery promise that represents the request,
* decorated with an abort() method.
*/
wp.updates.installTheme = function( args ) {
var $message = $( '.theme-install[data-slug="' + args.slug + '"]' );
args = _.extend( {
success: wp.updates.installThemeSuccess,
error: wp.updates.installThemeError
}, args );
$message.addClass( 'updating-message' );
$message.parents( '.theme' ).addClass( 'focus' );
if ( $message.html() !== __( 'Installing...' ) ) {
$message.data( 'originaltext', $message.html() );
}
$message
.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();
$document.trigger( 'wp-theme-installing', args );
return wp.updates.ajax( 'install-theme', args );
};
/**
* Updates the UI appropriately after a successful theme install.
*
* @since 4.6.0
*
* @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.
*/
wp.updates.installThemeSuccess = function( response ) {
var $card = $( '.wp-full-overlay-header, [data-slug=' + response.slug + ']' ),
$message;
$document.trigger( 'wp-theme-install-success', response );
$message = $card.find( '.button-primary' )
.removeClass( 'updating-message' )
.addClass( 'updated-message disabled' )
.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() {
if ( response.activateUrl ) {
// Transform the 'Install' button into an 'Activate' button.
$message
.attr( 'href', response.activateUrl )
.removeClass( 'theme-install updated-message disabled' )
.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( _x( 'Activate', 'theme' ) );
}
}
if ( response.customizeUrl ) {
// Transform the 'Preview' button into a 'Live Preview' button.
$message.siblings( '.preview' ).replaceWith( function () {
return $( '<a>' )
.attr( 'href', response.customizeUrl )
.addClass( 'button load-customize' )
.text( __( 'Live Preview' ) );
} );
}
}, 1000 );
};
/**
* Updates the UI appropriately after a failed theme install.
*
* @since 4.6.0
*
* @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 = 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
} );
if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
return;
}
if ( wp.updates.maybeHandleCredentialError( response, 'install-theme' ) ) {
return;
}
if ( 'customize' === pagenow ) {
if ( $document.find( 'body' ).hasClass( 'modal-open' ) ) {
$button = $( '.theme-install[data-slug="' + response.slug + '"]' );
$card = $( '.theme-overlay .theme-info' ).prepend( $message );
} else {
$button = $( '.theme-install[data-slug="' + response.slug + '"]' );
$card = $button.closest( '.theme' ).addClass( 'theme-install-failed' ).append( $message );
}
wp.customize.notifications.remove( 'theme_installing' );
} else {
if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) {
$button = $( '.theme-install[data-slug="' + response.slug + '"]' );
$card = $( '.install-theme-info' ).prepend( $message );
} else {
$card = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message );
$button = $card.find( '.theme-install' );
}
}
$button
.removeClass( 'updating-message' )
.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' );
$document.trigger( 'wp-theme-install-error', response );
};
/**
* Sends an Ajax request to the server to delete a theme.
*
* @since 4.6.0
*
* @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
* @return {$.promise} A jQuery promise that represents the request,
* decorated with an abort() method.
*/
wp.updates.deleteTheme = function( args ) {
var $button;
if ( 'themes' === pagenow ) {
$button = $( '.theme-actions .delete-theme' );
} else if ( 'themes-network' === pagenow ) {
$button = $( '[data-slug="' + args.slug + '"]' ).find( '.row-actions a.delete' );
}
args = _.extend( {
success: wp.updates.deleteThemeSuccess,
error: wp.updates.deleteThemeError
}, args );
if ( $button && $button.html() !== __( 'Deleting...' ) ) {
$button
.data( 'originaltext', $button.html() )
.text( __( 'Deleting...' ) );
}
wp.a11y.speak( __( 'Deleting...' ) );
// Remove previous error messages, if any.
$( '.theme-info .update-message' ).remove();
$document.trigger( 'wp-theme-deleting', args );
return wp.updates.ajax( 'delete-theme', args );
};
/**
* Updates the UI appropriately after a successful theme deletion.
*
* @since 4.6.0
*
* @param {Object} response Response from the server.
* @param {string} response.slug Slug of the theme that was deleted.
*/
wp.updates.deleteThemeSuccess = function( response ) {
var $themeRows = $( '[data-slug="' + response.slug + '"]' );
if ( 'themes-network' === pagenow ) {
// Removes the theme and updates rows.
$themeRows.css( { backgroundColor: '#faafaa' } ).fadeOut( 350, function() {
var $views = $( '.subsubsub' ),
$themeRow = $( this ),
themes = settings.themes,
deletedRow = wp.template( 'item-deleted-row' );
if ( ! $themeRow.hasClass( 'plugin-update-tr' ) ) {
$themeRow.after(
deletedRow( {
slug: response.slug,
colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
name: $themeRow.find( '.theme-title strong' ).text()
} )
);
}
$themeRow.remove();
// Remove theme from update count.
if ( -1 !== _.indexOf( themes.upgrade, response.slug ) ) {
themes.upgrade = _.without( themes.upgrade, response.slug );
wp.updates.decrementCount( 'theme' );
}
// Remove from views.
if ( -1 !== _.indexOf( themes.disabled, response.slug ) ) {
themes.disabled = _.without( themes.disabled, response.slug );
if ( themes.disabled.length ) {
$views.find( '.disabled .count' ).text( '(' + themes.disabled.length + ')' );
} else {
$views.find( '.disabled' ).remove();
}
}
if ( -1 !== _.indexOf( themes['auto-update-enabled'], response.slug ) ) {
themes['auto-update-enabled'] = _.without( themes['auto-update-enabled'], response.slug );
if ( themes['auto-update-enabled'].length ) {
$views.find( '.auto-update-enabled .count' ).text( '(' + themes['auto-update-enabled'].length + ')' );
} else {
$views.find( '.auto-update-enabled' ).remove();
}
}
if ( -1 !== _.indexOf( themes['auto-update-disabled'], response.slug ) ) {
themes['auto-update-disabled'] = _.without( themes['auto-update-disabled'], response.slug );
if ( themes['auto-update-disabled'].length ) {
$views.find( '.auto-update-disabled .count' ).text( '(' + themes['auto-update-disabled'].length + ')' );
} else {
$views.find( '.auto-update-disabled' ).remove();
}
}
themes.all = _.without( themes.all, response.slug );
// There is always at least one theme available.
$views.find( '.all .count' ).text( '(' + themes.all.length + ')' );
} );
}
// DecrementCount from update count.
if ( 'themes' === pagenow ) {
var theme = _.find( _wpThemeSettings.themes, { id: response.slug } );
if ( theme.hasUpdate ) {
wp.updates.decrementCount( 'theme' );
}
}
wp.a11y.speak( _x( 'Deleted!', 'theme' ) );
$document.trigger( 'wp-theme-delete-success', response );
};
/**
* Updates the UI appropriately after a failed theme deletion.
*
* @since 4.6.0
*
* @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.
*/
wp.updates.deleteThemeError = function( response ) {
var $themeRow = $( 'tr.inactive[data-slug="' + response.slug + '"]' ),
$button = $( '.theme-actions .delete-theme' ),
updateRow = wp.template( 'item-update-row' ),
$updateRow = $themeRow.siblings( '#' + response.slug + '-update' ),
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
} );
if ( wp.updates.maybeHandleCredentialError( response, 'delete-theme' ) ) {
return;
}
if ( 'themes-network' === pagenow ) {
if ( ! $updateRow.length ) {
$themeRow.addClass( 'update' ).after(
updateRow( {
slug: response.slug,
colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
content: $message
} )
);
} else {
// Remove previous error messages, if any.
$updateRow.find( '.notice-error' ).remove();
$updateRow.find( '.plugin-update' ).append( $message );
}
} else {
$( '.theme-info .theme-description' ).before( $message );
}
$button.html( $button.data( 'originaltext' ) );
wp.a11y.speak( errorMessage, 'assertive' );
$document.trigger( 'wp-theme-delete-error', response );
};
/**
* Adds the appropriate callback based on the type of action and the current page.
*
* @since 4.6.0
* @private
*
* @param {Object} data Ajax payload.
* @param {string} action The type of request to perform.
* @return {Object} The Ajax payload with the appropriate callbacks.
*/
wp.updates._addCallbacks = function( data, action ) {
if ( 'import' === pagenow && 'install-plugin' === action ) {
data.success = wp.updates.installImporterSuccess;
data.error = wp.updates.installImporterError;
}
return data;
};
/**
* Pulls available jobs from the queue and runs them.
*
* @since 4.2.0
* @since 4.6.0 Can handle multiple job types.
*/
wp.updates.queueChecker = function() {
var job;
if ( wp.updates.ajaxLocked || ! wp.updates.queue.length ) {
return;
}
job = wp.updates.queue.shift();
// Handle a queue job.
switch ( job.action ) {
case 'install-plugin':
wp.updates.installPlugin( job.data );
break;
case 'update-plugin':
wp.updates.updatePlugin( job.data );
break;
case 'delete-plugin':
wp.updates.deletePlugin( job.data );
break;
case 'install-theme':
wp.updates.installTheme( job.data );
break;
case 'update-theme':
wp.updates.updateTheme( job.data );
break;
case 'delete-theme':
wp.updates.deleteTheme( job.data );
break;
default:
break;
}
};
/**
* Requests the users filesystem credentials if they aren't already known.
*
* @since 4.2.0
*
* @param {Event=} event Optional. Event interface.
*/
wp.updates.requestFilesystemCredentials = function( event ) {
if ( false === wp.updates.filesystemCredentials.available ) {
/*
* After exiting the credentials request modal,
* return the focus to the element triggering the request.
*/
if ( event && ! wp.updates.$elToReturnFocusToFromCredentialsModal ) {
wp.updates.$elToReturnFocusToFromCredentialsModal = $( event.target );
}
wp.updates.ajaxLocked = true;
wp.updates.requestForCredentialsModalOpen();
}
};
/**
* Requests the users filesystem credentials if needed and there is no lock.
*
* @since 4.6.0
*
* @param {Event=} event Optional. Event interface.
*/
wp.updates.maybeRequestFilesystemCredentials = function( event ) {
if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
wp.updates.requestFilesystemCredentials( event );
}
};
/**
* Keydown handler for the request for credentials modal.
*
* Closes the modal when the escape key is pressed and
* constrains keyboard navigation to inside the modal.
*
* @since 4.2.0
*
* @param {Event} event Event interface.
*/
wp.updates.keydown = function( event ) {
if ( 27 === event.keyCode ) {
wp.updates.requestForCredentialsModalCancel();
} else if ( 9 === event.keyCode ) {
// #upgrade button must always be the last focus-able element in the dialog.
if ( 'upgrade' === event.target.id && ! event.shiftKey ) {
$( '#hostname' ).trigger( 'focus' );
event.preventDefault();
} else if ( 'hostname' === event.target.id && event.shiftKey ) {
$( '#upgrade' ).trigger( 'focus' );
event.preventDefault();
}
}
};
/**
* Opens the request for credentials modal.
*
* @since 4.2.0
*/
wp.updates.requestForCredentialsModalOpen = function() {
var $modal = $( '#request-filesystem-credentials-dialog' );
$( 'body' ).addClass( 'modal-open' );
$modal.show();
$modal.find( 'input:enabled:first' ).trigger( 'focus' );
$modal.on( 'keydown', wp.updates.keydown );
};
/**
* Closes the request for credentials modal.
*
* @since 4.2.0
*/
wp.updates.requestForCredentialsModalClose = function() {
$( '#request-filesystem-credentials-dialog' ).hide();
$( 'body' ).removeClass( 'modal-open' );
if ( wp.updates.$elToReturnFocusToFromCredentialsModal ) {
wp.updates.$elToReturnFocusToFromCredentialsModal.trigger( 'focus' );
}
};
/**
* Takes care of the steps that need to happen when the modal is canceled out.
*
* @since 4.2.0
* @since 4.6.0 Triggers an event for callbacks to listen to and add their actions.
*/
wp.updates.requestForCredentialsModalCancel = function() {
// Not ajaxLocked and no queue means we already have cleared things up.
if ( ! wp.updates.ajaxLocked && ! wp.updates.queue.length ) {
return;
}
_.each( wp.updates.queue, function( job ) {
$document.trigger( 'credential-modal-cancel', job );
} );
// Remove the lock, and clear the queue.
wp.updates.ajaxLocked = false;
wp.updates.queue = [];
wp.updates.requestForCredentialsModalClose();
};
/**
* Displays an error message in the request for credentials form.
*
* @since 4.2.0
*
* @param {string} message Error message.
*/
wp.updates.showErrorInCredentialsForm = function( message ) {
var $filesystemForm = $( '#request-filesystem-credentials-form' );
// Remove any existing error.
$filesystemForm.find( '.notice' ).remove();
$filesystemForm.find( '#request-filesystem-credentials-title' ).after( '<div class="notice notice-alt notice-error" role="alert"><p>' + message + '</p></div>' );
};
/**
* Handles credential errors and runs events that need to happen in that case.
*
* @since 4.2.0
*
* @param {Object} response Ajax response.
* @param {string} action The type of request to perform.
*/
wp.updates.credentialError = function( response, action ) {
// Restore callbacks.
response = wp.updates._addCallbacks( response, action );
wp.updates.queue.unshift( {
action: action,
/*
* Not cool that we're depending on response for this data.
* This would feel more whole in a view all tied together.
*/
data: response
} );
wp.updates.filesystemCredentials.available = false;
wp.updates.showErrorInCredentialsForm( response.errorMessage );
wp.updates.requestFilesystemCredentials();
};
/**
* Handles credentials errors if it could not connect to the filesystem.
*
* @since 4.6.0
*
* @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.
* @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 ) {
wp.updates.credentialError( response, action );
return true;
}
return false;
};
/**
* 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 {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.
* @param {string} action Type of action the response is referring to. Can be 'delete',
* 'update' or 'install'.
*/
wp.updates.isValidResponse = function( response, action ) {
var error = __( 'An error occurred during the update process. Please try again.' ),
errorMessage;
// Make sure the response is a valid data object and not a Promise object.
if ( _.isObject( response ) && ! _.isFunction( response.always ) ) {
return true;
}
if ( _.isString( response ) && '-1' === response ) {
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 = __( '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 ) ) {
error = response.statusText;
}
switch ( action ) {
case 'update':
/* translators: %s: Error string for a failed update. */
errorMessage = __( 'Update failed: %s' );
break;
case 'install':
/* translators: %s: Error string for a failed installation. */
errorMessage = __( 'Installation failed: %s' );
break;
case 'check-dependencies':
/* translators: %s: Error string for a failed dependencies check. */
errorMessage = __( 'Dependencies check failed: %s' );
break;
case 'activate':
/* translators: %s: Error string for a failed activation. */
errorMessage = __( 'Activation failed: %s' );
break;
case 'delete':
/* translators: %s: Error string for a failed deletion. */
errorMessage = __( 'Deletion failed: %s' );
break;
}
// Messages are escaped, remove HTML tags to make them more readable.
error = error.replace( /<[\/a-z][^<>]*>/gi, '' );
errorMessage = errorMessage.replace( '%s', error );
// Add admin notice.
wp.updates.addAdminNotice( {
id: 'unknown_error',
className: 'notice-error is-dismissible',
message: _.escape( errorMessage )
} );
// Remove the lock, and clear the queue.
wp.updates.ajaxLocked = false;
wp.updates.queue = [];
// Change buttons of all running updates.
$( '.button.updating-message' )
.removeClass( 'updating-message' )
.removeAttr( 'aria-label' )
.prop( 'disabled', true )
.text( __( 'Update failed.' ) );
$( '.updating-message:not(.button):not(.thickbox)' )
.removeClass( 'updating-message notice-warning' )
.addClass( 'notice-error' )
.find( 'p' )
.removeAttr( 'aria-label' )
.text( errorMessage );
wp.a11y.speak( errorMessage, 'assertive' );
return false;
};
/**
* Potentially adds an AYS to a user attempting to leave the page.
*
* If an update is on-going and a user attempts to leave the page,
* opens an "Are you sure?" alert.
*
* @since 4.2.0
*/
wp.updates.beforeunload = function() {
if ( wp.updates.ajaxLocked ) {
return __( 'Updates may not complete if you navigate away from this page.' );
}
};
$( function() {
var $pluginFilter = $( '#plugin-filter, #plugin-information-footer' ),
$bulkActionForm = $( '#bulk-action-form' ),
$filesystemForm = $( '#request-filesystem-credentials-form' ),
$filesystemModal = $( '#request-filesystem-credentials-dialog' ),
$pluginSearch = $( '.plugins-php .wp-filter-search' ),
$pluginInstallSearch = $( '.plugin-install-php .wp-filter-search' );
settings = _.extend( settings, window._wpUpdatesItemCounts || {} );
if ( settings.totals ) {
wp.updates.refreshCount();
}
/*
* Whether a user needs to submit filesystem credentials.
*
* This is based on whether the form was output on the page server-side.
*
* @see {wp_print_request_filesystem_credentials_modal() in PHP}
*/
wp.updates.shouldRequestFilesystemCredentials = $filesystemModal.length > 0;
/**
* File system credentials form submit noop-er / handler.
*
* @since 4.2.0
*/
$filesystemModal.on( 'submit', 'form', function( event ) {
event.preventDefault();
// Persist the credentials input by the user for the duration of the page load.
wp.updates.filesystemCredentials.ftp.hostname = $( '#hostname' ).val();
wp.updates.filesystemCredentials.ftp.username = $( '#username' ).val();
wp.updates.filesystemCredentials.ftp.password = $( '#password' ).val();
wp.updates.filesystemCredentials.ftp.connectionType = $( 'input[name="connection_type"]:checked' ).val();
wp.updates.filesystemCredentials.ssh.publicKey = $( '#public_key' ).val();
wp.updates.filesystemCredentials.ssh.privateKey = $( '#private_key' ).val();
wp.updates.filesystemCredentials.fsNonce = $( '#_fs_nonce' ).val();
wp.updates.filesystemCredentials.available = true;
// Unlock and invoke the queue.
wp.updates.ajaxLocked = false;
wp.updates.queueChecker();
wp.updates.requestForCredentialsModalClose();
} );
/**
* Closes the request credentials modal when clicking the 'Cancel' button or outside of the modal.
*
* @since 4.2.0
*/
$filesystemModal.on( 'click', '[data-js-action="close"], .notification-dialog-background', wp.updates.requestForCredentialsModalCancel );
/**
* Hide SSH fields when not selected.
*
* @since 4.2.0
*/
$filesystemForm.on( 'change', 'input[name="connection_type"]', function() {
$( '#ssh-keys' ).toggleClass( 'hidden', ( 'ssh' !== $( this ).val() ) );
} ).trigger( 'change' );
/**
* Handles events after the credential modal was closed.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
* @param {string} job The install/update.delete request.
*/
$document.on( 'credential-modal-cancel', function( event, job ) {
var $updatingMessage = $( '.updating-message' ),
$message, originalText;
if ( 'import' === pagenow ) {
$updatingMessage.removeClass( 'updating-message' );
} else if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
if ( 'update-plugin' === job.action ) {
$message = $( 'tr[data-plugin="' + job.data.plugin + '"]' ).find( '.update-message' );
} else if ( 'delete-plugin' === job.action ) {
$message = $( '[data-plugin="' + job.data.plugin + '"]' ).find( '.row-actions a.delete' );
}
} else if ( 'themes' === pagenow || 'themes-network' === pagenow ) {
if ( 'update-theme' === job.action ) {
$message = $( '[data-slug="' + job.data.slug + '"]' ).find( '.update-message' );
} else if ( 'delete-theme' === job.action && 'themes-network' === pagenow ) {
$message = $( '[data-slug="' + job.data.slug + '"]' ).find( '.row-actions a.delete' );
} else if ( 'delete-theme' === job.action && 'themes' === pagenow ) {
$message = $( '.theme-actions .delete-theme' );
}
} else {
$message = $updatingMessage;
}
if ( $message && $message.hasClass( 'updating-message' ) ) {
originalText = $message.data( 'originaltext' );
if ( 'undefined' === typeof originalText ) {
originalText = $( '<p>' ).html( $message.find( 'p' ).data( 'originaltext' ) );
}
$message
.removeClass( 'updating-message' )
.html( originalText );
if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
if ( 'update-plugin' === job.action ) {
$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',
sprintf(
/* translators: %s: Plugin name. */
_x( 'Install %s now', 'plugin' ),
$message.data( 'name' )
)
);
}
}
}
wp.a11y.speak( __( 'Update canceled.' ) );
} );
/**
* Click handler for plugin updates in List Table view.
*
* @since 4.2.0
*
* @param {Event} event Event interface.
*/
$bulkActionForm.on( 'click', '[data-plugin] .update-link', function( event ) {
var $message = $( event.target ),
$pluginRow = $message.parents( 'tr' );
event.preventDefault();
if ( $message.hasClass( 'updating-message' ) || $message.hasClass( 'button-disabled' ) ) {
return;
}
wp.updates.maybeRequestFilesystemCredentials( event );
// Return the user to the input box of the plugin's table row after closing the modal.
wp.updates.$elToReturnFocusToFromCredentialsModal = $pluginRow.find( '.check-column input' );
wp.updates.updatePlugin( {
plugin: $pluginRow.data( 'plugin' ),
slug: $pluginRow.data( 'slug' )
} );
} );
/**
* Click handler for plugin updates in plugin install view.
*
* @since 4.2.0
*
* @param {Event} event Event interface.
*/
$pluginFilter.on( 'click', '.update-now', function( event ) {
var $button = $( event.target );
event.preventDefault();
if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
return;
}
wp.updates.maybeRequestFilesystemCredentials( event );
wp.updates.updatePlugin( {
plugin: $button.data( 'plugin' ),
slug: $button.data( 'slug' )
} );
} );
/**
* Click handler for plugin installs in plugin install view.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
*/
$pluginFilter.on( 'click', '.install-now', function( event ) {
var $button = $( event.target );
event.preventDefault();
if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
return;
}
if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
wp.updates.requestFilesystemCredentials( event );
$document.on( 'credential-modal-cancel', function() {
var $message = $( '.install-now.updating-message' );
$message
.removeClass( 'updating-message' )
.text( _x( 'Install Now', 'plugin' ) );
wp.a11y.speak( __( 'Update canceled.' ) );
} );
}
wp.updates.installPlugin( {
slug: $button.data( 'slug' )
} );
} );
/**
* Click handler for plugin activations in plugin activation modal view.
*
* @since 6.5.0
* @since 6.5.4 Redirect the parent window to the activation URL.
*
* @param {Event} event Event interface.
*/
$document.on( 'click', '#plugin-information-footer .activate-now', function( event ) {
event.preventDefault();
window.parent.location.href = $( event.target ).attr( 'href' );
});
/**
* Click handler for importer plugins installs in the Import screen.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
*/
$document.on( 'click', '.importer-item .install-now', function( event ) {
var $button = $( event.target ),
pluginName = $( this ).data( 'name' );
event.preventDefault();
if ( $button.hasClass( 'updating-message' ) ) {
return;
}
if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
wp.updates.requestFilesystemCredentials( event );
$document.on( 'credential-modal-cancel', function() {
$button
.removeClass( 'updating-message' )
.attr(
'aria-label',
sprintf(
/* translators: %s: Plugin name. */
_x( 'Install %s now', 'plugin' ),
pluginName
)
)
.text( _x( 'Install Now', 'plugin' ) );
wp.a11y.speak( __( 'Update canceled.' ) );
} );
}
wp.updates.installPlugin( {
slug: $button.data( 'slug' ),
pagenow: pagenow,
success: wp.updates.installImporterSuccess,
error: wp.updates.installImporterError
} );
} );
/**
* Click handler for plugin deletions.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
*/
$bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) {
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( confirmMessage ) ) {
return;
}
wp.updates.maybeRequestFilesystemCredentials( event );
wp.updates.deletePlugin( {
plugin: $pluginRow.data( 'plugin' ),
slug: $pluginRow.data( 'slug' )
} );
} );
/**
* Click handler for theme updates.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
*/
$document.on( 'click', '.themes-php.network-admin .update-link', function( event ) {
var $message = $( event.target ),
$themeRow = $message.parents( 'tr' );
event.preventDefault();
if ( $message.hasClass( 'updating-message' ) || $message.hasClass( 'button-disabled' ) ) {
return;
}
wp.updates.maybeRequestFilesystemCredentials( event );
// Return the user to the input box of the theme's table row after closing the modal.
wp.updates.$elToReturnFocusToFromCredentialsModal = $themeRow.find( '.check-column input' );
wp.updates.updateTheme( {
slug: $themeRow.data( 'slug' )
} );
} );
/**
* Click handler for theme deletions.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
*/
$document.on( 'click', '.themes-php.network-admin a.delete', function( event ) {
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( confirmMessage ) ) {
return;
}
wp.updates.maybeRequestFilesystemCredentials( event );
wp.updates.deleteTheme( {
slug: $themeRow.data( 'slug' )
} );
} );
/**
* Bulk action handler for plugins and themes.
*
* Handles both deletions and updates.
*
* @since 4.6.0
*
* @param {Event} event Event interface.
*/
$bulkActionForm.on( 'click', '[type="submit"]:not([name="clear-recent-list"])', function( event ) {
var bulkAction = $( event.target ).siblings( 'select' ).val(),
itemsSelected = $bulkActionForm.find( 'input[name="checked[]"]:checked' ),
success = 0,
error = 0,
errorMessages = [],
type, action;
// Determine which type of item we're dealing with.
switch ( pagenow ) {
case 'plugins':
case 'plugins-network':
type = 'plugin';
break;
case 'themes-network':
type = 'theme';
break;
default:
return;
}
// Bail if there were no items selected.
if ( ! itemsSelected.length ) {
bulkAction = false;
}
// Determine the type of request we're dealing with.
switch ( bulkAction ) {
case 'update-selected':
action = bulkAction.replace( 'selected', type );
break;
case 'delete-selected':
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;
}
action = bulkAction.replace( 'selected', type );
break;
default:
return;
}
wp.updates.maybeRequestFilesystemCredentials( event );
event.preventDefault();
// Un-check the bulk checkboxes.
$bulkActionForm.find( '.manage-column [type="checkbox"]' ).prop( 'checked', false );
$document.trigger( 'wp-' + type + '-bulk-' + bulkAction, itemsSelected );
// Find all the checkboxes which have been checked.
itemsSelected.each( function( index, element ) {
var $checkbox = $( element ),
$itemRow = $checkbox.parents( 'tr' );
// Only add update-able items to the update queue.
if ( 'update-selected' === bulkAction && ( ! $itemRow.hasClass( 'update' ) || $itemRow.find( 'notice-error' ).length ) ) {
// Un-check the box.
$checkbox.prop( 'checked', false );
return;
}
// Don't add items to the update queue again, even if the user clicks the update button several times.
if ( 'update-selected' === bulkAction && $itemRow.hasClass( 'is-enqueued' ) ) {
return;
}
$itemRow.addClass( 'is-enqueued' );
// Add it to the queue.
wp.updates.queue.push( {
action: action,
data: {
plugin: $itemRow.data( 'plugin' ),
slug: $itemRow.data( 'slug' )
}
} );
} );
// Display bulk notification for updates of any kind.
$document.on( 'wp-plugin-update-success wp-plugin-update-error wp-theme-update-success wp-theme-update-error', function( event, response ) {
var $itemRow = $( '[data-slug="' + response.slug + '"]' ),
$bulkActionNotice, itemName;
if ( 'wp-' + response.update + '-update-success' === event.type ) {
success++;
} else {
itemName = response.pluginName ? response.pluginName : $itemRow.find( '.column-primary strong' ).text();
error++;
errorMessages.push( itemName + ': ' + response.errorMessage );
}
$itemRow.find( 'input[name="checked[]"]:checked' ).prop( 'checked', false );
wp.updates.adminNotice = wp.template( 'wp-bulk-updates-admin-notice' );
var successMessage = null;
if ( success ) {
if ( 'plugin' === response.update ) {
successMessage = sprintf(
/* translators: %s: Number of plugins. */
_n( '%s plugin successfully updated.', '%s plugins successfully updated.', success ),
success
);
} else {
successMessage = sprintf(
/* translators: %s: Number of themes. */
_n( '%s theme successfully updated.', '%s themes successfully updated.', success ),
success
);
}
}
var errorMessage = null;
if ( error ) {
errorMessage = sprintf(
/* translators: %s: Number of failed updates. */
_n( '%s update failed.', '%s updates failed.', error ),
error
);
}
wp.updates.addAdminNotice( {
id: 'bulk-action-notice',
className: 'bulk-action-notice',
successMessage: successMessage,
errorMessage: errorMessage,
errorMessages: errorMessages,
type: response.update
} );
$bulkActionNotice = $( '#bulk-action-notice' ).on( 'click', 'button', function() {
// $( this ) is the clicked button, no need to get it again.
$( this )
.toggleClass( 'bulk-action-errors-collapsed' )
.attr( 'aria-expanded', ! $( this ).hasClass( 'bulk-action-errors-collapsed' ) );
// Show the errors list.
$bulkActionNotice.find( '.bulk-action-errors' ).toggleClass( 'hidden' );
} );
if ( error > 0 && ! wp.updates.queue.length ) {
$( 'html, body' ).animate( { scrollTop: 0 } );
}
} );
// Reset admin notice template after #bulk-action-notice was added.
$document.on( 'wp-updates-notice-added', function() {
wp.updates.adminNotice = wp.template( 'wp-updates-admin-notice' );
} );
// Check the queue, now that the event handlers have been added.
wp.updates.queueChecker();
} );
if ( $pluginInstallSearch.length ) {
$pluginInstallSearch.attr( 'aria-describedby', 'live-search-desc' );
}
// Track the previous search string length.
var previousSearchStringLength = 0;
wp.updates.shouldSearch = function( searchStringLength ) {
var shouldSearch = searchStringLength >= wp.updates.searchMinCharacters ||
previousSearchStringLength > wp.updates.searchMinCharacters;
previousSearchStringLength = searchStringLength;
return shouldSearch;
};
/**
* Handles changes to the plugin search box on the new-plugin page,
* searching the repository dynamically.
*
* @since 4.6.0
*/
$pluginInstallSearch.on( 'keyup input', _.debounce( function( event, eventtype ) {
var $searchTab = $( '.plugin-install-search' ), data, searchLocation,
searchStringLength = $pluginInstallSearch.val().length;
data = {
_ajax_nonce: wp.updates.ajaxNonce,
s: encodeURIComponent( event.target.value ),
tab: 'search',
type: $( '#typeselector' ).val(),
pagenow: pagenow
};
searchLocation = location.href.split( '?' )[ 0 ] + '?' + $.param( _.omit( data, [ '_ajax_nonce', 'pagenow' ] ) );
// Set the autocomplete attribute, turning off autocomplete 1 character before ajax search kicks in.
if ( wp.updates.shouldSearch( searchStringLength ) ) {
$pluginInstallSearch.attr( 'autocomplete', 'off' );
} else {
$pluginInstallSearch.attr( 'autocomplete', 'on' );
return;
}
// Clear on escape.
if ( 'keyup' === event.type && 27 === event.which ) {
event.target.value = '';
}
if ( wp.updates.searchTerm === data.s && 'typechange' !== eventtype ) {
return;
} else {
$pluginFilter.empty();
wp.updates.searchTerm = data.s;
}
if ( window.history && window.history.replaceState ) {
window.history.replaceState( null, '', searchLocation );
}
if ( ! $searchTab.length ) {
$searchTab = $( '<li class="plugin-install-search" />' )
.append( $( '<a />', {
'class': 'current',
'href': searchLocation,
'text': __( 'Search Results' )
} ) );
$( '.wp-filter .filter-links .current' )
.removeClass( 'current' )
.parents( '.filter-links' )
.prepend( $searchTab );
$pluginFilter.prev( 'p' ).remove();
$( '.plugins-popular-tags-wrapper' ).remove();
}
if ( 'undefined' !== typeof wp.updates.searchRequest ) {
wp.updates.searchRequest.abort();
}
$( 'body' ).addClass( 'loading-content' );
wp.updates.searchRequest = wp.ajax.post( 'search-install-plugins', data ).done( function( response ) {
$( 'body' ).removeClass( 'loading-content' );
$pluginFilter.append( response.items );
delete wp.updates.searchRequest;
if ( 0 === response.count ) {
wp.a11y.speak( __( 'You do not appear to have any plugins available at this time.' ) );
} else {
wp.a11y.speak(
sprintf(
/* translators: %s: Number of plugins. */
__( 'Number of plugins found: %d' ),
response.count
)
);
}
} );
}, 1000 ) );
if ( $pluginSearch.length ) {
$pluginSearch.attr( 'aria-describedby', 'live-search-desc' );
}
/**
* Handles changes to the plugin search box on the Installed Plugins screen,
* searching the plugin list dynamically.
*
* @since 4.6.0
*/
$pluginSearch.on( 'keyup input', _.debounce( function( event ) {
var data = {
_ajax_nonce: wp.updates.ajaxNonce,
s: encodeURIComponent( event.target.value ),
pagenow: pagenow,
plugin_status: 'all'
},
queryArgs,
searchStringLength = $pluginSearch.val().length;
// Set the autocomplete attribute, turning off autocomplete 1 character before ajax search kicks in.
if ( wp.updates.shouldSearch( searchStringLength ) ) {
$pluginSearch.attr( 'autocomplete', 'off' );
} else {
$pluginSearch.attr( 'autocomplete', 'on' );
return;
}
// Clear on escape.
if ( 'keyup' === event.type && 27 === event.which ) {
event.target.value = '';
}
if ( wp.updates.searchTerm === data.s ) {
return;
} else {
wp.updates.searchTerm = data.s;
}
queryArgs = _.object( _.compact( _.map( location.search.slice( 1 ).split( '&' ), function( item ) {
if ( item ) return item.split( '=' );
} ) ) );
data.plugin_status = queryArgs.plugin_status || 'all';
if ( window.history && window.history.replaceState ) {
window.history.replaceState( null, '', location.href.split( '?' )[ 0 ] + '?s=' + data.s + '&plugin_status=' + data.plugin_status );
}
if ( 'undefined' !== typeof wp.updates.searchRequest ) {
wp.updates.searchRequest.abort();
}
$bulkActionForm.empty();
$( 'body' ).addClass( 'loading-content' );
$( '.subsubsub .current' ).removeClass( 'current' );
wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) {
// Can we just ditch this whole subtitle business?
var $subTitle = $( '<span />' ).addClass( 'subtitle' ).html(
sprintf(
/* translators: %s: Search query. */
__( 'Search results for: %s' ),
'<strong>' + _.escape( decodeURIComponent( data.s ) ) + '</strong>'
) ),
$oldSubTitle = $( '.wrap .subtitle' );
if ( ! data.s.length ) {
$oldSubTitle.remove();
$( '.subsubsub .' + data.plugin_status + ' a' ).addClass( 'current' );
} else if ( $oldSubTitle.length ) {
$oldSubTitle.replaceWith( $subTitle );
} else {
$( '.wp-header-end' ).before( $subTitle );
}
$( 'body' ).removeClass( 'loading-content' );
$bulkActionForm.append( response.items );
delete wp.updates.searchRequest;
if ( 0 === response.count ) {
wp.a11y.speak( __( 'No plugins found. Try a different search.' ) );
} else {
wp.a11y.speak(
sprintf(
/* translators: %s: Number of plugins. */
__( 'Number of plugins found: %d' ),
response.count
)
);
}
} );
}, 500 ) );
/**
* Trigger a search event when the search form gets submitted.
*
* @since 4.6.0
*/
$document.on( 'submit', '.search-plugins', function( event ) {
event.preventDefault();
$( 'input.wp-filter-search' ).trigger( 'input' );
} );
/**
* Trigger a search event when the "Try Again" button is clicked.
*
* @since 4.9.0
*/
$document.on( 'click', '.try-again', function( event ) {
event.preventDefault();
$pluginInstallSearch.trigger( 'input' );
} );
/**
* Trigger a search event when the search type gets changed.
*
* @since 4.6.0
*/
$( '#typeselector' ).on( 'change', function() {
var $search = $( 'input[name="s"]' );
if ( $search.val().length ) {
$search.trigger( 'input', 'typechange' );
}
} );
/**
* Click handler for updating a plugin from the details modal on `plugin-install.php`.
*
* @since 4.2.0
*
* @param {Event} event Event interface.
*/
$( '#plugin_update_from_iframe' ).on( 'click', function( event ) {
var target = window.parent === window ? null : window.parent,
update;
$.support.postMessage = !! window.postMessage;
if ( false === $.support.postMessage || null === target || -1 !== window.parent.location.pathname.indexOf( 'update-core.php' ) ) {
return;
}
event.preventDefault();
update = {
action: 'update-plugin',
data: {
plugin: $( this ).data( 'plugin' ),
slug: $( this ).data( 'slug' )
}
};
target.postMessage( JSON.stringify( update ), window.location.origin );
} );
/**
* Handles postMessage events.
*
* @since 4.2.0
* @since 4.6.0 Switched `update-plugin` action to use the queue.
*
* @param {Event} event Event interface.
*/
$( window ).on( 'message', function( event ) {
var originalEvent = event.originalEvent,
expectedOrigin = document.location.protocol + '//' + document.location.host,
message;
if ( originalEvent.origin !== expectedOrigin ) {
return;
}
try {
message = JSON.parse( originalEvent.data );
} catch ( e ) {
return;
}
if ( ! message ) {
return;
}
if (
'undefined' !== typeof message.status &&
'undefined' !== typeof message.slug &&
'undefined' !== typeof message.text &&
'undefined' !== typeof message.ariaLabel
) {
var $card = $( '.plugin-card-' + message.slug ),
$message = $card.find( '[data-slug="' + message.slug + '"]' );
if ( 'undefined' !== typeof message.removeClasses ) {
$message.removeClass( message.removeClasses );
}
if ( 'undefined' !== typeof message.addClasses ) {
$message.addClass( message.addClasses );
}
if ( '' === message.ariaLabel ) {
$message.removeAttr( 'aria-label' );
} else {
$message.attr( 'aria-label', message.ariaLabel );
}
if ( 'dependencies-check-success' === message.status ) {
$message
.attr( 'data-name', message.pluginName )
.attr( 'data-slug', message.slug )
.attr( 'data-plugin', message.plugin )
.attr( 'href', message.href );
}
$message.text( message.text );
}
if ( 'undefined' === typeof message.action ) {
return;
}
switch ( message.action ) {
// Called from `wp-admin/includes/class-wp-upgrader-skins.php`.
case 'decrementUpdateCount':
/** @property {string} message.upgradeType */
wp.updates.decrementCount( message.upgradeType );
break;
case 'install-plugin':
case 'update-plugin':
if ( 'undefined' === typeof message.data || 'undefined' === typeof message.data.slug ) {
return;
}
message.data = wp.updates._addCallbacks( message.data, message.action );
wp.updates.queue.push( message );
wp.updates.queueChecker();
break;
}
} );
/**
* Adds a callback to display a warning before leaving the page.
*
* @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 );
Run Command [Bypass]
Run Command
وزير الاتصالات وتكنولوجيا المعلومات: نُخطط لمستقبل رقمي لا يعتمد على الورق” – tahkoom.com
كتبت : فرح سمير في ضوء التحولات المتسارعة التي يشهدها العالم في مجالات الاتصالات وتكنولوجيا
المعلومات، تسعى مصر، من خلال رؤية استراتيجية طموحة، إلى تحقيق التحول الرقمي الشامل وتعزيز مكانتها الإقليمية والدولية في الاقتصاد الرقمي.
وفي هذا الإطار، يسعدنا أن نلتقي مع معالي الدكتور” عمرو طلعت، وزير الاتصالات وتكنولوجيا المعلومات”،
لمناقشة أبرز إنجازات الوزارة، وخططها المستقبلية في مجالات التحول الرقمي، وتطوير البنية التحتية، ودعم الشركات الناشئة، وتنمية الكفاءات التكنولوجية، بالإضافة إلى استعراض جهود الدولة في تبني الذكاء الاصطناعي كأحد ركائز التنمية المستدامة.
يأتي هذا اللقاء لإلقاء الضوء على ملامح المشهد الرقمي في مصر، والتعرف على المبادرات والمشروعات الوطنية التي تسعى لتحقيق نقلة نوعية في حياة المواطنين وتعزيز تنافسية الاقتصاد المصري.
– في ضوء الجهود المستمرة لتطوير منصة مصر الرقمية وتوسيع نطاق خدماتها، ما هو الموقف الحالي للخدمات المقدمة من خلال المنصة من حيث عدد الخدمات المقدمة وعدد المستخدمين؟ وما هي الخطط المستقبلية لتعزيز دورها في تحسين كفاءة الخدمات الحكومية وتسهيل حياة المواطنين؟
نعمل على بناء مصر الرقمية التى ترتكز على الفكر الابتكارىوتطويع التكنولوجيات الحديثة للوصول إلى مجتمع رقمى متكامل يتم من خلاله دفع مسيرة التحول الرقمي ومعاونة كافة قطاعات الدولة فىتطويع التكنولوجيات الحديثة لرفع كفاءة الأداء الحكومى وزيادة الانتاجية، وتمكين الشباب من الحصول على فرص عمل متميزة، بالإضافة إلى دعم الابداع الرقمي وريادة الأعمال.
ويعد التحول الرقمي أحد المحاور الرئيسية لاستراتيجية مصر الرقمية. ونسعى من خلال مشروعات التحول الرقمي التعاون مع كافة قطاعات الدولة فى تطويع التكنولوجيات الحديثة لرفع كفاءة الأداء الحكومى وزيادة الإنتاجية وتحسين جودة الخدمات الحكومية المقدمة للمواطنين.
وفي هذا الإطار، تُمثل منصة “مصر الرقمية” أحد أبرز ثمار هذا التوجه؛ حيت توفر المنصة حتى الآن أكثر من 180 خدمة حكومية على المنصة، كما يوجد حاليا 9 مليون مواطن مسجل على منصة مصر الرقمية. ونعمل على التوسع في عدد الخدمات الخدمات الحكومية على المنصة بشكل مستمر، حيث نخطط لإطلاق المزيد منها خلال العام الجاري..
كما نخطط حاليا للمرحلة الثانية من رقمنة الخدمات الحكومية والتي سيتم خلالها قصر تقديم بعض الخدمات الحكومية على المنصات الرقمية بحيث أنه لا يتم تقديمها إلا من خلال منصة رقمية لتخفيف العبء على المكاتب المقدمة للخدمة وتخفيف الجهد على العاملين المقدمين لهذه الخدمات. حيث من المقرر أن يكون هناك 55 خدمة حكومية مقدمة فقط من خلال منصة مصر الرقمية وذلك بنهاية هذا العام وتشمل خدمات التأمينات والسجل التجاري والتعليم وخدمات متعلقة بالأحوال الشخصية بالتعاون مع وزارة الداخلية.
– ما هي أبرز مشروعات التحول الرقمي الجاري تنفيذها؟
هناك العديد من المشروعات التى يتم تنفيذها بالتعاون مع مختلف قطاعات الدولة أبرزها مشروعات تستهدف تفعيل دور التحول الرقمىفى رفع كفاءة خدمات الرعاية الصحية لتطوير الخدمات المقدمة للمواطنين وحوكمة المنظومة وتشمل مشروع النشر المرقمن لمنظومة التأمين الصحى الشامل، والتوسع فى استخدام تقنيات إنترنت الأشياء والتشخيص والتطبيب عن بعد لخدمة المناطق النائية بالتعاون مع وزارتى الصحة والتعليم العالى والبحث العلمى.
كما يتم التعاون مع وزارة الاستثمار والتجارة الخارجية في عدد من المشروعات التي تستهدف إنشاء منظومة رقمية لخدمة الكيانات الاقتصادية وتحسين خدمة المستثمرين في مراحل التعامل مع الجهات الحكومية، وعمل رقمنة كاملة لدورة حياة الشركات تبدأ من تأسيس الشركات حتى تصفية الأعمال. بالإضافة إلى التعاون مع وزارة الزرعة في تنفيذ مشروعات ميكنة الحيازة الزراعية وإصدار كارت الفلاح، وتطوير البوابة المعلوماتية لوزارة الزراعة واستصلاح الأراضى، وحصر وإدارة املاك وأصول وزارة الزراعة، وميكنة الهيئة العامة لمشروعات التعمير والتنمية الزراعية.
كما يتم التعاون مع وزارة الأوقاف في تنفيذ مشروع ميكنة خدمات وزارة الأوقاف وإطلاقها على منصة مصر الرقمية، ورقمنة عملية تلقى تبرعات صكوك الأضاحي، ومشروع رقمنة أطلس وزارة الأوقاف، بالإضافة إلى التعاون فى تنفيذ مشروع حصر وإدارة املاك وأصول الدولة لتمكين وزارة الأوقاف وهيئة الأوقاف المصرية من إدارة أملاكهم على نحو كفء.
– ما العوامل التي ساهمت في حصول مصر على لقب أسرع إنترنت ثابت في أفريقيا في فترات سابقة؟ وما هي أبرز الجهود الحالية والمستقبلية لتحسين خدمات الاتصالات بشكل عام؟
تمثل البنية التحتية الرقمية أحد الممكنات الرئيسية لاستراتيجية مصر الرقمية من أجل تقديم خدمات اتصالات ذات كفاءة عالية، ودعم عمليات التحول الرقمي؛ ولذا كانت من أهم محاور العمل التي تم البدء في تنفيذها لتحقيق استراتيجية مصر الرقمية هو تطوير البنية التحتية الرقمية ورفع كفاءة الانترنت؛ وعند البدء في تنفيذ المشروع في 2018 كان ترتيب مصر في متوسط سرعة الانترنت في المركز الأربعين على مستوى القارة الأفريقية؛ وتم تنفيذ المشروع باستثمارات منذ 2018. حيث تم ضخ استثمارات بقيمة 3.5 مليار دولار فى تطوير البنية التحتية الرقمية ساهمت في مضاعفة سرعة الانترنت الثابت أكثر من 15 مرة خلال 7 أعوام.
وفيما يخض الجهود المعنية بتحسين خدمات المحمول؛ فقد تم مؤخرا منح تراخيص تشغيل خدمات الجيل الخامس للتليفون المحمول لشركات المحمول الأربعة العاملة فى مصر بقيمة 675 مليون دولار. كما يتم استكمال العمل فى تنفيذ خطة لزيادة عدد أبراج المحمول حيث بلغ الاجمالى حتى الان نحو 37 ألف برج محمول فى كافة أنحاء الجمهورية. ومن المقرر خلال منتصف العام الحالي الانتهاء من تغطية كافة الطرق السريعة مكتملة الإنشاء بخدمات المحمول. وذلك بالإضافة الى إطلاق خدمات جديدة لتحسين الجودة ومن أبرزها الشرائح المدمجة eSIM، وخدمات مكالمات المحمول عبر شبكة الإنترنت الهوائى WiFi Calling.
كذلك يتم تنفيذ عدد من المشروعات لتطوير البنية التحتية الرقمية فىقرى مشروع حياة كريمة لخدمة 60 مليون مواطن وتستهدف المشروعات توصيل كابلات الألياف الضوئية إلى 9.3 مليون مبنى لتوفير الانترنت فائق السرعة، وكذلك نشر 3700 برج محمول لتحسين خدمات الاتصالات بالقرى. باستثمارات تقدر بنحو 8.8 مليار جنيه في مختلف مراحل المشروع، ومن المخطط الانتهاء من كافة مراحل المشروع فى منتصف العام الحالي.
– ما هي أبرز جهود وزارة الاتصالات وتكنولوجيا المعلومات في بناء القدرات الرقمية للشباب وتأهيلهم لسوق العمل المحلي والعالمي؟
تحظى مصر بثورة بشرية الأغلبية منها شباب، وتخرج مصر سنويًا أكثر من 50,000 متخصص في تكنولوجيا المعلومات؛ وتعد الكفاءات الرقمية أبرز العناصر الجاذبة لاستثمارات الشركات العالمية لمصر. لذا تحرص وزارة الاتصالات وتكنولوجيا المعلومات على توفير مبادرات لكل فئات المجتمع في مختلف المراحل العمرية ومن خريجي مختلف التخصصات سواء تكنولوجية أو غير تكنولوجية وفي جميع أنحاء الجمهورية.
وفى خلال 7 أعوام تضاعفت موازنة التدريب بالوزارة 40 مرة، وتضاعف عدد المتدربين 125 مرة ليصل إلى 500 ألف متدرب بموازنة 2 مليار جنيه خلال العام المالى الحالي.
ونحرص على أن تشمل البرامج التدريبية كافة المراحل العمرية بدءا من سن 8 سنوات من خلال مبادرات أجيال مصر الرقمية التي توفر برامج تدريبية متخصصة لطلاب المدارس والجامعات والخريجين بهدف تنمية مهاراتهم فى التخصصات الحديثة فى مجال الاتصالات وتكنولوجيا المعلومات ومنها الذكاء الاصطناعى، وعلوم البيانات، والأمن السيبرانى، والنظم المدمجة، والفنون الرقمية، وإدارة موارد المؤسسات وغيرها بما يتواكب مع متطلبات سوق العمل المحلى والعالمى، كما يتم توفير برامج تعليمية متخصصة بأعلى معايير الكفاءة والجودة في مجال الاتصالات وتكنولوجيا المعلومات من خلال مدارسwe للتكنولوجيا التطبيقية. حيث تم إطلاق 19 مدرسة في إطار خطة تستهدف إنشاء مدرسة بكل محافظة.
كما يتم توفير برامج تعليمية متقدمة فى أحدث مجالات الاتصالات وتكنولوجيا المعلومات مثل: الذكاء الاصطناعى وعلوم وهندسة البيانات، وهندسة الإلكترونيات والاتصالات، وتحليل الأعمال والتسويق الرقمي، والفنون الرقمية من خلال جامعة مصر للمعلوماتية، التى تعد أول جامعة متخصصة فى الاتصالات وتكنولوجيا المعلومات فى أفريقيا.
ومؤخرا أطلقنا مبادرة الرواد الرقميون التي توفر مجموعة من البرامج الأكاديمية المكثفة التى تتيح الحصول على دبلومات وماجستير مهنىأو علمى في التخصصات التكنولوجية بالشراكة مع كبرى الشركات التكنولوجية المحلية والعالمية وكذلك الشركات المتخصصة فى تنمية المهارات الشخصية واللغوية والجامعات المرموقة
– ما أشكال الدعم التي تقدمها الوزارة لرواد الأعمال في مجال التكنولوجيا والشركات الناشئة؟
صناعة الاتصالات وتكنولوجيا المعلومات هي صناعة قوامها الابداع؛ لذا نحرص على تحفيز الشباب على العمل الابتكاري وتعزيز نمو قطاع الشركات التكنولوجية الناشئة، وتذليل العقبات التى تواجه رواد الأعمال في مختلف المحافظات.
ويمكن للشركات الناشئة والمبتكرين الالتحاق بالبرامج المقدمة في مراكز إبداع مصر الرقمية في مختلف المحافظات والتي يتم من خلالها تقديم سلسلة القيمة للشركات الناشئة والتي تبدأ بالعصف الفكرى وبناء أفكار تصلح لإقامة شركات ثم تدريب الشباب أصحاب هذه الأفكار تدريب تقني واقتصادى وتسويقى ثم بناء خطة لتأسيس الشركة تمهيدا لبدء النشاط، ثم إنشاء الشركات واحتضانها، ثم عقد محافل تشبيك لجذب الاستثمارات.
ولقد تم حتى الآن إنشاء 23 مركزا في مختلف المحافظات في إطار خطة تستهدف إنشاء مركز إبداع مصر الرقمية بكل محافظة. كما تم الاتفاق مع شركتين عالميتين مرموقتين فى مجال تمكين الشركات الناشئة وتسريع نموها للاستفادة من الخبرات العالمية في تنمية الاستثمارات فى الشركات الناشئة.
ونعمل حاليا على التوسع في انشاء الحاضنات التي تركز على تخصصات تكنولوجية محددة؛ حيث تم إنشاء معمل الابتكار الحكومى في مركز إبداع مصر الرقمية الجيزة الذي يهدف إلى تمكين الشركات الناشئة المبتكرة المهتمة بمجال التكنولوجيا الحكومية، وتعزيز الابتكار فى مجال التكنولوجيا الحكومية مما يساهم فىتحسين جودة الخدمات الحكومية المقدمة للمواطنين، وتعزيز مكانة مصر في المؤشرات العالمية للتحول الرقمي.
ونعمل حاليا على توفير بنية تحتية حوسبية للشركات الناشئة التي تعمل في مجال الذكاء الاصطناعي من خلال مركز الابتكار التطبيقي، كما نوفر منظومة متكاملة لدعم الشركات العاملة في مجال تصميم الالكترونيات من خلال مركز امحوتب للابداع والتطوير في مدينة المعرفة بالعاصمة الإدارية الجديدة. وذلك بالإضافة الى دعم الشركات الناشئة العاملة في مجال التكنولوجيات المساعدة من خلال الأكاديمية الوطنية لتكنولوجيا المعلومات للأشخاص ذوي الإعاقة.
– يشهد سوق العمل تحولًا متزايدًا نحو العمل الحر. ما هي أبرز المبادرات وأنواع الدعم التي تقدمها الوزارة للمهنيين المستقلين لتمكينهم وتنمية مساهمتهم في الاقتصاد الرقمي؟
تولي وزارة الاتصالات وتكنولوجيا المعلومات أهمية كبيرة لدعم المهنيين المستقلين، بما يمثله من أهمية في توفير فرص عمل جديدة وغير تقليدية، والمساهمة في دعم نمو الاقتصاد الرقمي. لذلك، حرصنا على أن تشتمل جميع المبادرات التدريبية التي تقدمها الوزارة على توفير تدريب متخصص في مهارات العمل الحر، إلى جانب التدريب التقني، بما يتيح للمتدربين خيارات متعددة للالتحاق بسوق العمل، سواء من خلال وظيفة تقليدية أو من خلال العمل كمحترف مستقل على المنصات الرقمية.
وترتكز استراتيجية الوزارة لدعم العمل الحر على أربعة محاور رئيسية وهي: إتاحة البرامج التدريبية، وتطوير خدمات الإنترنت، وتوفير مساحات عمل مجهزة، إلى جانب تقديم الحوافز والدعم الفني والمالي للمهنيين المستقلين.
ومن خلال الجهات التدريبية التابعة للوزارة كالمعهد القومي للاتصالات، ومعهد تكنولوجيا المعلومات، ومدارس WE للتكنولوجيا التطبيقية، والبرامج التدريبية المتاحة على منصة “مهارة تك”، وضمن مبادرة “رواد مصر الرقمية”، ومبادرة “ITIDA Gigs”، نعمل على إعداد وتأهيل كفاءات قادرة على المنافسة في سوق العمل الحر، وصقلها بالمهارات التقنية المطلوبة، ومهارات تقديم العروض والتسعير والتفاوض، إلى جانب تدريب المتدربين على كيفية إنشاء حسابات فعالة على منصات العمل الحر الدولية.
كما نضخ استثمارات مستمرة لتطوير شبكة الإنترنت، وتوفير بيئة عمل محفزة من خلال مراكز “إبداع مصر الرقمية” المنتشرة في مختلف المحافظات، والتي وصل عددها إلى 23 مركزاً، ومستهدف زيادتها إلى 27 مركزاً بنهاية العام الجاري.
ندعم أيضًا المهنيين المستقلين من خلال إقامة معسكرات تدريب تطبيقية وملتقيات تشبيك، وإطلاق مبادرة بالتعاون مع بنك ناصر لتوفير تمويل ميسر لشراء أجهزة الحاسب الآلي. كذلك تم بناء منصة رقمية للمهنيين المستقلين تتضمن مجموعة من المميزات لهم بما فىذلك توفير خدمات استشارية فى مجال الضرائب، وتمكينهم من كتابة عقود منمطة وفتح حساب بنكى.
في ختام لقاءنا مع معالي الوزير تتضح ملامح الرؤية المستقبلية التي تتبناها الدولة لبناء مجتمع رقمي متكامل، يدعم الابتكار، ويعزز الشمول الرقمي، ويرسخ مكانة مصر كمركز إقليمي في مجالات التكنولوجيا الحديثة.
وقد أبرز الحوار حجم الجهود المبذولة في تطوير البنية التحتية الرقمية، ودعم رواد الأعمال، وتأهيل الكوادر الوطنية، بما يتماشى مع مستهدفات “مصر الرقمية” واستراتيجية الذكاء الاصطناعي.
كما تبين لنا أيضا قوة مصر العقلية المبتكرة من قديم الازل حيث أوضح لنا معالي الوزير اثناء زيارته شعار الوزارة الرسمي، حيث تتمركز ايست أله التدوين للمعلومات وحفظها، فهى ركز لقوة مصر من قديم الزمان حتى الان على الابتكار والتطور التكنولوجي والعلمي
نتوجه بالشكر إلى معالي الوزير “عمرو طلعت ” على سعة صدره وإجاباته الوافية، ونتطلع إلى استمرار هذا الزخم لتحقيق المزيد من الإنجازات التي تصب في مصلحة المواطن المصري وتواكب تطورات العصر الرقمي.
ممثلين فريق عمل مجلة تحكم مع معالي وزير الاتصالات وتكنولوجيا المعلومات “عمرو طلعت”
زر الذهاب إلى الأعلى