Diff: STRATO-apps/wordpress_03/app/wp-admin/js/updates.js
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
/**
2
+
* Functions for ajaxified updates, deletions and installs inside the WordPress admin.
3
+
*
4
+
* @version 4.2.0
5
+
* @output wp-admin/js/updates.js
6
+
*/
7
+
8
+
/* global pagenow, _wpThemeSettings */
9
+
10
+
/**
11
+
* @param {jQuery} $ jQuery object.
12
+
* @param {object} wp WP object.
13
+
* @param {object} settings WP Updates settings.
14
+
* @param {string} settings.ajax_nonce Ajax nonce.
15
+
* @param {object=} settings.plugins Base names of plugins in their different states.
16
+
* @param {Array} settings.plugins.all Base names of all plugins.
17
+
* @param {Array} settings.plugins.active Base names of active plugins.
18
+
* @param {Array} settings.plugins.inactive Base names of inactive plugins.
19
+
* @param {Array} settings.plugins.upgrade Base names of plugins with updates available.
20
+
* @param {Array} settings.plugins.recently_activated Base names of recently activated plugins.
21
+
* @param {Array} settings.plugins['auto-update-enabled'] Base names of plugins set to auto-update.
22
+
* @param {Array} settings.plugins['auto-update-disabled'] Base names of plugins set to not auto-update.
23
+
* @param {object=} settings.themes Slugs of themes in their different states.
24
+
* @param {Array} settings.themes.all Slugs of all themes.
25
+
* @param {Array} settings.themes.upgrade Slugs of themes with updates available.
26
+
* @param {Arrat} settings.themes.disabled Slugs of disabled themes.
27
+
* @param {Array} settings.themes['auto-update-enabled'] Slugs of themes set to auto-update.
28
+
* @param {Array} settings.themes['auto-update-disabled'] Slugs of themes set to not auto-update.
29
+
* @param {object=} settings.totals Combined information for available update counts.
30
+
* @param {number} settings.totals.count Holds the amount of available updates.
31
+
*/
32
+
(function( $, wp, settings ) {
33
+
var $document = $( document ),
34
+
__ = wp.i18n.__,
35
+
_x = wp.i18n._x,
36
+
_n = wp.i18n._n,
37
+
_nx = wp.i18n._nx,
38
+
sprintf = wp.i18n.sprintf;
39
+
40
+
wp = wp || {};
41
+
42
+
/**
43
+
* The WP Updates object.
44
+
*
45
+
* @since 4.2.0
46
+
*
47
+
* @namespace wp.updates
48
+
*/
49
+
wp.updates = {};
50
+
51
+
/**
52
+
* Removed in 5.5.0, needed for back-compatibility.
53
+
*
54
+
* @since 4.2.0
55
+
* @deprecated 5.5.0
56
+
*
57
+
* @type {object}
58
+
*/
59
+
wp.updates.l10n = {
60
+
searchResults: '',
61
+
searchResultsLabel: '',
62
+
noPlugins: '',
63
+
noItemsSelected: '',
64
+
updating: '',
65
+
pluginUpdated: '',
66
+
themeUpdated: '',
67
+
update: '',
68
+
updateNow: '',
69
+
pluginUpdateNowLabel: '',
70
+
updateFailedShort: '',
71
+
updateFailed: '',
72
+
pluginUpdatingLabel: '',
73
+
pluginUpdatedLabel: '',
74
+
pluginUpdateFailedLabel: '',
75
+
updatingMsg: '',
76
+
updatedMsg: '',
77
+
updateCancel: '',
78
+
beforeunload: '',
79
+
installNow: '',
80
+
pluginInstallNowLabel: '',
81
+
installing: '',
82
+
pluginInstalled: '',
83
+
themeInstalled: '',
84
+
installFailedShort: '',
85
+
installFailed: '',
86
+
pluginInstallingLabel: '',
87
+
themeInstallingLabel: '',
88
+
pluginInstalledLabel: '',
89
+
themeInstalledLabel: '',
90
+
pluginInstallFailedLabel: '',
91
+
themeInstallFailedLabel: '',
92
+
installingMsg: '',
93
+
installedMsg: '',
94
+
importerInstalledMsg: '',
95
+
aysDelete: '',
96
+
aysDeleteUninstall: '',
97
+
aysBulkDelete: '',
98
+
aysBulkDeleteThemes: '',
99
+
deleting: '',
100
+
deleteFailed: '',
101
+
pluginDeleted: '',
102
+
themeDeleted: '',
103
+
livePreview: '',
104
+
activatePlugin: '',
105
+
activateTheme: '',
106
+
activatePluginLabel: '',
107
+
activateThemeLabel: '',
108
+
activateImporter: '',
109
+
activateImporterLabel: '',
110
+
unknownError: '',
111
+
connectionError: '',
112
+
nonceError: '',
113
+
pluginsFound: '',
114
+
noPluginsFound: '',
115
+
autoUpdatesEnable: '',
116
+
autoUpdatesEnabling: '',
117
+
autoUpdatesEnabled: '',
118
+
autoUpdatesDisable: '',
119
+
autoUpdatesDisabling: '',
120
+
autoUpdatesDisabled: '',
121
+
autoUpdatesError: ''
122
+
};
123
+
124
+
wp.updates.l10n = window.wp.deprecateL10nObject( 'wp.updates.l10n', wp.updates.l10n, '5.5.0' );
125
+
126
+
/**
127
+
* User nonce for ajax calls.
128
+
*
129
+
* @since 4.2.0
130
+
*
131
+
* @type {string}
132
+
*/
133
+
wp.updates.ajaxNonce = settings.ajax_nonce;
134
+
135
+
/**
136
+
* Current search term.
137
+
*
138
+
* @since 4.6.0
139
+
*
140
+
* @type {string}
141
+
*/
142
+
wp.updates.searchTerm = '';
143
+
144
+
/**
145
+
* Minimum number of characters before an ajax search is fired.
146
+
*
147
+
* @since 6.7.0
148
+
*
149
+
* @type {number}
150
+
*/
151
+
wp.updates.searchMinCharacters = 2;
152
+
153
+
/**
154
+
* Whether filesystem credentials need to be requested from the user.
155
+
*
156
+
* @since 4.2.0
157
+
*
158
+
* @type {bool}
159
+
*/
160
+
wp.updates.shouldRequestFilesystemCredentials = false;
161
+
162
+
/**
163
+
* Filesystem credentials to be packaged along with the request.
164
+
*
165
+
* @since 4.2.0
166
+
* @since 4.6.0 Added `available` property to indicate whether credentials have been provided.
167
+
*
168
+
* @type {Object}
169
+
* @property {Object} filesystemCredentials.ftp Holds FTP credentials.
170
+
* @property {string} filesystemCredentials.ftp.host FTP host. Default empty string.
171
+
* @property {string} filesystemCredentials.ftp.username FTP user name. Default empty string.
172
+
* @property {string} filesystemCredentials.ftp.password FTP password. Default empty string.
173
+
* @property {string} filesystemCredentials.ftp.connectionType Type of FTP connection. 'ssh', 'ftp', or 'ftps'.
174
+
* Default empty string.
175
+
* @property {Object} filesystemCredentials.ssh Holds SSH credentials.
176
+
* @property {string} filesystemCredentials.ssh.publicKey The public key. Default empty string.
177
+
* @property {string} filesystemCredentials.ssh.privateKey The private key. Default empty string.
178
+
* @property {string} filesystemCredentials.fsNonce Filesystem credentials form nonce.
179
+
* @property {bool} filesystemCredentials.available Whether filesystem credentials have been provided.
180
+
* Default 'false'.
181
+
*/
182
+
wp.updates.filesystemCredentials = {
183
+
ftp: {
184
+
host: '',
185
+
username: '',
186
+
password: '',
187
+
connectionType: ''
188
+
},
189
+
ssh: {
190
+
publicKey: '',
191
+
privateKey: ''
192
+
},
193
+
fsNonce: '',
194
+
available: false
195
+
};
196
+
197
+
/**
198
+
* Whether we're waiting for an Ajax request to complete.
199
+
*
200
+
* @since 4.2.0
201
+
* @since 4.6.0 More accurately named `ajaxLocked`.
202
+
*
203
+
* @type {bool}
204
+
*/
205
+
wp.updates.ajaxLocked = false;
206
+
207
+
/**
208
+
* Admin notice template.
209
+
*
210
+
* @since 4.6.0
211
+
*
212
+
* @type {function}
213
+
*/
214
+
wp.updates.adminNotice = wp.template( 'wp-updates-admin-notice' );
215
+
216
+
/**
217
+
* Update queue.
218
+
*
219
+
* If the user tries to update a plugin while an update is
220
+
* already happening, it can be placed in this queue to perform later.
221
+
*
222
+
* @since 4.2.0
223
+
* @since 4.6.0 More accurately named `queue`.
224
+
*
225
+
* @type {Array.object}
226
+
*/
227
+
wp.updates.queue = [];
228
+
229
+
/**
230
+
* Holds a jQuery reference to return focus to when exiting the request credentials modal.
231
+
*
232
+
* @since 4.2.0
233
+
*
234
+
* @type {jQuery}
235
+
*/
236
+
wp.updates.$elToReturnFocusToFromCredentialsModal = undefined;
237
+
238
+
/**
239
+
* Adds or updates an admin notice.
240
+
*
241
+
* @since 4.6.0
242
+
*
243
+
* @param {Object} data
244
+
* @param {*=} data.selector Optional. Selector of an element to be replaced with the admin notice.
245
+
* @param {string=} data.id Optional. Unique id that will be used as the notice's id attribute.
246
+
* @param {string=} data.className Optional. Class names that will be used in the admin notice.
247
+
* @param {string=} data.message Optional. The message displayed in the notice.
248
+
* @param {number=} data.successes Optional. The amount of successful operations.
249
+
* @param {number=} data.errors Optional. The amount of failed operations.
250
+
* @param {Array=} data.errorMessages Optional. Error messages of failed operations.
251
+
*
252
+
*/
253
+
wp.updates.addAdminNotice = function( data ) {
254
+
var $notice = $( data.selector ),
255
+
$headerEnd = $( '.wp-header-end' ),
256
+
$adminNotice;
257
+
258
+
delete data.selector;
259
+
$adminNotice = wp.updates.adminNotice( data );
260
+
261
+
// Check if this admin notice already exists.
262
+
if ( ! $notice.length ) {
263
+
$notice = $( '#' + data.id );
264
+
}
265
+
266
+
if ( $notice.length ) {
267
+
$notice.replaceWith( $adminNotice );
268
+
} else if ( $headerEnd.length ) {
269
+
$headerEnd.after( $adminNotice );
270
+
} else {
271
+
if ( 'customize' === pagenow ) {
272
+
$( '.customize-themes-notifications' ).append( $adminNotice );
273
+
} else {
274
+
$( '.wrap' ).find( '> h1' ).after( $adminNotice );
275
+
}
276
+
}
277
+
278
+
$document.trigger( 'wp-updates-notice-added' );
279
+
};
280
+
281
+
/**
282
+
* Handles Ajax requests to WordPress.
283
+
*
284
+
* @since 4.6.0
285
+
*
286
+
* @param {string} action The type of Ajax request ('update-plugin', 'install-theme', etc).
287
+
* @param {Object} data Data that needs to be passed to the ajax callback.
288
+
* @return {$.promise} A jQuery promise that represents the request,
289
+
* decorated with an abort() method.
290
+
*/
291
+
wp.updates.ajax = function( action, data ) {
292
+
var options = {};
293
+
294
+
if ( wp.updates.ajaxLocked ) {
295
+
wp.updates.queue.push( {
296
+
action: action,
297
+
data: data
298
+
} );
299
+
300
+
// Return a Deferred object so callbacks can always be registered.
301
+
return $.Deferred();
302
+
}
303
+
304
+
wp.updates.ajaxLocked = true;
305
+
306
+
if ( data.success ) {
307
+
options.success = data.success;
308
+
delete data.success;
309
+
}
310
+
311
+
if ( data.error ) {
312
+
options.error = data.error;
313
+
delete data.error;
314
+
}
315
+
316
+
options.data = _.extend( data, {
317
+
action: action,
318
+
_ajax_nonce: wp.updates.ajaxNonce,
319
+
_fs_nonce: wp.updates.filesystemCredentials.fsNonce,
320
+
username: wp.updates.filesystemCredentials.ftp.username,
321
+
password: wp.updates.filesystemCredentials.ftp.password,
322
+
hostname: wp.updates.filesystemCredentials.ftp.hostname,
323
+
connection_type: wp.updates.filesystemCredentials.ftp.connectionType,
324
+
public_key: wp.updates.filesystemCredentials.ssh.publicKey,
325
+
private_key: wp.updates.filesystemCredentials.ssh.privateKey
326
+
} );
327
+
328
+
return wp.ajax.send( options ).always( wp.updates.ajaxAlways );
329
+
};
330
+
331
+
/**
332
+
* Actions performed after every Ajax request.
333
+
*
334
+
* @since 4.6.0
335
+
*
336
+
* @param {Object} response
337
+
* @param {Array=} response.debug Optional. Debug information.
338
+
* @param {string=} response.errorCode Optional. Error code for an error that occurred.
339
+
*/
340
+
wp.updates.ajaxAlways = function( response ) {
341
+
if ( ! response.errorCode || 'unable_to_connect_to_filesystem' !== response.errorCode ) {
342
+
wp.updates.ajaxLocked = false;
343
+
wp.updates.queueChecker();
344
+
}
345
+
346
+
if ( 'undefined' !== typeof response.debug && window.console && window.console.log ) {
347
+
_.map( response.debug, function( message ) {
348
+
// Remove all HTML tags and write a message to the console.
349
+
window.console.log( wp.sanitize.stripTagsAndEncodeText( message ) );
350
+
} );
351
+
}
352
+
};
353
+
354
+
/**
355
+
* Refreshes update counts everywhere on the screen.
356
+
*
357
+
* @since 4.7.0
358
+
*/
359
+
wp.updates.refreshCount = function() {
360
+
var $adminBarUpdates = $( '#wp-admin-bar-updates' ),
361
+
$dashboardNavMenuUpdateCount = $( 'a[href="update-core.php"] .update-plugins' ),
362
+
$pluginsNavMenuUpdateCount = $( 'a[href="plugins.php"] .update-plugins' ),
363
+
$appearanceNavMenuUpdateCount = $( 'a[href="themes.php"] .update-plugins' ),
364
+
itemCount;
365
+
366
+
$adminBarUpdates.find( '.ab-label' ).text( settings.totals.counts.total );
367
+
$adminBarUpdates.find( '.updates-available-text' ).text(
368
+
sprintf(
369
+
/* translators: %s: Total number of updates available. */
370
+
_n( '%s update available', '%s updates available', settings.totals.counts.total ),
371
+
settings.totals.counts.total
372
+
)
373
+
);
374
+
375
+
// Remove the update count from the toolbar if it's zero.
376
+
if ( 0 === settings.totals.counts.total ) {
377
+
$adminBarUpdates.find( '.ab-label' ).parents( 'li' ).remove();
378
+
}
379
+
380
+
// Update the "Updates" menu item.
381
+
$dashboardNavMenuUpdateCount.each( function( index, element ) {
382
+
element.className = element.className.replace( /count-\d+/, 'count-' + settings.totals.counts.total );
383
+
} );
384
+
if ( settings.totals.counts.total > 0 ) {
385
+
$dashboardNavMenuUpdateCount.find( '.update-count' ).text( settings.totals.counts.total );
386
+
} else {
387
+
$dashboardNavMenuUpdateCount.remove();
388
+
}
389
+
390
+
// Update the "Plugins" menu item.
391
+
$pluginsNavMenuUpdateCount.each( function( index, element ) {
392
+
element.className = element.className.replace( /count-\d+/, 'count-' + settings.totals.counts.plugins );
393
+
} );
394
+
if ( settings.totals.counts.total > 0 ) {
395
+
$pluginsNavMenuUpdateCount.find( '.plugin-count' ).text( settings.totals.counts.plugins );
396
+
} else {
397
+
$pluginsNavMenuUpdateCount.remove();
398
+
}
399
+
400
+
// Update the "Appearance" menu item.
401
+
$appearanceNavMenuUpdateCount.each( function( index, element ) {
402
+
element.className = element.className.replace( /count-\d+/, 'count-' + settings.totals.counts.themes );
403
+
} );
404
+
if ( settings.totals.counts.total > 0 ) {
405
+
$appearanceNavMenuUpdateCount.find( '.theme-count' ).text( settings.totals.counts.themes );
406
+
} else {
407
+
$appearanceNavMenuUpdateCount.remove();
408
+
}
409
+
410
+
// Update list table filter navigation.
411
+
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
412
+
itemCount = settings.totals.counts.plugins;
413
+
} else if ( 'themes' === pagenow || 'themes-network' === pagenow ) {
414
+
itemCount = settings.totals.counts.themes;
415
+
}
416
+
417
+
if ( itemCount > 0 ) {
418
+
$( '.subsubsub .upgrade .count' ).text( '(' + itemCount + ')' );
419
+
} else {
420
+
$( '.subsubsub .upgrade' ).remove();
421
+
$( '.subsubsub li:last' ).html( function() { return $( this ).children(); } );
422
+
}
423
+
};
424
+
425
+
/**
426
+
* Sends a message from a modal to the main screen to update buttons in plugin cards.
427
+
*
428
+
* @since 6.5.0
429
+
*
430
+
* @param {Object} data An object of data to use for the button.
431
+
* @param {string} data.slug The plugin's slug.
432
+
* @param {string} data.text The text to use for the button.
433
+
* @param {string} data.ariaLabel The value for the button's aria-label attribute. An empty string removes the attribute.
434
+
* @param {string=} data.status Optional. An identifier for the status.
435
+
* @param {string=} data.removeClasses Optional. A space-separated list of classes to remove from the button.
436
+
* @param {string=} data.addClasses Optional. A space-separated list of classes to add to the button.
437
+
* @param {string=} data.href Optional. The button's URL.
438
+
* @param {string=} data.pluginName Optional. The plugin's name.
439
+
* @param {string=} data.plugin Optional. The plugin file, relative to the plugins directory.
440
+
*/
441
+
wp.updates.setCardButtonStatus = function( data ) {
442
+
var target = window.parent === window ? null : window.parent;
443
+
444
+
$.support.postMessage = !! window.postMessage;
445
+
if ( false !== $.support.postMessage && null !== target && -1 === window.parent.location.pathname.indexOf( 'index.php' ) ) {
446
+
target.postMessage( JSON.stringify( data ), window.location.origin );
447
+
}
448
+
};
449
+
450
+
/**
451
+
* Decrements the update counts throughout the various menus.
452
+
*
453
+
* This includes the toolbar, the "Updates" menu item and the menu items
454
+
* for plugins and themes.
455
+
*
456
+
* @since 3.9.0
457
+
*
458
+
* @param {string} type The type of item that was updated or deleted.
459
+
* Can be 'plugin', 'theme'.
460
+
*/
461
+
wp.updates.decrementCount = function( type ) {
462
+
settings.totals.counts.total = Math.max( --settings.totals.counts.total, 0 );
463
+
464
+
if ( 'plugin' === type ) {
465
+
settings.totals.counts.plugins = Math.max( --settings.totals.counts.plugins, 0 );
466
+
} else if ( 'theme' === type ) {
467
+
settings.totals.counts.themes = Math.max( --settings.totals.counts.themes, 0 );
468
+
}
469
+
470
+
wp.updates.refreshCount( type );
471
+
};
472
+
473
+
/**
474
+
* Sends an Ajax request to the server to update a plugin.
475
+
*
476
+
* @since 4.2.0
477
+
* @since 4.6.0 More accurately named `updatePlugin`.
478
+
*
479
+
* @param {Object} args Arguments.
480
+
* @param {string} args.plugin Plugin basename.
481
+
* @param {string} args.slug Plugin slug.
482
+
* @param {updatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.updatePluginSuccess
483
+
* @param {updatePluginError=} args.error Optional. Error callback. Default: wp.updates.updatePluginError
484
+
* @return {$.promise} A jQuery promise that represents the request,
485
+
* decorated with an abort() method.
486
+
*/
487
+
wp.updates.updatePlugin = function( args ) {
488
+
var $updateRow, $card, $message, message,
489
+
$adminBarUpdates = $( '#wp-admin-bar-updates' ),
490
+
buttonText = __( 'Updating...' ),
491
+
isPluginInstall = 'plugin-install' === pagenow || 'plugin-install-network' === pagenow;
492
+
493
+
args = _.extend( {
494
+
success: wp.updates.updatePluginSuccess,
495
+
error: wp.updates.updatePluginError
496
+
}, args );
497
+
498
+
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
499
+
$updateRow = $( 'tr[data-plugin="' + args.plugin + '"]' );
500
+
$message = $updateRow.find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' );
501
+
message = sprintf(
502
+
/* translators: %s: Plugin name and version. */
503
+
_x( 'Updating %s...', 'plugin' ),
504
+
$updateRow.find( '.plugin-title strong' ).text()
505
+
);
506
+
} else if ( isPluginInstall ) {
507
+
$card = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' );
508
+
$message = $card.find( '.update-now' ).addClass( 'updating-message' );
509
+
message = sprintf(
510
+
/* translators: %s: Plugin name and version. */
511
+
_x( 'Updating %s...', 'plugin' ),
512
+
$message.data( 'name' )
513
+
);
514
+
515
+
// Remove previous error messages, if any.
516
+
$card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove();
517
+
}
518
+
519
+
$adminBarUpdates.addClass( 'spin' );
520
+
521
+
if ( $message.html() !== __( 'Updating...' ) ) {
522
+
$message.data( 'originaltext', $message.html() );
523
+
}
524
+
525
+
$message
526
+
.attr( 'aria-label', message )
527
+
.text( buttonText );
528
+
529
+
$document.trigger( 'wp-plugin-updating', args );
530
+
531
+
if ( isPluginInstall && 'plugin-information-footer' === $card.attr( 'id' ) ) {
532
+
wp.updates.setCardButtonStatus(
533
+
{
534
+
status: 'updating-plugin',
535
+
slug: args.slug,
536
+
addClasses: 'updating-message',
537
+
text: buttonText,
538
+
ariaLabel: message
539
+
}
540
+
);
541
+
}
542
+
543
+
return wp.updates.ajax( 'update-plugin', args );
544
+
};
545
+
546
+
/**
547
+
* Updates the UI appropriately after a successful plugin update.
548
+
*
549
+
* @since 4.2.0
550
+
* @since 4.6.0 More accurately named `updatePluginSuccess`.
551
+
* @since 5.5.0 Auto-update "time to next update" text cleared.
552
+
*
553
+
* @param {Object} response Response from the server.
554
+
* @param {string} response.slug Slug of the plugin to be updated.
555
+
* @param {string} response.plugin Basename of the plugin to be updated.
556
+
* @param {string} response.pluginName Name of the plugin to be updated.
557
+
* @param {string} response.oldVersion Old version of the plugin.
558
+
* @param {string} response.newVersion New version of the plugin.
559
+
*/
560
+
wp.updates.updatePluginSuccess = function( response ) {
561
+
var $pluginRow, $updateMessage, newText,
562
+
$adminBarUpdates = $( '#wp-admin-bar-updates' ),
563
+
buttonText = _x( 'Updated!', 'plugin' ),
564
+
ariaLabel = sprintf(
565
+
/* translators: %s: Plugin name and version. */
566
+
_x( '%s updated!', 'plugin' ),
567
+
response.pluginName
568
+
);
569
+
570
+
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
571
+
$pluginRow = $( 'tr[data-plugin="' + response.plugin + '"]' )
572
+
.removeClass( 'update is-enqueued' )
573
+
.addClass( 'updated' );
574
+
$updateMessage = $pluginRow.find( '.update-message' )
575
+
.removeClass( 'updating-message notice-warning' )
576
+
.addClass( 'updated-message notice-success' ).find( 'p' );
577
+
578
+
// Update the version number in the row.
579
+
newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
580
+
$pluginRow.find( '.plugin-version-author-uri' ).html( newText );
581
+
582
+
// Clear the "time to next auto-update" text.
583
+
$pluginRow.find( '.auto-update-time' ).empty();
584
+
} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
585
+
$updateMessage = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.update-now' )
586
+
.removeClass( 'updating-message' )
587
+
.addClass( 'button-disabled updated-message' );
588
+
}
589
+
590
+
$adminBarUpdates.removeClass( 'spin' );
591
+
592
+
$updateMessage
593
+
.attr( 'aria-label', ariaLabel )
594
+
.text( buttonText );
595
+
596
+
wp.a11y.speak( __( 'Update completed successfully.' ) );
597
+
598
+
if ( 'plugin_install_from_iframe' !== $updateMessage.attr( 'id' ) ) {
599
+
wp.updates.decrementCount( 'plugin' );
600
+
} else {
601
+
wp.updates.setCardButtonStatus(
602
+
{
603
+
status: 'updated-plugin',
604
+
slug: response.slug,
605
+
removeClasses: 'updating-message',
606
+
addClasses: 'button-disabled updated-message',
607
+
text: buttonText,
608
+
ariaLabel: ariaLabel
609
+
}
610
+
);
611
+
}
612
+
613
+
$document.trigger( 'wp-plugin-update-success', response );
614
+
};
615
+
616
+
/**
617
+
* Updates the UI appropriately after a failed plugin update.
618
+
*
619
+
* @since 4.2.0
620
+
* @since 4.6.0 More accurately named `updatePluginError`.
621
+
*
622
+
* @param {Object} response Response from the server.
623
+
* @param {string} response.slug Slug of the plugin to be updated.
624
+
* @param {string} response.plugin Basename of the plugin to be updated.
625
+
* @param {string=} response.pluginName Optional. Name of the plugin to be updated.
626
+
* @param {string} response.errorCode Error code for the error that occurred.
627
+
* @param {string} response.errorMessage The error that occurred.
628
+
*/
629
+
wp.updates.updatePluginError = function( response ) {
630
+
var $pluginRow, $card, $message, errorMessage, buttonText, ariaLabel,
631
+
$adminBarUpdates = $( '#wp-admin-bar-updates' );
632
+
633
+
if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
634
+
return;
635
+
}
636
+
637
+
if ( wp.updates.maybeHandleCredentialError( response, 'update-plugin' ) ) {
638
+
return;
639
+
}
640
+
641
+
errorMessage = sprintf(
642
+
/* translators: %s: Error string for a failed update. */
643
+
__( 'Update failed: %s' ),
644
+
response.errorMessage
645
+
);
646
+
647
+
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
648
+
$pluginRow = $( 'tr[data-plugin="' + response.plugin + '"]' ).removeClass( 'is-enqueued' );
649
+
650
+
if ( response.plugin ) {
651
+
$message = $( 'tr[data-plugin="' + response.plugin + '"]' ).find( '.update-message' );
652
+
} else {
653
+
$message = $( 'tr[data-slug="' + response.slug + '"]' ).find( '.update-message' );
654
+
}
655
+
$message.removeClass( 'updating-message notice-warning' ).addClass( 'notice-error' ).find( 'p' ).html( errorMessage );
656
+
657
+
if ( response.pluginName ) {
658
+
$message.find( 'p' )
659
+
.attr(
660
+
'aria-label',
661
+
sprintf(
662
+
/* translators: %s: Plugin name and version. */
663
+
_x( '%s update failed.', 'plugin' ),
664
+
response.pluginName
665
+
)
666
+
);
667
+
} else {
668
+
$message.find( 'p' ).removeAttr( 'aria-label' );
669
+
}
670
+
} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
671
+
buttonText = __( 'Update failed.' );
672
+
673
+
$card = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' )
674
+
.append( wp.updates.adminNotice( {
675
+
className: 'update-message notice-error notice-alt is-dismissible',
676
+
message: errorMessage
677
+
} ) );
678
+
679
+
if ( $card.hasClass( 'plugin-card-' + response.slug ) ) {
680
+
$card.addClass( 'plugin-card-update-failed' );
681
+
}
682
+
683
+
$card.find( '.update-now' )
684
+
.text( buttonText )
685
+
.removeClass( 'updating-message' );
686
+
687
+
if ( response.pluginName ) {
688
+
ariaLabel = sprintf(
689
+
/* translators: %s: Plugin name and version. */
690
+
_x( '%s update failed.', 'plugin' ),
691
+
response.pluginName
692
+
);
693
+
694
+
$card.find( '.update-now' ).attr( 'aria-label', ariaLabel );
695
+
} else {
696
+
ariaLabel = '';
697
+
$card.find( '.update-now' ).removeAttr( 'aria-label' );
698
+
}
699
+
700
+
$card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
701
+
702
+
// Use same delay as the total duration of the notice fadeTo + slideUp animation.
703
+
setTimeout( function() {
704
+
$card
705
+
.removeClass( 'plugin-card-update-failed' )
706
+
.find( '.column-name a' ).trigger( 'focus' );
707
+
708
+
$card.find( '.update-now' )
709
+
.attr( 'aria-label', false )
710
+
.text( __( 'Update Now' ) );
711
+
}, 200 );
712
+
} );
713
+
}
714
+
715
+
$adminBarUpdates.removeClass( 'spin' );
716
+
717
+
wp.a11y.speak( errorMessage, 'assertive' );
718
+
719
+
if ( 'plugin-information-footer' === $card.attr('id' ) ) {
720
+
wp.updates.setCardButtonStatus(
721
+
{
722
+
status: 'plugin-update-failed',
723
+
slug: response.slug,
724
+
removeClasses: 'updating-message',
725
+
text: buttonText,
726
+
ariaLabel: ariaLabel
727
+
}
728
+
);
729
+
}
730
+
731
+
$document.trigger( 'wp-plugin-update-error', response );
732
+
};
733
+
734
+
/**
735
+
* Sends an Ajax request to the server to install a plugin.
736
+
*
737
+
* @since 4.6.0
738
+
*
739
+
* @param {Object} args Arguments.
740
+
* @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository.
741
+
* @param {installPluginSuccess=} args.success Optional. Success callback. Default: wp.updates.installPluginSuccess
742
+
* @param {installPluginError=} args.error Optional. Error callback. Default: wp.updates.installPluginError
743
+
* @return {$.promise} A jQuery promise that represents the request,
744
+
* decorated with an abort() method.
745
+
*/
746
+
wp.updates.installPlugin = function( args ) {
747
+
var $card = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' ),
748
+
$message = $card.find( '.install-now' ),
749
+
buttonText = __( 'Installing...' ),
750
+
ariaLabel;
751
+
752
+
args = _.extend( {
753
+
success: wp.updates.installPluginSuccess,
754
+
error: wp.updates.installPluginError
755
+
}, args );
756
+
757
+
if ( 'import' === pagenow ) {
758
+
$message = $( '[data-slug="' + args.slug + '"]' );
759
+
}
760
+
761
+
if ( $message.html() !== __( 'Installing...' ) ) {
762
+
$message.data( 'originaltext', $message.html() );
763
+
}
764
+
765
+
ariaLabel = sprintf(
766
+
/* translators: %s: Plugin name and version. */
767
+
_x( 'Installing %s...', 'plugin' ),
768
+
$message.data( 'name' )
769
+
);
770
+
771
+
$message
772
+
.addClass( 'updating-message' )
773
+
.attr( 'aria-label', ariaLabel )
774
+
.text( buttonText );
775
+
776
+
wp.a11y.speak( __( 'Installing... please wait.' ) );
777
+
778
+
// Remove previous error messages, if any.
779
+
$card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove();
780
+
781
+
$document.trigger( 'wp-plugin-installing', args );
782
+
783
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
784
+
wp.updates.setCardButtonStatus(
785
+
{
786
+
status: 'installing-plugin',
787
+
slug: args.slug,
788
+
addClasses: 'updating-message',
789
+
text: buttonText,
790
+
ariaLabel: ariaLabel
791
+
}
792
+
);
793
+
}
794
+
795
+
return wp.updates.ajax( 'install-plugin', args );
796
+
};
797
+
798
+
/**
799
+
* Updates the UI appropriately after a successful plugin install.
800
+
*
801
+
* @since 4.6.0
802
+
*
803
+
* @param {Object} response Response from the server.
804
+
* @param {string} response.slug Slug of the installed plugin.
805
+
* @param {string} response.pluginName Name of the installed plugin.
806
+
* @param {string} response.activateUrl URL to activate the just installed plugin.
807
+
*/
808
+
wp.updates.installPluginSuccess = function( response ) {
809
+
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ),
810
+
buttonText = _x( 'Installed!', 'plugin' ),
811
+
ariaLabel = sprintf(
812
+
/* translators: %s: Plugin name and version. */
813
+
_x( '%s installed!', 'plugin' ),
814
+
response.pluginName
815
+
);
816
+
817
+
$message
818
+
.removeClass( 'updating-message' )
819
+
.addClass( 'updated-message installed button-disabled' )
820
+
.attr( 'aria-label', ariaLabel )
821
+
.text( buttonText );
822
+
823
+
wp.a11y.speak( __( 'Installation completed successfully.' ) );
824
+
825
+
$document.trigger( 'wp-plugin-install-success', response );
826
+
827
+
if ( response.activateUrl ) {
828
+
setTimeout( function() {
829
+
wp.updates.checkPluginDependencies( {
830
+
slug: response.slug
831
+
} );
832
+
}, 1000 );
833
+
}
834
+
835
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
836
+
wp.updates.setCardButtonStatus(
837
+
{
838
+
status: 'installed-plugin',
839
+
slug: response.slug,
840
+
removeClasses: 'updating-message',
841
+
addClasses: 'updated-message installed button-disabled',
842
+
text: buttonText,
843
+
ariaLabel: ariaLabel
844
+
}
845
+
);
846
+
}
847
+
};
848
+
849
+
/**
850
+
* Updates the UI appropriately after a failed plugin install.
851
+
*
852
+
* @since 4.6.0
853
+
*
854
+
* @param {Object} response Response from the server.
855
+
* @param {string} response.slug Slug of the plugin to be installed.
856
+
* @param {string=} response.pluginName Optional. Name of the plugin to be installed.
857
+
* @param {string} response.errorCode Error code for the error that occurred.
858
+
* @param {string} response.errorMessage The error that occurred.
859
+
*/
860
+
wp.updates.installPluginError = function( response ) {
861
+
var $card = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ),
862
+
$button = $card.find( '.install-now' ),
863
+
buttonText = __( 'Installation failed.' ),
864
+
ariaLabel = sprintf(
865
+
/* translators: %s: Plugin name and version. */
866
+
_x( '%s installation failed', 'plugin' ),
867
+
$button.data( 'name' )
868
+
),
869
+
errorMessage;
870
+
871
+
if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
872
+
return;
873
+
}
874
+
875
+
if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
876
+
return;
877
+
}
878
+
879
+
errorMessage = sprintf(
880
+
/* translators: %s: Error string for a failed installation. */
881
+
__( 'Installation failed: %s' ),
882
+
response.errorMessage
883
+
);
884
+
885
+
$card
886
+
.addClass( 'plugin-card-update-failed' )
887
+
.append( '<div class="notice notice-error notice-alt is-dismissible" role="alert"><p>' + errorMessage + '</p></div>' );
888
+
889
+
$card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
890
+
891
+
// Use same delay as the total duration of the notice fadeTo + slideUp animation.
892
+
setTimeout( function() {
893
+
$card
894
+
.removeClass( 'plugin-card-update-failed' )
895
+
.find( '.column-name a' ).trigger( 'focus' );
896
+
}, 200 );
897
+
} );
898
+
899
+
$button
900
+
.removeClass( 'updating-message' ).addClass( 'button-disabled' )
901
+
.attr( 'aria-label', ariaLabel )
902
+
.text( buttonText );
903
+
904
+
wp.a11y.speak( errorMessage, 'assertive' );
905
+
906
+
wp.updates.setCardButtonStatus(
907
+
{
908
+
status: 'plugin-install-failed',
909
+
slug: response.slug,
910
+
removeClasses: 'updating-message',
911
+
addClasses: 'button-disabled',
912
+
text: buttonText,
913
+
ariaLabel: ariaLabel
914
+
}
915
+
);
916
+
917
+
$document.trigger( 'wp-plugin-install-error', response );
918
+
};
919
+
920
+
/**
921
+
* Sends an Ajax request to the server to check a plugin's dependencies.
922
+
*
923
+
* @since 6.5.0
924
+
*
925
+
* @param {Object} args Arguments.
926
+
* @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository.
927
+
* @param {checkPluginDependenciesSuccess=} args.success Optional. Success callback. Default: wp.updates.checkPluginDependenciesSuccess
928
+
* @param {checkPluginDependenciesError=} args.error Optional. Error callback. Default: wp.updates.checkPluginDependenciesError
929
+
* @return {$.promise} A jQuery promise that represents the request,
930
+
* decorated with an abort() method.
931
+
*/
932
+
wp.updates.checkPluginDependencies = function( args ) {
933
+
args = _.extend( {
934
+
success: wp.updates.checkPluginDependenciesSuccess,
935
+
error: wp.updates.checkPluginDependenciesError
936
+
}, args );
937
+
938
+
wp.a11y.speak( __( 'Checking plugin dependencies... please wait.' ) );
939
+
$document.trigger( 'wp-checking-plugin-dependencies', args );
940
+
941
+
return wp.updates.ajax( 'check_plugin_dependencies', args );
942
+
};
943
+
944
+
/**
945
+
* Updates the UI appropriately after a successful plugin dependencies check.
946
+
*
947
+
* @since 6.5.0
948
+
*
949
+
* @param {Object} response Response from the server.
950
+
* @param {string} response.slug Slug of the checked plugin.
951
+
* @param {string} response.pluginName Name of the checked plugin.
952
+
* @param {string} response.plugin The plugin file, relative to the plugins directory.
953
+
* @param {string} response.activateUrl URL to activate the just checked plugin.
954
+
*/
955
+
wp.updates.checkPluginDependenciesSuccess = function( response ) {
956
+
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ),
957
+
buttonText, ariaLabel;
958
+
959
+
// Transform the 'Install' button into an 'Activate' button.
960
+
$message
961
+
.removeClass( 'install-now installed button-disabled updated-message' )
962
+
.addClass( 'activate-now button-primary' )
963
+
.attr( 'href', response.activateUrl );
964
+
965
+
wp.a11y.speak( __( 'Plugin dependencies check completed successfully.' ) );
966
+
$document.trigger( 'wp-check-plugin-dependencies-success', response );
967
+
968
+
if ( 'plugins-network' === pagenow || 'plugin-install-network' === pagenow ) {
969
+
buttonText = _x( 'Network Activate', 'plugin' );
970
+
ariaLabel = sprintf(
971
+
/* translators: %s: Plugin name. */
972
+
_x( 'Network Activate %s', 'plugin' ),
973
+
response.pluginName
974
+
);
975
+
976
+
$message
977
+
.attr( 'aria-label', ariaLabel )
978
+
.text( buttonText );
979
+
} else {
980
+
buttonText = _x( 'Activate', 'plugin' );
981
+
ariaLabel = sprintf(
982
+
/* translators: %s: Plugin name. */
983
+
_x( 'Activate %s', 'plugin' ),
984
+
response.pluginName
985
+
);
986
+
987
+
$message
988
+
.attr( 'aria-label', ariaLabel )
989
+
.attr( 'data-name', response.pluginName )
990
+
.attr( 'data-slug', response.slug )
991
+
.attr( 'data-plugin', response.plugin )
992
+
.text( buttonText );
993
+
}
994
+
995
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
996
+
wp.updates.setCardButtonStatus(
997
+
{
998
+
status: 'dependencies-check-success',
999
+
slug: response.slug,
1000
+
removeClasses: 'install-now installed button-disabled updated-message',
1001
+
addClasses: 'activate-now button-primary',
1002
+
text: buttonText,
1003
+
ariaLabel: ariaLabel,
1004
+
pluginName: response.pluginName,
1005
+
plugin: response.plugin,
1006
+
href: response.activateUrl
1007
+
}
1008
+
);
1009
+
}
1010
+
};
1011
+
1012
+
/**
1013
+
* Updates the UI appropriately after a failed plugin dependencies check.
1014
+
*
1015
+
* @since 6.5.0
1016
+
*
1017
+
* @param {Object} response Response from the server.
1018
+
* @param {string} response.slug Slug of the plugin to be checked.
1019
+
* @param {string=} response.pluginName Optional. Name of the plugin to be checked.
1020
+
* @param {string} response.errorCode Error code for the error that occurred.
1021
+
* @param {string} response.errorMessage The error that occurred.
1022
+
*/
1023
+
wp.updates.checkPluginDependenciesError = function( response ) {
1024
+
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.install-now' ),
1025
+
buttonText = _x( 'Activate', 'plugin' ),
1026
+
ariaLabel = sprintf(
1027
+
/* translators: 1: Plugin name, 2. The reason the plugin cannot be activated. */
1028
+
_x( 'Cannot activate %1$s. %2$s', 'plugin' ),
1029
+
response.pluginName,
1030
+
response.errorMessage
1031
+
),
1032
+
errorMessage;
1033
+
1034
+
if ( ! wp.updates.isValidResponse( response, 'check-dependencies' ) ) {
1035
+
return;
1036
+
}
1037
+
1038
+
errorMessage = sprintf(
1039
+
/* translators: %s: Error string for a failed activation. */
1040
+
__( 'Activation failed: %s' ),
1041
+
response.errorMessage
1042
+
);
1043
+
1044
+
wp.a11y.speak( errorMessage, 'assertive' );
1045
+
$document.trigger( 'wp-check-plugin-dependencies-error', response );
1046
+
1047
+
$message
1048
+
.removeClass( 'install-now installed updated-message' )
1049
+
.addClass( 'activate-now button-primary' )
1050
+
.attr( 'aria-label', ariaLabel )
1051
+
.text( buttonText );
1052
+
1053
+
if ( 'plugin-information-footer' === $message.parent().attr('id' ) ) {
1054
+
wp.updates.setCardButtonStatus(
1055
+
{
1056
+
status: 'dependencies-check-failed',
1057
+
slug: response.slug,
1058
+
removeClasses: 'install-now installed updated-message',
1059
+
addClasses: 'activate-now button-primary',
1060
+
text: buttonText,
1061
+
ariaLabel: ariaLabel
1062
+
}
1063
+
);
1064
+
}
1065
+
};
1066
+
1067
+
/**
1068
+
* Sends an Ajax request to the server to activate a plugin.
1069
+
*
1070
+
* @since 6.5.0
1071
+
*
1072
+
* @param {Object} args Arguments.
1073
+
* @param {string} args.name The name of the plugin.
1074
+
* @param {string} args.slug Plugin identifier in the WordPress.org Plugin repository.
1075
+
* @param {string} args.plugin The plugin file, relative to the plugins directory.
1076
+
* @param {activatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.activatePluginSuccess
1077
+
* @param {activatePluginError=} args.error Optional. Error callback. Default: wp.updates.activatePluginError
1078
+
* @return {$.promise} A jQuery promise that represents the request,
1079
+
* decorated with an abort() method.
1080
+
*/
1081
+
wp.updates.activatePlugin = function( args ) {
1082
+
var $message = $( '.plugin-card-' + args.slug + ', #plugin-information-footer' ).find( '.activate-now, .activating-message' );
1083
+
1084
+
args = _.extend( {
1085
+
success: wp.updates.activatePluginSuccess,
1086
+
error: wp.updates.activatePluginError
1087
+
}, args );
1088
+
1089
+
wp.a11y.speak( __( 'Activating... please wait.' ) );
1090
+
$document.trigger( 'wp-activating-plugin', args );
1091
+
1092
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
1093
+
wp.updates.setCardButtonStatus(
1094
+
{
1095
+
status: 'activating-plugin',
1096
+
slug: args.slug,
1097
+
removeClasses: 'installed updated-message button-primary',
1098
+
addClasses: 'activating-message',
1099
+
text: __( 'Activating...' ),
1100
+
ariaLabel: sprintf(
1101
+
/* translators: %s: Plugin name. */
1102
+
_x( 'Activating %s', 'plugin' ),
1103
+
args.name
1104
+
)
1105
+
}
1106
+
);
1107
+
}
1108
+
1109
+
return wp.updates.ajax( 'activate-plugin', args );
1110
+
};
1111
+
1112
+
/**
1113
+
* Updates the UI appropriately after a successful plugin activation.
1114
+
*
1115
+
* @since 6.5.0
1116
+
*
1117
+
* @param {Object} response Response from the server.
1118
+
* @param {string} response.slug Slug of the activated plugin.
1119
+
* @param {string} response.pluginName Name of the activated plugin.
1120
+
* @param {string} response.plugin The plugin file, relative to the plugins directory.
1121
+
*/
1122
+
wp.updates.activatePluginSuccess = function( response ) {
1123
+
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.activating-message' ),
1124
+
buttonText = _x( 'Activated!', 'plugin' ),
1125
+
ariaLabel = sprintf(
1126
+
/* translators: %s: The plugin name. */
1127
+
'%s activated successfully.',
1128
+
response.pluginName
1129
+
);
1130
+
1131
+
wp.a11y.speak( __( 'Activation completed successfully.' ) );
1132
+
$document.trigger( 'wp-plugin-activate-success', response );
1133
+
1134
+
$message
1135
+
.removeClass( 'activating-message' )
1136
+
.addClass( 'activated-message button-disabled' )
1137
+
.attr( 'aria-label', ariaLabel )
1138
+
.text( buttonText );
1139
+
1140
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
1141
+
wp.updates.setCardButtonStatus(
1142
+
{
1143
+
status: 'activated-plugin',
1144
+
slug: response.slug,
1145
+
removeClasses: 'activating-message',
1146
+
addClasses: 'activated-message button-disabled',
1147
+
text: buttonText,
1148
+
ariaLabel: ariaLabel
1149
+
}
1150
+
);
1151
+
}
1152
+
1153
+
setTimeout( function() {
1154
+
$message.removeClass( 'activated-message' )
1155
+
.text( _x( 'Active', 'plugin' ) );
1156
+
1157
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
1158
+
wp.updates.setCardButtonStatus(
1159
+
{
1160
+
status: 'plugin-active',
1161
+
slug: response.slug,
1162
+
removeClasses: 'activated-message',
1163
+
text: _x( 'Active', 'plugin' ),
1164
+
ariaLabel: sprintf(
1165
+
/* translators: %s: The plugin name. */
1166
+
'%s is active.',
1167
+
response.pluginName
1168
+
)
1169
+
}
1170
+
);
1171
+
}
1172
+
}, 1000 );
1173
+
};
1174
+
1175
+
/**
1176
+
* Updates the UI appropriately after a failed plugin activation.
1177
+
*
1178
+
* @since 6.5.0
1179
+
*
1180
+
* @param {Object} response Response from the server.
1181
+
* @param {string} response.slug Slug of the plugin to be activated.
1182
+
* @param {string=} response.pluginName Optional. Name of the plugin to be activated.
1183
+
* @param {string} response.errorCode Error code for the error that occurred.
1184
+
* @param {string} response.errorMessage The error that occurred.
1185
+
*/
1186
+
wp.updates.activatePluginError = function( response ) {
1187
+
var $message = $( '.plugin-card-' + response.slug + ', #plugin-information-footer' ).find( '.activating-message' ),
1188
+
buttonText = __( 'Activation failed.' ),
1189
+
ariaLabel = sprintf(
1190
+
/* translators: %s: Plugin name. */
1191
+
_x( '%s activation failed', 'plugin' ),
1192
+
response.pluginName
1193
+
),
1194
+
errorMessage;
1195
+
1196
+
if ( ! wp.updates.isValidResponse( response, 'activate' ) ) {
1197
+
return;
1198
+
}
1199
+
1200
+
errorMessage = sprintf(
1201
+
/* translators: %s: Error string for a failed activation. */
1202
+
__( 'Activation failed: %s' ),
1203
+
response.errorMessage
1204
+
);
1205
+
1206
+
wp.a11y.speak( errorMessage, 'assertive' );
1207
+
$document.trigger( 'wp-plugin-activate-error', response );
1208
+
1209
+
$message
1210
+
.removeClass( 'install-now installed activating-message' )
1211
+
.addClass( 'button-disabled' )
1212
+
.attr( 'aria-label', ariaLabel )
1213
+
.text( buttonText );
1214
+
1215
+
if ( 'plugin-information-footer' === $message.parent().attr( 'id' ) ) {
1216
+
wp.updates.setCardButtonStatus(
1217
+
{
1218
+
status: 'plugin-activation-failed',
1219
+
slug: response.slug,
1220
+
removeClasses: 'install-now installed activating-message',
1221
+
addClasses: 'button-disabled',
1222
+
text: buttonText,
1223
+
ariaLabel: ariaLabel
1224
+
}
1225
+
);
1226
+
}
1227
+
};
1228
+
1229
+
/**
1230
+
* Updates the UI appropriately after a successful importer install.
1231
+
*
1232
+
* @since 4.6.0
1233
+
*
1234
+
* @param {Object} response Response from the server.
1235
+
* @param {string} response.slug Slug of the installed plugin.
1236
+
* @param {string} response.pluginName Name of the installed plugin.
1237
+
* @param {string} response.activateUrl URL to activate the just installed plugin.
1238
+
*/
1239
+
wp.updates.installImporterSuccess = function( response ) {
1240
+
wp.updates.addAdminNotice( {
1241
+
id: 'install-success',
1242
+
className: 'notice-success is-dismissible',
1243
+
message: sprintf(
1244
+
/* translators: %s: Activation URL. */
1245
+
__( 'Importer installed successfully. <a href="%s">Run importer</a>' ),
1246
+
response.activateUrl + '&from=import'
1247
+
)
1248
+
} );
1249
+
1250
+
$( '[data-slug="' + response.slug + '"]' )
1251
+
.removeClass( 'install-now updating-message' )
1252
+
.addClass( 'activate-now' )
1253
+
.attr({
1254
+
'href': response.activateUrl + '&from=import',
1255
+
'aria-label':sprintf(
1256
+
/* translators: %s: Importer name. */
1257
+
__( 'Run %s' ),
1258
+
response.pluginName
1259
+
)
1260
+
})
1261
+
.text( __( 'Run Importer' ) );
1262
+
1263
+
wp.a11y.speak( __( 'Installation completed successfully.' ) );
1264
+
1265
+
$document.trigger( 'wp-importer-install-success', response );
1266
+
};
1267
+
1268
+
/**
1269
+
* Updates the UI appropriately after a failed importer install.
1270
+
*
1271
+
* @since 4.6.0
1272
+
*
1273
+
* @param {Object} response Response from the server.
1274
+
* @param {string} response.slug Slug of the plugin to be installed.
1275
+
* @param {string=} response.pluginName Optional. Name of the plugin to be installed.
1276
+
* @param {string} response.errorCode Error code for the error that occurred.
1277
+
* @param {string} response.errorMessage The error that occurred.
1278
+
*/
1279
+
wp.updates.installImporterError = function( response ) {
1280
+
var errorMessage = sprintf(
1281
+
/* translators: %s: Error string for a failed installation. */
1282
+
__( 'Installation failed: %s' ),
1283
+
response.errorMessage
1284
+
),
1285
+
$installLink = $( '[data-slug="' + response.slug + '"]' ),
1286
+
pluginName = $installLink.data( 'name' );
1287
+
1288
+
if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
1289
+
return;
1290
+
}
1291
+
1292
+
if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
1293
+
return;
1294
+
}
1295
+
1296
+
wp.updates.addAdminNotice( {
1297
+
id: response.errorCode,
1298
+
className: 'notice-error is-dismissible',
1299
+
message: errorMessage
1300
+
} );
1301
+
1302
+
$installLink
1303
+
.removeClass( 'updating-message' )
1304
+
.attr(
1305
+
'aria-label',
1306
+
sprintf(
1307
+
/* translators: %s: Plugin name. */
1308
+
_x( 'Install %s now', 'plugin' ),
1309
+
pluginName
1310
+
)
1311
+
)
1312
+
.text( _x( 'Install Now', 'plugin' ) );
1313
+
1314
+
wp.a11y.speak( errorMessage, 'assertive' );
1315
+
1316
+
$document.trigger( 'wp-importer-install-error', response );
1317
+
};
1318
+
1319
+
/**
1320
+
* Sends an Ajax request to the server to delete a plugin.
1321
+
*
1322
+
* @since 4.6.0
1323
+
*
1324
+
* @param {Object} args Arguments.
1325
+
* @param {string} args.plugin Basename of the plugin to be deleted.
1326
+
* @param {string} args.slug Slug of the plugin to be deleted.
1327
+
* @param {deletePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.deletePluginSuccess
1328
+
* @param {deletePluginError=} args.error Optional. Error callback. Default: wp.updates.deletePluginError
1329
+
* @return {$.promise} A jQuery promise that represents the request,
1330
+
* decorated with an abort() method.
1331
+
*/
1332
+
wp.updates.deletePlugin = function( args ) {
1333
+
var $link = $( '[data-plugin="' + args.plugin + '"]' ).find( '.row-actions a.delete' );
1334
+
1335
+
args = _.extend( {
1336
+
success: wp.updates.deletePluginSuccess,
1337
+
error: wp.updates.deletePluginError
1338
+
}, args );
1339
+
1340
+
if ( $link.html() !== __( 'Deleting...' ) ) {
1341
+
$link
1342
+
.data( 'originaltext', $link.html() )
1343
+
.text( __( 'Deleting...' ) );
1344
+
}
1345
+
1346
+
wp.a11y.speak( __( 'Deleting...' ) );
1347
+
1348
+
$document.trigger( 'wp-plugin-deleting', args );
1349
+
1350
+
return wp.updates.ajax( 'delete-plugin', args );
1351
+
};
1352
+
1353
+
/**
1354
+
* Updates the UI appropriately after a successful plugin deletion.
1355
+
*
1356
+
* @since 4.6.0
1357
+
*
1358
+
* @param {Object} response Response from the server.
1359
+
* @param {string} response.slug Slug of the plugin that was deleted.
1360
+
* @param {string} response.plugin Base name of the plugin that was deleted.
1361
+
* @param {string} response.pluginName Name of the plugin that was deleted.
1362
+
*/
1363
+
wp.updates.deletePluginSuccess = function( response ) {
1364
+
1365
+
// Removes the plugin and updates rows.
1366
+
$( '[data-plugin="' + response.plugin + '"]' ).css( { backgroundColor: '#faafaa' } ).fadeOut( 350, function() {
1367
+
var $form = $( '#bulk-action-form' ),
1368
+
$views = $( '.subsubsub' ),
1369
+
$pluginRow = $( this ),
1370
+
$currentView = $views.find( '[aria-current="page"]' ),
1371
+
$itemsCount = $( '.displaying-num' ),
1372
+
columnCount = $form.find( 'thead th:not(.hidden), thead td' ).length,
1373
+
pluginDeletedRow = wp.template( 'item-deleted-row' ),
1374
+
/**
1375
+
* Plugins Base names of plugins in their different states.
1376
+
*
1377
+
* @type {Object}
1378
+
*/
1379
+
plugins = settings.plugins,
1380
+
remainingCount;
1381
+
1382
+
// Add a success message after deleting a plugin.
1383
+
if ( ! $pluginRow.hasClass( 'plugin-update-tr' ) ) {
1384
+
$pluginRow.after(
1385
+
pluginDeletedRow( {
1386
+
slug: response.slug,
1387
+
plugin: response.plugin,
1388
+
colspan: columnCount,
1389
+
name: response.pluginName
1390
+
} )
1391
+
);
1392
+
}
1393
+
1394
+
$pluginRow.remove();
1395
+
1396
+
// Remove plugin from update count.
1397
+
if ( -1 !== _.indexOf( plugins.upgrade, response.plugin ) ) {
1398
+
plugins.upgrade = _.without( plugins.upgrade, response.plugin );
1399
+
wp.updates.decrementCount( 'plugin' );
1400
+
}
1401
+
1402
+
// Remove from views.
1403
+
if ( -1 !== _.indexOf( plugins.inactive, response.plugin ) ) {
1404
+
plugins.inactive = _.without( plugins.inactive, response.plugin );
1405
+
if ( plugins.inactive.length ) {
1406
+
$views.find( '.inactive .count' ).text( '(' + plugins.inactive.length + ')' );
1407
+
} else {
1408
+
$views.find( '.inactive' ).remove();
1409
+
}
1410
+
}
1411
+
1412
+
if ( -1 !== _.indexOf( plugins.active, response.plugin ) ) {
1413
+
plugins.active = _.without( plugins.active, response.plugin );
1414
+
if ( plugins.active.length ) {
1415
+
$views.find( '.active .count' ).text( '(' + plugins.active.length + ')' );
1416
+
} else {
1417
+
$views.find( '.active' ).remove();
1418
+
}
1419
+
}
1420
+
1421
+
if ( -1 !== _.indexOf( plugins.recently_activated, response.plugin ) ) {
1422
+
plugins.recently_activated = _.without( plugins.recently_activated, response.plugin );
1423
+
if ( plugins.recently_activated.length ) {
1424
+
$views.find( '.recently_activated .count' ).text( '(' + plugins.recently_activated.length + ')' );
1425
+
} else {
1426
+
$views.find( '.recently_activated' ).remove();
1427
+
}
1428
+
}
1429
+
1430
+
if ( -1 !== _.indexOf( plugins['auto-update-enabled'], response.plugin ) ) {
1431
+
plugins['auto-update-enabled'] = _.without( plugins['auto-update-enabled'], response.plugin );
1432
+
if ( plugins['auto-update-enabled'].length ) {
1433
+
$views.find( '.auto-update-enabled .count' ).text( '(' + plugins['auto-update-enabled'].length + ')' );
1434
+
} else {
1435
+
$views.find( '.auto-update-enabled' ).remove();
1436
+
}
1437
+
}
1438
+
1439
+
if ( -1 !== _.indexOf( plugins['auto-update-disabled'], response.plugin ) ) {
1440
+
plugins['auto-update-disabled'] = _.without( plugins['auto-update-disabled'], response.plugin );
1441
+
if ( plugins['auto-update-disabled'].length ) {
1442
+
$views.find( '.auto-update-disabled .count' ).text( '(' + plugins['auto-update-disabled'].length + ')' );
1443
+
} else {
1444
+
$views.find( '.auto-update-disabled' ).remove();
1445
+
}
1446
+
}
1447
+
1448
+
plugins.all = _.without( plugins.all, response.plugin );
1449
+
1450
+
if ( plugins.all.length ) {
1451
+
$views.find( '.all .count' ).text( '(' + plugins.all.length + ')' );
1452
+
} else {
1453
+
$form.find( '.tablenav' ).css( { visibility: 'hidden' } );
1454
+
$views.find( '.all' ).remove();
1455
+
1456
+
if ( ! $form.find( 'tr.no-items' ).length ) {
1457
+
$form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + __( 'No plugins are currently available.' ) + '</td></tr>' );
1458
+
}
1459
+
}
1460
+
1461
+
if ( $itemsCount.length && $currentView.length ) {
1462
+
remainingCount = plugins[ $currentView.parent( 'li' ).attr('class') ].length;
1463
+
$itemsCount.text(
1464
+
sprintf(
1465
+
/* translators: %s: The remaining number of plugins. */
1466
+
_nx( '%s item', '%s items', remainingCount, 'plugin/plugins' ),
1467
+
remainingCount
1468
+
)
1469
+
);
1470
+
}
1471
+
} );
1472
+
1473
+
wp.a11y.speak( _x( 'Deleted!', 'plugin' ) );
1474
+
1475
+
$document.trigger( 'wp-plugin-delete-success', response );
1476
+
};
1477
+
1478
+
/**
1479
+
* Updates the UI appropriately after a failed plugin deletion.
1480
+
*
1481
+
* @since 4.6.0
1482
+
*
1483
+
* @param {Object} response Response from the server.
1484
+
* @param {string} response.slug Slug of the plugin to be deleted.
1485
+
* @param {string} response.plugin Base name of the plugin to be deleted
1486
+
* @param {string=} response.pluginName Optional. Name of the plugin to be deleted.
1487
+
* @param {string} response.errorCode Error code for the error that occurred.
1488
+
* @param {string} response.errorMessage The error that occurred.
1489
+
*/
1490
+
wp.updates.deletePluginError = function( response ) {
1491
+
var $plugin, $pluginUpdateRow,
1492
+
pluginUpdateRow = wp.template( 'item-update-row' ),
1493
+
noticeContent = wp.updates.adminNotice( {
1494
+
className: 'update-message notice-error notice-alt',
1495
+
message: response.errorMessage
1496
+
} );
1497
+
1498
+
if ( response.plugin ) {
1499
+
$plugin = $( 'tr.inactive[data-plugin="' + response.plugin + '"]' );
1500
+
$pluginUpdateRow = $plugin.siblings( '[data-plugin="' + response.plugin + '"]' );
1501
+
} else {
1502
+
$plugin = $( 'tr.inactive[data-slug="' + response.slug + '"]' );
1503
+
$pluginUpdateRow = $plugin.siblings( '[data-slug="' + response.slug + '"]' );
1504
+
}
1505
+
1506
+
if ( ! wp.updates.isValidResponse( response, 'delete' ) ) {
1507
+
return;
1508
+
}
1509
+
1510
+
if ( wp.updates.maybeHandleCredentialError( response, 'delete-plugin' ) ) {
1511
+
return;
1512
+
}
1513
+
1514
+
// Add a plugin update row if it doesn't exist yet.
1515
+
if ( ! $pluginUpdateRow.length ) {
1516
+
$plugin.addClass( 'update' ).after(
1517
+
pluginUpdateRow( {
1518
+
slug: response.slug,
1519
+
plugin: response.plugin || response.slug,
1520
+
colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
1521
+
content: noticeContent
1522
+
} )
1523
+
);
1524
+
} else {
1525
+
1526
+
// Remove previous error messages, if any.
1527
+
$pluginUpdateRow.find( '.notice-error' ).remove();
1528
+
1529
+
$pluginUpdateRow.find( '.plugin-update' ).append( noticeContent );
1530
+
}
1531
+
1532
+
$document.trigger( 'wp-plugin-delete-error', response );
1533
+
};
1534
+
1535
+
/**
1536
+
* Sends an Ajax request to the server to update a theme.
1537
+
*
1538
+
* @since 4.6.0
1539
+
*
1540
+
* @param {Object} args Arguments.
1541
+
* @param {string} args.slug Theme stylesheet.
1542
+
* @param {updateThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.updateThemeSuccess
1543
+
* @param {updateThemeError=} args.error Optional. Error callback. Default: wp.updates.updateThemeError
1544
+
* @return {$.promise} A jQuery promise that represents the request,
1545
+
* decorated with an abort() method.
1546
+
*/
1547
+
wp.updates.updateTheme = function( args ) {
1548
+
var $notice;
1549
+
1550
+
args = _.extend( {
1551
+
success: wp.updates.updateThemeSuccess,
1552
+
error: wp.updates.updateThemeError
1553
+
}, args );
1554
+
1555
+
if ( 'themes-network' === pagenow ) {
1556
+
$notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' );
1557
+
1558
+
} else if ( 'customize' === pagenow ) {
1559
+
1560
+
// Update the theme details UI.
1561
+
$notice = $( '[data-slug="' + args.slug + '"].notice' ).removeClass( 'notice-large' );
1562
+
1563
+
$notice.find( 'h3' ).remove();
1564
+
1565
+
// Add the top-level UI, and update both.
1566
+
$notice = $notice.add( $( '#customize-control-installed_theme_' + args.slug ).find( '.update-message' ) );
1567
+
$notice = $notice.addClass( 'updating-message' ).find( 'p' );
1568
+
1569
+
} else {
1570
+
$notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' );
1571
+
1572
+
$notice.find( 'h3' ).remove();
1573
+
1574
+
$notice = $notice.add( $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ) );
1575
+
$notice = $notice.addClass( 'updating-message' ).find( 'p' );
1576
+
}
1577
+
1578
+
if ( $notice.html() !== __( 'Updating...' ) ) {
1579
+
$notice.data( 'originaltext', $notice.html() );
1580
+
}
1581
+
1582
+
wp.a11y.speak( __( 'Updating... please wait.' ) );
1583
+
$notice.text( __( 'Updating...' ) );
1584
+
1585
+
$document.trigger( 'wp-theme-updating', args );
1586
+
1587
+
return wp.updates.ajax( 'update-theme', args );
1588
+
};
1589
+
1590
+
/**
1591
+
* Updates the UI appropriately after a successful theme update.
1592
+
*
1593
+
* @since 4.6.0
1594
+
* @since 5.5.0 Auto-update "time to next update" text cleared.
1595
+
*
1596
+
* @param {Object} response
1597
+
* @param {string} response.slug Slug of the theme to be updated.
1598
+
* @param {Object} response.theme Updated theme.
1599
+
* @param {string} response.oldVersion Old version of the theme.
1600
+
* @param {string} response.newVersion New version of the theme.
1601
+
*/
1602
+
wp.updates.updateThemeSuccess = function( response ) {
1603
+
var isModalOpen = $( 'body.modal-open' ).length,
1604
+
$theme = $( '[data-slug="' + response.slug + '"]' ),
1605
+
updatedMessage = {
1606
+
className: 'updated-message notice-success notice-alt',
1607
+
message: _x( 'Updated!', 'theme' )
1608
+
},
1609
+
$notice, newText;
1610
+
1611
+
if ( 'customize' === pagenow ) {
1612
+
$theme = $( '.updating-message' ).siblings( '.theme-name' );
1613
+
1614
+
if ( $theme.length ) {
1615
+
1616
+
// Update the version number in the row.
1617
+
newText = $theme.html().replace( response.oldVersion, response.newVersion );
1618
+
$theme.html( newText );
1619
+
}
1620
+
1621
+
$notice = $( '.theme-info .notice' ).add( wp.customize.control( 'installed_theme_' + response.slug ).container.find( '.theme' ).find( '.update-message' ) );
1622
+
} else if ( 'themes-network' === pagenow ) {
1623
+
$notice = $theme.find( '.update-message' );
1624
+
1625
+
// Update the version number in the row.
1626
+
newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
1627
+
$theme.find( '.theme-version-author-uri' ).html( newText );
1628
+
1629
+
// Clear the "time to next auto-update" text.
1630
+
$theme.find( '.auto-update-time' ).empty();
1631
+
} else {
1632
+
$notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) );
1633
+
1634
+
// Focus on Customize button after updating.
1635
+
if ( isModalOpen ) {
1636
+
$( '.load-customize:visible' ).trigger( 'focus' );
1637
+
$( '.theme-info .theme-autoupdate' ).find( '.auto-update-time' ).empty();
1638
+
} else {
1639
+
$theme.find( '.load-customize' ).trigger( 'focus' );
1640
+
}
1641
+
}
1642
+
1643
+
wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) );
1644
+
wp.a11y.speak( __( 'Update completed successfully.' ) );
1645
+
1646
+
wp.updates.decrementCount( 'theme' );
1647
+
1648
+
$document.trigger( 'wp-theme-update-success', response );
1649
+
1650
+
// Show updated message after modal re-rendered.
1651
+
if ( isModalOpen && 'customize' !== pagenow ) {
1652
+
$( '.theme-info .theme-author' ).after( wp.updates.adminNotice( updatedMessage ) );
1653
+
}
1654
+
};
1655
+
1656
+
/**
1657
+
* Updates the UI appropriately after a failed theme update.
1658
+
*
1659
+
* @since 4.6.0
1660
+
*
1661
+
* @param {Object} response Response from the server.
1662
+
* @param {string} response.slug Slug of the theme to be updated.
1663
+
* @param {string} response.errorCode Error code for the error that occurred.
1664
+
* @param {string} response.errorMessage The error that occurred.
1665
+
*/
1666
+
wp.updates.updateThemeError = function( response ) {
1667
+
var $theme = $( '[data-slug="' + response.slug + '"]' ),
1668
+
errorMessage = sprintf(
1669
+
/* translators: %s: Error string for a failed update. */
1670
+
__( 'Update failed: %s' ),
1671
+
response.errorMessage
1672
+
),
1673
+
$notice;
1674
+
1675
+
if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
1676
+
return;
1677
+
}
1678
+
1679
+
if ( wp.updates.maybeHandleCredentialError( response, 'update-theme' ) ) {
1680
+
return;
1681
+
}
1682
+
1683
+
if ( 'customize' === pagenow ) {
1684
+
$theme = wp.customize.control( 'installed_theme_' + response.slug ).container.find( '.theme' );
1685
+
}
1686
+
1687
+
if ( 'themes-network' === pagenow ) {
1688
+
$notice = $theme.find( '.update-message ' );
1689
+
} else {
1690
+
$notice = $( '.theme-info .notice' ).add( $theme.find( '.notice' ) );
1691
+
1692
+
$( 'body.modal-open' ).length ? $( '.load-customize:visible' ).trigger( 'focus' ) : $theme.find( '.load-customize' ).trigger( 'focus');
1693
+
}
1694
+
1695
+
wp.updates.addAdminNotice( {
1696
+
selector: $notice,
1697
+
className: 'update-message notice-error notice-alt is-dismissible',
1698
+
message: errorMessage
1699
+
} );
1700
+
1701
+
wp.a11y.speak( errorMessage );
1702
+
1703
+
$document.trigger( 'wp-theme-update-error', response );
1704
+
};
1705
+
1706
+
/**
1707
+
* Sends an Ajax request to the server to install a theme.
1708
+
*
1709
+
* @since 4.6.0
1710
+
*
1711
+
* @param {Object} args
1712
+
* @param {string} args.slug Theme stylesheet.
1713
+
* @param {installThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.installThemeSuccess
1714
+
* @param {installThemeError=} args.error Optional. Error callback. Default: wp.updates.installThemeError
1715
+
* @return {$.promise} A jQuery promise that represents the request,
1716
+
* decorated with an abort() method.
1717
+
*/
1718
+
wp.updates.installTheme = function( args ) {
1719
+
var $message = $( '.theme-install[data-slug="' + args.slug + '"]' );
1720
+
1721
+
args = _.extend( {
1722
+
success: wp.updates.installThemeSuccess,
1723
+
error: wp.updates.installThemeError
1724
+
}, args );
1725
+
1726
+
$message.addClass( 'updating-message' );
1727
+
$message.parents( '.theme' ).addClass( 'focus' );
1728
+
if ( $message.html() !== __( 'Installing...' ) ) {
1729
+
$message.data( 'originaltext', $message.html() );
1730
+
}
1731
+
1732
+
$message
1733
+
.attr(
1734
+
'aria-label',
1735
+
sprintf(
1736
+
/* translators: %s: Theme name and version. */
1737
+
_x( 'Installing %s...', 'theme' ),
1738
+
$message.data( 'name' )
1739
+
)
1740
+
)
1741
+
.text( __( 'Installing...' ) );
1742
+
1743
+
wp.a11y.speak( __( 'Installing... please wait.' ) );
1744
+
1745
+
// Remove previous error messages, if any.
1746
+
$( '.install-theme-info, [data-slug="' + args.slug + '"]' ).removeClass( 'theme-install-failed' ).find( '.notice.notice-error' ).remove();
1747
+
1748
+
$document.trigger( 'wp-theme-installing', args );
1749
+
1750
+
return wp.updates.ajax( 'install-theme', args );
1751
+
};
1752
+
1753
+
/**
1754
+
* Updates the UI appropriately after a successful theme install.
1755
+
*
1756
+
* @since 4.6.0
1757
+
*
1758
+
* @param {Object} response Response from the server.
1759
+
* @param {string} response.slug Slug of the theme to be installed.
1760
+
* @param {string} response.customizeUrl URL to the Customizer for the just installed theme.
1761
+
* @param {string} response.activateUrl URL to activate the just installed theme.
1762
+
*/
1763
+
wp.updates.installThemeSuccess = function( response ) {
1764
+
var $card = $( '.wp-full-overlay-header, [data-slug=' + response.slug + ']' ),
1765
+
$message;
1766
+
1767
+
$document.trigger( 'wp-theme-install-success', response );
1768
+
1769
+
$message = $card.find( '.button-primary' )
1770
+
.removeClass( 'updating-message' )
1771
+
.addClass( 'updated-message disabled' )
1772
+
.attr(
1773
+
'aria-label',
1774
+
sprintf(
1775
+
/* translators: %s: Theme name and version. */
1776
+
_x( '%s installed!', 'theme' ),
1777
+
response.themeName
1778
+
)
1779
+
)
1780
+
.text( _x( 'Installed!', 'theme' ) );
1781
+
1782
+
wp.a11y.speak( __( 'Installation completed successfully.' ) );
1783
+
1784
+
setTimeout( function() {
1785
+
1786
+
if ( response.activateUrl ) {
1787
+
1788
+
// Transform the 'Install' button into an 'Activate' button.
1789
+
$message
1790
+
.attr( 'href', response.activateUrl )
1791
+
.removeClass( 'theme-install updated-message disabled' )
1792
+
.addClass( 'activate' );
1793
+
1794
+
if ( 'themes-network' === pagenow ) {
1795
+
$message
1796
+
.attr(
1797
+
'aria-label',
1798
+
sprintf(
1799
+
/* translators: %s: Theme name. */
1800
+
_x( 'Network Activate %s', 'theme' ),
1801
+
response.themeName
1802
+
)
1803
+
)
1804
+
.text( __( 'Network Enable' ) );
1805
+
} else {
1806
+
$message
1807
+
.attr(
1808
+
'aria-label',
1809
+
sprintf(
1810
+
/* translators: %s: Theme name. */
1811
+
_x( 'Activate %s', 'theme' ),
1812
+
response.themeName
1813
+
)
1814
+
)
1815
+
.text( _x( 'Activate', 'theme' ) );
1816
+
}
1817
+
}
1818
+
1819
+
if ( response.customizeUrl ) {
1820
+
1821
+
// Transform the 'Preview' button into a 'Live Preview' button.
1822
+
$message.siblings( '.preview' ).replaceWith( function () {
1823
+
return $( '<a>' )
1824
+
.attr( 'href', response.customizeUrl )
1825
+
.addClass( 'button load-customize' )
1826
+
.text( __( 'Live Preview' ) );
1827
+
} );
1828
+
}
1829
+
}, 1000 );
1830
+
};
1831
+
1832
+
/**
1833
+
* Updates the UI appropriately after a failed theme install.
1834
+
*
1835
+
* @since 4.6.0
1836
+
*
1837
+
* @param {Object} response Response from the server.
1838
+
* @param {string} response.slug Slug of the theme to be installed.
1839
+
* @param {string} response.errorCode Error code for the error that occurred.
1840
+
* @param {string} response.errorMessage The error that occurred.
1841
+
*/
1842
+
wp.updates.installThemeError = function( response ) {
1843
+
var $card, $button,
1844
+
errorMessage = sprintf(
1845
+
/* translators: %s: Error string for a failed installation. */
1846
+
__( 'Installation failed: %s' ),
1847
+
response.errorMessage
1848
+
),
1849
+
$message = wp.updates.adminNotice( {
1850
+
className: 'update-message notice-error notice-alt',
1851
+
message: errorMessage
1852
+
} );
1853
+
1854
+
if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
1855
+
return;
1856
+
}
1857
+
1858
+
if ( wp.updates.maybeHandleCredentialError( response, 'install-theme' ) ) {
1859
+
return;
1860
+
}
1861
+
1862
+
if ( 'customize' === pagenow ) {
1863
+
if ( $document.find( 'body' ).hasClass( 'modal-open' ) ) {
1864
+
$button = $( '.theme-install[data-slug="' + response.slug + '"]' );
1865
+
$card = $( '.theme-overlay .theme-info' ).prepend( $message );
1866
+
} else {
1867
+
$button = $( '.theme-install[data-slug="' + response.slug + '"]' );
1868
+
$card = $button.closest( '.theme' ).addClass( 'theme-install-failed' ).append( $message );
1869
+
}
1870
+
wp.customize.notifications.remove( 'theme_installing' );
1871
+
} else {
1872
+
if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) {
1873
+
$button = $( '.theme-install[data-slug="' + response.slug + '"]' );
1874
+
$card = $( '.install-theme-info' ).prepend( $message );
1875
+
} else {
1876
+
$card = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message );
1877
+
$button = $card.find( '.theme-install' );
1878
+
}
1879
+
}
1880
+
1881
+
$button
1882
+
.removeClass( 'updating-message' )
1883
+
.attr(
1884
+
'aria-label',
1885
+
sprintf(
1886
+
/* translators: %s: Theme name and version. */
1887
+
_x( '%s installation failed', 'theme' ),
1888
+
$button.data( 'name' )
1889
+
)
1890
+
)
1891
+
.text( __( 'Installation failed.' ) );
1892
+
1893
+
wp.a11y.speak( errorMessage, 'assertive' );
1894
+
1895
+
$document.trigger( 'wp-theme-install-error', response );
1896
+
};
1897
+
1898
+
/**
1899
+
* Sends an Ajax request to the server to delete a theme.
1900
+
*
1901
+
* @since 4.6.0
1902
+
*
1903
+
* @param {Object} args
1904
+
* @param {string} args.slug Theme stylesheet.
1905
+
* @param {deleteThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.deleteThemeSuccess
1906
+
* @param {deleteThemeError=} args.error Optional. Error callback. Default: wp.updates.deleteThemeError
1907
+
* @return {$.promise} A jQuery promise that represents the request,
1908
+
* decorated with an abort() method.
1909
+
*/
1910
+
wp.updates.deleteTheme = function( args ) {
1911
+
var $button;
1912
+
1913
+
if ( 'themes' === pagenow ) {
1914
+
$button = $( '.theme-actions .delete-theme' );
1915
+
} else if ( 'themes-network' === pagenow ) {
1916
+
$button = $( '[data-slug="' + args.slug + '"]' ).find( '.row-actions a.delete' );
1917
+
}
1918
+
1919
+
args = _.extend( {
1920
+
success: wp.updates.deleteThemeSuccess,
1921
+
error: wp.updates.deleteThemeError
1922
+
}, args );
1923
+
1924
+
if ( $button && $button.html() !== __( 'Deleting...' ) ) {
1925
+
$button
1926
+
.data( 'originaltext', $button.html() )
1927
+
.text( __( 'Deleting...' ) );
1928
+
}
1929
+
1930
+
wp.a11y.speak( __( 'Deleting...' ) );
1931
+
1932
+
// Remove previous error messages, if any.
1933
+
$( '.theme-info .update-message' ).remove();
1934
+
1935
+
$document.trigger( 'wp-theme-deleting', args );
1936
+
1937
+
return wp.updates.ajax( 'delete-theme', args );
1938
+
};
1939
+
1940
+
/**
1941
+
* Updates the UI appropriately after a successful theme deletion.
1942
+
*
1943
+
* @since 4.6.0
1944
+
*
1945
+
* @param {Object} response Response from the server.
1946
+
* @param {string} response.slug Slug of the theme that was deleted.
1947
+
*/
1948
+
wp.updates.deleteThemeSuccess = function( response ) {
1949
+
var $themeRows = $( '[data-slug="' + response.slug + '"]' );
1950
+
1951
+
if ( 'themes-network' === pagenow ) {
1952
+
1953
+
// Removes the theme and updates rows.
1954
+
$themeRows.css( { backgroundColor: '#faafaa' } ).fadeOut( 350, function() {
1955
+
var $views = $( '.subsubsub' ),
1956
+
$themeRow = $( this ),
1957
+
themes = settings.themes,
1958
+
deletedRow = wp.template( 'item-deleted-row' );
1959
+
1960
+
if ( ! $themeRow.hasClass( 'plugin-update-tr' ) ) {
1961
+
$themeRow.after(
1962
+
deletedRow( {
1963
+
slug: response.slug,
1964
+
colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
1965
+
name: $themeRow.find( '.theme-title strong' ).text()
1966
+
} )
1967
+
);
1968
+
}
1969
+
1970
+
$themeRow.remove();
1971
+
1972
+
// Remove theme from update count.
1973
+
if ( -1 !== _.indexOf( themes.upgrade, response.slug ) ) {
1974
+
themes.upgrade = _.without( themes.upgrade, response.slug );
1975
+
wp.updates.decrementCount( 'theme' );
1976
+
}
1977
+
1978
+
// Remove from views.
1979
+
if ( -1 !== _.indexOf( themes.disabled, response.slug ) ) {
1980
+
themes.disabled = _.without( themes.disabled, response.slug );
1981
+
if ( themes.disabled.length ) {
1982
+
$views.find( '.disabled .count' ).text( '(' + themes.disabled.length + ')' );
1983
+
} else {
1984
+
$views.find( '.disabled' ).remove();
1985
+
}
1986
+
}
1987
+
1988
+
if ( -1 !== _.indexOf( themes['auto-update-enabled'], response.slug ) ) {
1989
+
themes['auto-update-enabled'] = _.without( themes['auto-update-enabled'], response.slug );
1990
+
if ( themes['auto-update-enabled'].length ) {
1991
+
$views.find( '.auto-update-enabled .count' ).text( '(' + themes['auto-update-enabled'].length + ')' );
1992
+
} else {
1993
+
$views.find( '.auto-update-enabled' ).remove();
1994
+
}
1995
+
}
1996
+
1997
+
if ( -1 !== _.indexOf( themes['auto-update-disabled'], response.slug ) ) {
1998
+
themes['auto-update-disabled'] = _.without( themes['auto-update-disabled'], response.slug );
1999
+
if ( themes['auto-update-disabled'].length ) {
2000
+
$views.find( '.auto-update-disabled .count' ).text( '(' + themes['auto-update-disabled'].length + ')' );
2001
+
} else {
2002
+
$views.find( '.auto-update-disabled' ).remove();
2003
+
}
2004
+
}
2005
+
2006
+
themes.all = _.without( themes.all, response.slug );
2007
+
2008
+
// There is always at least one theme available.
2009
+
$views.find( '.all .count' ).text( '(' + themes.all.length + ')' );
2010
+
} );
2011
+
}
2012
+
2013
+
// DecrementCount from update count.
2014
+
if ( 'themes' === pagenow ) {
2015
+
var theme = _.find( _wpThemeSettings.themes, { id: response.slug } );
2016
+
if ( theme.hasUpdate ) {
2017
+
wp.updates.decrementCount( 'theme' );
2018
+
}
2019
+
}
2020
+
2021
+
wp.a11y.speak( _x( 'Deleted!', 'theme' ) );
2022
+
2023
+
$document.trigger( 'wp-theme-delete-success', response );
2024
+
};
2025
+
2026
+
/**
2027
+
* Updates the UI appropriately after a failed theme deletion.
2028
+
*
2029
+
* @since 4.6.0
2030
+
*
2031
+
* @param {Object} response Response from the server.
2032
+
* @param {string} response.slug Slug of the theme to be deleted.
2033
+
* @param {string} response.errorCode Error code for the error that occurred.
2034
+
* @param {string} response.errorMessage The error that occurred.
2035
+
*/
2036
+
wp.updates.deleteThemeError = function( response ) {
2037
+
var $themeRow = $( 'tr.inactive[data-slug="' + response.slug + '"]' ),
2038
+
$button = $( '.theme-actions .delete-theme' ),
2039
+
updateRow = wp.template( 'item-update-row' ),
2040
+
$updateRow = $themeRow.siblings( '#' + response.slug + '-update' ),
2041
+
errorMessage = sprintf(
2042
+
/* translators: %s: Error string for a failed deletion. */
2043
+
__( 'Deletion failed: %s' ),
2044
+
response.errorMessage
2045
+
),
2046
+
$message = wp.updates.adminNotice( {
2047
+
className: 'update-message notice-error notice-alt',
2048
+
message: errorMessage
2049
+
} );
2050
+
2051
+
if ( wp.updates.maybeHandleCredentialError( response, 'delete-theme' ) ) {
2052
+
return;
2053
+
}
2054
+
2055
+
if ( 'themes-network' === pagenow ) {
2056
+
if ( ! $updateRow.length ) {
2057
+
$themeRow.addClass( 'update' ).after(
2058
+
updateRow( {
2059
+
slug: response.slug,
2060
+
colspan: $( '#bulk-action-form' ).find( 'thead th:not(.hidden), thead td' ).length,
2061
+
content: $message
2062
+
} )
2063
+
);
2064
+
} else {
2065
+
// Remove previous error messages, if any.
2066
+
$updateRow.find( '.notice-error' ).remove();
2067
+
$updateRow.find( '.plugin-update' ).append( $message );
2068
+
}
2069
+
} else {
2070
+
$( '.theme-info .theme-description' ).before( $message );
2071
+
}
2072
+
2073
+
$button.html( $button.data( 'originaltext' ) );
2074
+
2075
+
wp.a11y.speak( errorMessage, 'assertive' );
2076
+
2077
+
$document.trigger( 'wp-theme-delete-error', response );
2078
+
};
2079
+
2080
+
/**
2081
+
* Adds the appropriate callback based on the type of action and the current page.
2082
+
*
2083
+
* @since 4.6.0
2084
+
* @private
2085
+
*
2086
+
* @param {Object} data Ajax payload.
2087
+
* @param {string} action The type of request to perform.
2088
+
* @return {Object} The Ajax payload with the appropriate callbacks.
2089
+
*/
2090
+
wp.updates._addCallbacks = function( data, action ) {
2091
+
if ( 'import' === pagenow && 'install-plugin' === action ) {
2092
+
data.success = wp.updates.installImporterSuccess;
2093
+
data.error = wp.updates.installImporterError;
2094
+
}
2095
+
2096
+
return data;
2097
+
};
2098
+
2099
+
/**
2100
+
* Pulls available jobs from the queue and runs them.
2101
+
*
2102
+
* @since 4.2.0
2103
+
* @since 4.6.0 Can handle multiple job types.
2104
+
*/
2105
+
wp.updates.queueChecker = function() {
2106
+
var job;
2107
+
2108
+
if ( wp.updates.ajaxLocked || ! wp.updates.queue.length ) {
2109
+
return;
2110
+
}
2111
+
2112
+
job = wp.updates.queue.shift();
2113
+
2114
+
// Handle a queue job.
2115
+
switch ( job.action ) {
2116
+
case 'install-plugin':
2117
+
wp.updates.installPlugin( job.data );
2118
+
break;
2119
+
2120
+
case 'update-plugin':
2121
+
wp.updates.updatePlugin( job.data );
2122
+
break;
2123
+
2124
+
case 'delete-plugin':
2125
+
wp.updates.deletePlugin( job.data );
2126
+
break;
2127
+
2128
+
case 'install-theme':
2129
+
wp.updates.installTheme( job.data );
2130
+
break;
2131
+
2132
+
case 'update-theme':
2133
+
wp.updates.updateTheme( job.data );
2134
+
break;
2135
+
2136
+
case 'delete-theme':
2137
+
wp.updates.deleteTheme( job.data );
2138
+
break;
2139
+
2140
+
default:
2141
+
break;
2142
+
}
2143
+
};
2144
+
2145
+
/**
2146
+
* Requests the users filesystem credentials if they aren't already known.
2147
+
*
2148
+
* @since 4.2.0
2149
+
*
2150
+
* @param {Event=} event Optional. Event interface.
2151
+
*/
2152
+
wp.updates.requestFilesystemCredentials = function( event ) {
2153
+
if ( false === wp.updates.filesystemCredentials.available ) {
2154
+
/*
2155
+
* After exiting the credentials request modal,
2156
+
* return the focus to the element triggering the request.
2157
+
*/
2158
+
if ( event && ! wp.updates.$elToReturnFocusToFromCredentialsModal ) {
2159
+
wp.updates.$elToReturnFocusToFromCredentialsModal = $( event.target );
2160
+
}
2161
+
2162
+
wp.updates.ajaxLocked = true;
2163
+
wp.updates.requestForCredentialsModalOpen();
2164
+
}
2165
+
};
2166
+
2167
+
/**
2168
+
* Requests the users filesystem credentials if needed and there is no lock.
2169
+
*
2170
+
* @since 4.6.0
2171
+
*
2172
+
* @param {Event=} event Optional. Event interface.
2173
+
*/
2174
+
wp.updates.maybeRequestFilesystemCredentials = function( event ) {
2175
+
if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
2176
+
wp.updates.requestFilesystemCredentials( event );
2177
+
}
2178
+
};
2179
+
2180
+
/**
2181
+
* Keydown handler for the request for credentials modal.
2182
+
*
2183
+
* Closes the modal when the escape key is pressed and
2184
+
* constrains keyboard navigation to inside the modal.
2185
+
*
2186
+
* @since 4.2.0
2187
+
*
2188
+
* @param {Event} event Event interface.
2189
+
*/
2190
+
wp.updates.keydown = function( event ) {
2191
+
if ( 27 === event.keyCode ) {
2192
+
wp.updates.requestForCredentialsModalCancel();
2193
+
} else if ( 9 === event.keyCode ) {
2194
+
2195
+
// #upgrade button must always be the last focus-able element in the dialog.
2196
+
if ( 'upgrade' === event.target.id && ! event.shiftKey ) {
2197
+
$( '#hostname' ).trigger( 'focus' );
2198
+
2199
+
event.preventDefault();
2200
+
} else if ( 'hostname' === event.target.id && event.shiftKey ) {
2201
+
$( '#upgrade' ).trigger( 'focus' );
2202
+
2203
+
event.preventDefault();
2204
+
}
2205
+
}
2206
+
};
2207
+
2208
+
/**
2209
+
* Opens the request for credentials modal.
2210
+
*
2211
+
* @since 4.2.0
2212
+
*/
2213
+
wp.updates.requestForCredentialsModalOpen = function() {
2214
+
var $modal = $( '#request-filesystem-credentials-dialog' );
2215
+
2216
+
$( 'body' ).addClass( 'modal-open' );
2217
+
$modal.show();
2218
+
$modal.find( 'input:enabled:first' ).trigger( 'focus' );
2219
+
$modal.on( 'keydown', wp.updates.keydown );
2220
+
};
2221
+
2222
+
/**
2223
+
* Closes the request for credentials modal.
2224
+
*
2225
+
* @since 4.2.0
2226
+
*/
2227
+
wp.updates.requestForCredentialsModalClose = function() {
2228
+
$( '#request-filesystem-credentials-dialog' ).hide();
2229
+
$( 'body' ).removeClass( 'modal-open' );
2230
+
2231
+
if ( wp.updates.$elToReturnFocusToFromCredentialsModal ) {
2232
+
wp.updates.$elToReturnFocusToFromCredentialsModal.trigger( 'focus' );
2233
+
}
2234
+
};
2235
+
2236
+
/**
2237
+
* Takes care of the steps that need to happen when the modal is canceled out.
2238
+
*
2239
+
* @since 4.2.0
2240
+
* @since 4.6.0 Triggers an event for callbacks to listen to and add their actions.
2241
+
*/
2242
+
wp.updates.requestForCredentialsModalCancel = function() {
2243
+
2244
+
// Not ajaxLocked and no queue means we already have cleared things up.
2245
+
if ( ! wp.updates.ajaxLocked && ! wp.updates.queue.length ) {
2246
+
return;
2247
+
}
2248
+
2249
+
_.each( wp.updates.queue, function( job ) {
2250
+
$document.trigger( 'credential-modal-cancel', job );
2251
+
} );
2252
+
2253
+
// Remove the lock, and clear the queue.
2254
+
wp.updates.ajaxLocked = false;
2255
+
wp.updates.queue = [];
2256
+
2257
+
wp.updates.requestForCredentialsModalClose();
2258
+
};
2259
+
2260
+
/**
2261
+
* Displays an error message in the request for credentials form.
2262
+
*
2263
+
* @since 4.2.0
2264
+
*
2265
+
* @param {string} message Error message.
2266
+
*/
2267
+
wp.updates.showErrorInCredentialsForm = function( message ) {
2268
+
var $filesystemForm = $( '#request-filesystem-credentials-form' );
2269
+
2270
+
// Remove any existing error.
2271
+
$filesystemForm.find( '.notice' ).remove();
2272
+
$filesystemForm.find( '#request-filesystem-credentials-title' ).after( '<div class="notice notice-alt notice-error" role="alert"><p>' + message + '</p></div>' );
2273
+
};
2274
+
2275
+
/**
2276
+
* Handles credential errors and runs events that need to happen in that case.
2277
+
*
2278
+
* @since 4.2.0
2279
+
*
2280
+
* @param {Object} response Ajax response.
2281
+
* @param {string} action The type of request to perform.
2282
+
*/
2283
+
wp.updates.credentialError = function( response, action ) {
2284
+
2285
+
// Restore callbacks.
2286
+
response = wp.updates._addCallbacks( response, action );
2287
+
2288
+
wp.updates.queue.unshift( {
2289
+
action: action,
2290
+
2291
+
/*
2292
+
* Not cool that we're depending on response for this data.
2293
+
* This would feel more whole in a view all tied together.
2294
+
*/
2295
+
data: response
2296
+
} );
2297
+
2298
+
wp.updates.filesystemCredentials.available = false;
2299
+
wp.updates.showErrorInCredentialsForm( response.errorMessage );
2300
+
wp.updates.requestFilesystemCredentials();
2301
+
};
2302
+
2303
+
/**
2304
+
* Handles credentials errors if it could not connect to the filesystem.
2305
+
*
2306
+
* @since 4.6.0
2307
+
*
2308
+
* @param {Object} response Response from the server.
2309
+
* @param {string} response.errorCode Error code for the error that occurred.
2310
+
* @param {string} response.errorMessage The error that occurred.
2311
+
* @param {string} action The type of request to perform.
2312
+
* @return {boolean} Whether there is an error that needs to be handled or not.
2313
+
*/
2314
+
wp.updates.maybeHandleCredentialError = function( response, action ) {
2315
+
if ( wp.updates.shouldRequestFilesystemCredentials && response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) {
2316
+
wp.updates.credentialError( response, action );
2317
+
return true;
2318
+
}
2319
+
2320
+
return false;
2321
+
};
2322
+
2323
+
/**
2324
+
* Validates an Ajax response to ensure it's a proper object.
2325
+
*
2326
+
* If the response deems to be invalid, an admin notice is being displayed.
2327
+
*
2328
+
* @param {(Object|string)} response Response from the server.
2329
+
* @param {function=} response.always Optional. Callback for when the Deferred is resolved or rejected.
2330
+
* @param {string=} response.statusText Optional. Status message corresponding to the status code.
2331
+
* @param {string=} response.responseText Optional. Request response as text.
2332
+
* @param {string} action Type of action the response is referring to. Can be 'delete',
2333
+
* 'update' or 'install'.
2334
+
*/
2335
+
wp.updates.isValidResponse = function( response, action ) {
2336
+
var error = __( 'An error occurred during the update process. Please try again.' ),
2337
+
errorMessage;
2338
+
2339
+
// Make sure the response is a valid data object and not a Promise object.
2340
+
if ( _.isObject( response ) && ! _.isFunction( response.always ) ) {
2341
+
return true;
2342
+
}
2343
+
2344
+
if ( _.isString( response ) && '-1' === response ) {
2345
+
error = __( 'An error has occurred. Please reload the page and try again.' );
2346
+
} else if ( _.isString( response ) ) {
2347
+
error = response;
2348
+
} else if ( 'undefined' !== typeof response.readyState && 0 === response.readyState ) {
2349
+
error = __( 'Connection lost or the server is busy. Please try again later.' );
2350
+
} else if ( _.isString( response.responseText ) && '' !== response.responseText ) {
2351
+
error = response.responseText;
2352
+
} else if ( _.isString( response.statusText ) ) {
2353
+
error = response.statusText;
2354
+
}
2355
+
2356
+
switch ( action ) {
2357
+
case 'update':
2358
+
/* translators: %s: Error string for a failed update. */
2359
+
errorMessage = __( 'Update failed: %s' );
2360
+
break;
2361
+
2362
+
case 'install':
2363
+
/* translators: %s: Error string for a failed installation. */
2364
+
errorMessage = __( 'Installation failed: %s' );
2365
+
break;
2366
+
2367
+
case 'check-dependencies':
2368
+
/* translators: %s: Error string for a failed dependencies check. */
2369
+
errorMessage = __( 'Dependencies check failed: %s' );
2370
+
break;
2371
+
2372
+
case 'activate':
2373
+
/* translators: %s: Error string for a failed activation. */
2374
+
errorMessage = __( 'Activation failed: %s' );
2375
+
break;
2376
+
2377
+
case 'delete':
2378
+
/* translators: %s: Error string for a failed deletion. */
2379
+
errorMessage = __( 'Deletion failed: %s' );
2380
+
break;
2381
+
}
2382
+
2383
+
// Messages are escaped, remove HTML tags to make them more readable.
2384
+
error = error.replace( /<[\/a-z][^<>]*>/gi, '' );
2385
+
errorMessage = errorMessage.replace( '%s', error );
2386
+
2387
+
// Add admin notice.
2388
+
wp.updates.addAdminNotice( {
2389
+
id: 'unknown_error',
2390
+
className: 'notice-error is-dismissible',
2391
+
message: _.escape( errorMessage )
2392
+
} );
2393
+
2394
+
// Remove the lock, and clear the queue.
2395
+
wp.updates.ajaxLocked = false;
2396
+
wp.updates.queue = [];
2397
+
2398
+
// Change buttons of all running updates.
2399
+
$( '.button.updating-message' )
2400
+
.removeClass( 'updating-message' )
2401
+
.removeAttr( 'aria-label' )
2402
+
.prop( 'disabled', true )
2403
+
.text( __( 'Update failed.' ) );
2404
+
2405
+
$( '.updating-message:not(.button):not(.thickbox)' )
2406
+
.removeClass( 'updating-message notice-warning' )
2407
+
.addClass( 'notice-error' )
2408
+
.find( 'p' )
2409
+
.removeAttr( 'aria-label' )
2410
+
.text( errorMessage );
2411
+
2412
+
wp.a11y.speak( errorMessage, 'assertive' );
2413
+
2414
+
return false;
2415
+
};
2416
+
2417
+
/**
2418
+
* Potentially adds an AYS to a user attempting to leave the page.
2419
+
*
2420
+
* If an update is on-going and a user attempts to leave the page,
2421
+
* opens an "Are you sure?" alert.
2422
+
*
2423
+
* @since 4.2.0
2424
+
*/
2425
+
wp.updates.beforeunload = function() {
2426
+
if ( wp.updates.ajaxLocked ) {
2427
+
return __( 'Updates may not complete if you navigate away from this page.' );
2428
+
}
2429
+
};
2430
+
2431
+
$( function() {
2432
+
var $pluginFilter = $( '#plugin-filter, #plugin-information-footer' ),
2433
+
$bulkActionForm = $( '#bulk-action-form' ),
2434
+
$filesystemForm = $( '#request-filesystem-credentials-form' ),
2435
+
$filesystemModal = $( '#request-filesystem-credentials-dialog' ),
2436
+
$pluginSearch = $( '.plugins-php .wp-filter-search' ),
2437
+
$pluginInstallSearch = $( '.plugin-install-php .wp-filter-search' );
2438
+
2439
+
settings = _.extend( settings, window._wpUpdatesItemCounts || {} );
2440
+
2441
+
if ( settings.totals ) {
2442
+
wp.updates.refreshCount();
2443
+
}
2444
+
2445
+
/*
2446
+
* Whether a user needs to submit filesystem credentials.
2447
+
*
2448
+
* This is based on whether the form was output on the page server-side.
2449
+
*
2450
+
* @see {wp_print_request_filesystem_credentials_modal() in PHP}
2451
+
*/
2452
+
wp.updates.shouldRequestFilesystemCredentials = $filesystemModal.length > 0;
2453
+
2454
+
/**
2455
+
* File system credentials form submit noop-er / handler.
2456
+
*
2457
+
* @since 4.2.0
2458
+
*/
2459
+
$filesystemModal.on( 'submit', 'form', function( event ) {
2460
+
event.preventDefault();
2461
+
2462
+
// Persist the credentials input by the user for the duration of the page load.
2463
+
wp.updates.filesystemCredentials.ftp.hostname = $( '#hostname' ).val();
2464
+
wp.updates.filesystemCredentials.ftp.username = $( '#username' ).val();
2465
+
wp.updates.filesystemCredentials.ftp.password = $( '#password' ).val();
2466
+
wp.updates.filesystemCredentials.ftp.connectionType = $( 'input[name="connection_type"]:checked' ).val();
2467
+
wp.updates.filesystemCredentials.ssh.publicKey = $( '#public_key' ).val();
2468
+
wp.updates.filesystemCredentials.ssh.privateKey = $( '#private_key' ).val();
2469
+
wp.updates.filesystemCredentials.fsNonce = $( '#_fs_nonce' ).val();
2470
+
wp.updates.filesystemCredentials.available = true;
2471
+
2472
+
// Unlock and invoke the queue.
2473
+
wp.updates.ajaxLocked = false;
2474
+
wp.updates.queueChecker();
2475
+
2476
+
wp.updates.requestForCredentialsModalClose();
2477
+
} );
2478
+
2479
+
/**
2480
+
* Closes the request credentials modal when clicking the 'Cancel' button or outside of the modal.
2481
+
*
2482
+
* @since 4.2.0
2483
+
*/
2484
+
$filesystemModal.on( 'click', '[data-js-action="close"], .notification-dialog-background', wp.updates.requestForCredentialsModalCancel );
2485
+
2486
+
/**
2487
+
* Hide SSH fields when not selected.
2488
+
*
2489
+
* @since 4.2.0
2490
+
*/
2491
+
$filesystemForm.on( 'change', 'input[name="connection_type"]', function() {
2492
+
$( '#ssh-keys' ).toggleClass( 'hidden', ( 'ssh' !== $( this ).val() ) );
2493
+
} ).trigger( 'change' );
2494
+
2495
+
/**
2496
+
* Handles events after the credential modal was closed.
2497
+
*
2498
+
* @since 4.6.0
2499
+
*
2500
+
* @param {Event} event Event interface.
2501
+
* @param {string} job The install/update.delete request.
2502
+
*/
2503
+
$document.on( 'credential-modal-cancel', function( event, job ) {
2504
+
var $updatingMessage = $( '.updating-message' ),
2505
+
$message, originalText;
2506
+
2507
+
if ( 'import' === pagenow ) {
2508
+
$updatingMessage.removeClass( 'updating-message' );
2509
+
} else if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
2510
+
if ( 'update-plugin' === job.action ) {
2511
+
$message = $( 'tr[data-plugin="' + job.data.plugin + '"]' ).find( '.update-message' );
2512
+
} else if ( 'delete-plugin' === job.action ) {
2513
+
$message = $( '[data-plugin="' + job.data.plugin + '"]' ).find( '.row-actions a.delete' );
2514
+
}
2515
+
} else if ( 'themes' === pagenow || 'themes-network' === pagenow ) {
2516
+
if ( 'update-theme' === job.action ) {
2517
+
$message = $( '[data-slug="' + job.data.slug + '"]' ).find( '.update-message' );
2518
+
} else if ( 'delete-theme' === job.action && 'themes-network' === pagenow ) {
2519
+
$message = $( '[data-slug="' + job.data.slug + '"]' ).find( '.row-actions a.delete' );
2520
+
} else if ( 'delete-theme' === job.action && 'themes' === pagenow ) {
2521
+
$message = $( '.theme-actions .delete-theme' );
2522
+
}
2523
+
} else {
2524
+
$message = $updatingMessage;
2525
+
}
2526
+
2527
+
if ( $message && $message.hasClass( 'updating-message' ) ) {
2528
+
originalText = $message.data( 'originaltext' );
2529
+
2530
+
if ( 'undefined' === typeof originalText ) {
2531
+
originalText = $( '<p>' ).html( $message.find( 'p' ).data( 'originaltext' ) );
2532
+
}
2533
+
2534
+
$message
2535
+
.removeClass( 'updating-message' )
2536
+
.html( originalText );
2537
+
2538
+
if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
2539
+
if ( 'update-plugin' === job.action ) {
2540
+
$message.attr(
2541
+
'aria-label',
2542
+
sprintf(
2543
+
/* translators: %s: Plugin name and version. */
2544
+
_x( 'Update %s now', 'plugin' ),
2545
+
$message.data( 'name' )
2546
+
)
2547
+
);
2548
+
} else if ( 'install-plugin' === job.action ) {
2549
+
$message.attr(
2550
+
'aria-label',
2551
+
sprintf(
2552
+
/* translators: %s: Plugin name. */
2553
+
_x( 'Install %s now', 'plugin' ),
2554
+
$message.data( 'name' )
2555
+
)
2556
+
);
2557
+
}
2558
+
}
2559
+
}
2560
+
2561
+
wp.a11y.speak( __( 'Update canceled.' ) );
2562
+
} );
2563
+
2564
+
/**
2565
+
* Click handler for plugin updates in List Table view.
2566
+
*
2567
+
* @since 4.2.0
2568
+
*
2569
+
* @param {Event} event Event interface.
2570
+
*/
2571
+
$bulkActionForm.on( 'click', '[data-plugin] .update-link', function( event ) {
2572
+
var $message = $( event.target ),
2573
+
$pluginRow = $message.parents( 'tr' );
2574
+
2575
+
event.preventDefault();
2576
+
2577
+
if ( $message.hasClass( 'updating-message' ) || $message.hasClass( 'button-disabled' ) ) {
2578
+
return;
2579
+
}
2580
+
2581
+
wp.updates.maybeRequestFilesystemCredentials( event );
2582
+
2583
+
// Return the user to the input box of the plugin's table row after closing the modal.
2584
+
wp.updates.$elToReturnFocusToFromCredentialsModal = $pluginRow.find( '.check-column input' );
2585
+
wp.updates.updatePlugin( {
2586
+
plugin: $pluginRow.data( 'plugin' ),
2587
+
slug: $pluginRow.data( 'slug' )
2588
+
} );
2589
+
} );
2590
+
2591
+
/**
2592
+
* Click handler for plugin updates in plugin install view.
2593
+
*
2594
+
* @since 4.2.0
2595
+
*
2596
+
* @param {Event} event Event interface.
2597
+
*/
2598
+
$pluginFilter.on( 'click', '.update-now', function( event ) {
2599
+
var $button = $( event.target );
2600
+
event.preventDefault();
2601
+
2602
+
if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
2603
+
return;
2604
+
}
2605
+
2606
+
wp.updates.maybeRequestFilesystemCredentials( event );
2607
+
2608
+
wp.updates.updatePlugin( {
2609
+
plugin: $button.data( 'plugin' ),
2610
+
slug: $button.data( 'slug' )
2611
+
} );
2612
+
} );
2613
+
2614
+
/**
2615
+
* Click handler for plugin installs in plugin install view.
2616
+
*
2617
+
* @since 4.6.0
2618
+
*
2619
+
* @param {Event} event Event interface.
2620
+
*/
2621
+
$pluginFilter.on( 'click', '.install-now', function( event ) {
2622
+
var $button = $( event.target );
2623
+
event.preventDefault();
2624
+
2625
+
if ( $button.hasClass( 'updating-message' ) || $button.hasClass( 'button-disabled' ) ) {
2626
+
return;
2627
+
}
2628
+
2629
+
if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
2630
+
wp.updates.requestFilesystemCredentials( event );
2631
+
2632
+
$document.on( 'credential-modal-cancel', function() {
2633
+
var $message = $( '.install-now.updating-message' );
2634
+
2635
+
$message
2636
+
.removeClass( 'updating-message' )
2637
+
.text( _x( 'Install Now', 'plugin' ) );
2638
+
2639
+
wp.a11y.speak( __( 'Update canceled.' ) );
2640
+
} );
2641
+
}
2642
+
2643
+
wp.updates.installPlugin( {
2644
+
slug: $button.data( 'slug' )
2645
+
} );
2646
+
} );
2647
+
2648
+
/**
2649
+
* Click handler for plugin activations in plugin activation modal view.
2650
+
*
2651
+
* @since 6.5.0
2652
+
* @since 6.5.4 Redirect the parent window to the activation URL.
2653
+
*
2654
+
* @param {Event} event Event interface.
2655
+
*/
2656
+
$document.on( 'click', '#plugin-information-footer .activate-now', function( event ) {
2657
+
event.preventDefault();
2658
+
window.parent.location.href = $( event.target ).attr( 'href' );
2659
+
});
2660
+
2661
+
/**
2662
+
* Click handler for importer plugins installs in the Import screen.
2663
+
*
2664
+
* @since 4.6.0
2665
+
*
2666
+
* @param {Event} event Event interface.
2667
+
*/
2668
+
$document.on( 'click', '.importer-item .install-now', function( event ) {
2669
+
var $button = $( event.target ),
2670
+
pluginName = $( this ).data( 'name' );
2671
+
2672
+
event.preventDefault();
2673
+
2674
+
if ( $button.hasClass( 'updating-message' ) ) {
2675
+
return;
2676
+
}
2677
+
2678
+
if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.ajaxLocked ) {
2679
+
wp.updates.requestFilesystemCredentials( event );
2680
+
2681
+
$document.on( 'credential-modal-cancel', function() {
2682
+
2683
+
$button
2684
+
.removeClass( 'updating-message' )
2685
+
.attr(
2686
+
'aria-label',
2687
+
sprintf(
2688
+
/* translators: %s: Plugin name. */
2689
+
_x( 'Install %s now', 'plugin' ),
2690
+
pluginName
2691
+
)
2692
+
)
2693
+
.text( _x( 'Install Now', 'plugin' ) );
2694
+
2695
+
wp.a11y.speak( __( 'Update canceled.' ) );
2696
+
} );
2697
+
}
2698
+
2699
+
wp.updates.installPlugin( {
2700
+
slug: $button.data( 'slug' ),
2701
+
pagenow: pagenow,
2702
+
success: wp.updates.installImporterSuccess,
2703
+
error: wp.updates.installImporterError
2704
+
} );
2705
+
} );
2706
+
2707
+
/**
2708
+
* Click handler for plugin deletions.
2709
+
*
2710
+
* @since 4.6.0
2711
+
*
2712
+
* @param {Event} event Event interface.
2713
+
*/
2714
+
$bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) {
2715
+
var $pluginRow = $( event.target ).parents( 'tr' ),
2716
+
confirmMessage;
2717
+
2718
+
if ( $pluginRow.hasClass( 'is-uninstallable' ) ) {
2719
+
confirmMessage = sprintf(
2720
+
/* translators: %s: Plugin name. */
2721
+
__( 'Are you sure you want to delete %s and its data?' ),
2722
+
$pluginRow.find( '.plugin-title strong' ).text()
2723
+
);
2724
+
} else {
2725
+
confirmMessage = sprintf(
2726
+
/* translators: %s: Plugin name. */
2727
+
__( 'Are you sure you want to delete %s?' ),
2728
+
$pluginRow.find( '.plugin-title strong' ).text()
2729
+
);
2730
+
}
2731
+
2732
+
event.preventDefault();
2733
+
2734
+
if ( ! window.confirm( confirmMessage ) ) {
2735
+
return;
2736
+
}
2737
+
2738
+
wp.updates.maybeRequestFilesystemCredentials( event );
2739
+
2740
+
wp.updates.deletePlugin( {
2741
+
plugin: $pluginRow.data( 'plugin' ),
2742
+
slug: $pluginRow.data( 'slug' )
2743
+
} );
2744
+
2745
+
} );
2746
+
2747
+
/**
2748
+
* Click handler for theme updates.
2749
+
*
2750
+
* @since 4.6.0
2751
+
*
2752
+
* @param {Event} event Event interface.
2753
+
*/
2754
+
$document.on( 'click', '.themes-php.network-admin .update-link', function( event ) {
2755
+
var $message = $( event.target ),
2756
+
$themeRow = $message.parents( 'tr' );
2757
+
2758
+
event.preventDefault();
2759
+
2760
+
if ( $message.hasClass( 'updating-message' ) || $message.hasClass( 'button-disabled' ) ) {
2761
+
return;
2762
+
}
2763
+
2764
+
wp.updates.maybeRequestFilesystemCredentials( event );
2765
+
2766
+
// Return the user to the input box of the theme's table row after closing the modal.
2767
+
wp.updates.$elToReturnFocusToFromCredentialsModal = $themeRow.find( '.check-column input' );
2768
+
wp.updates.updateTheme( {
2769
+
slug: $themeRow.data( 'slug' )
2770
+
} );
2771
+
} );
2772
+
2773
+
/**
2774
+
* Click handler for theme deletions.
2775
+
*
2776
+
* @since 4.6.0
2777
+
*
2778
+
* @param {Event} event Event interface.
2779
+
*/
2780
+
$document.on( 'click', '.themes-php.network-admin a.delete', function( event ) {
2781
+
var $themeRow = $( event.target ).parents( 'tr' ),
2782
+
confirmMessage = sprintf(
2783
+
/* translators: %s: Theme name. */
2784
+
__( 'Are you sure you want to delete %s?' ),
2785
+
$themeRow.find( '.theme-title strong' ).text()
2786
+
);
2787
+
2788
+
event.preventDefault();
2789
+
2790
+
if ( ! window.confirm( confirmMessage ) ) {
2791
+
return;
2792
+
}
2793
+
2794
+
wp.updates.maybeRequestFilesystemCredentials( event );
2795
+
2796
+
wp.updates.deleteTheme( {
2797
+
slug: $themeRow.data( 'slug' )
2798
+
} );
2799
+
} );
2800
+
2801
+
/**
2802
+
* Bulk action handler for plugins and themes.
2803
+
*
2804
+
* Handles both deletions and updates.
2805
+
*
2806
+
* @since 4.6.0
2807
+
*
2808
+
* @param {Event} event Event interface.
2809
+
*/
2810
+
$bulkActionForm.on( 'click', '[type="submit"]:not([name="clear-recent-list"])', function( event ) {
2811
+
var bulkAction = $( event.target ).siblings( 'select' ).val(),
2812
+
itemsSelected = $bulkActionForm.find( 'input[name="checked[]"]:checked' ),
2813
+
success = 0,
2814
+
error = 0,
2815
+
errorMessages = [],
2816
+
type, action;
2817
+
2818
+
// Determine which type of item we're dealing with.
2819
+
switch ( pagenow ) {
2820
+
case 'plugins':
2821
+
case 'plugins-network':
2822
+
type = 'plugin';
2823
+
break;
2824
+
2825
+
case 'themes-network':
2826
+
type = 'theme';
2827
+
break;
2828
+
2829
+
default:
2830
+
return;
2831
+
}
2832
+
2833
+
// Bail if there were no items selected.
2834
+
if ( ! itemsSelected.length ) {
2835
+
bulkAction = false;
2836
+
}
2837
+
2838
+
// Determine the type of request we're dealing with.
2839
+
switch ( bulkAction ) {
2840
+
case 'update-selected':
2841
+
action = bulkAction.replace( 'selected', type );
2842
+
break;
2843
+
2844
+
case 'delete-selected':
2845
+
var confirmMessage = 'plugin' === type ?
2846
+
__( 'Are you sure you want to delete the selected plugins and their data?' ) :
2847
+
__( 'Caution: These themes may be active on other sites in the network. Are you sure you want to proceed?' );
2848
+
2849
+
if ( ! window.confirm( confirmMessage ) ) {
2850
+
event.preventDefault();
2851
+
return;
2852
+
}
2853
+
2854
+
action = bulkAction.replace( 'selected', type );
2855
+
break;
2856
+
2857
+
default:
2858
+
return;
2859
+
}
2860
+
2861
+
wp.updates.maybeRequestFilesystemCredentials( event );
2862
+
2863
+
event.preventDefault();
2864
+
2865
+
// Un-check the bulk checkboxes.
2866
+
$bulkActionForm.find( '.manage-column [type="checkbox"]' ).prop( 'checked', false );
2867
+
2868
+
$document.trigger( 'wp-' + type + '-bulk-' + bulkAction, itemsSelected );
2869
+
2870
+
// Find all the checkboxes which have been checked.
2871
+
itemsSelected.each( function( index, element ) {
2872
+
var $checkbox = $( element ),
2873
+
$itemRow = $checkbox.parents( 'tr' );
2874
+
2875
+
// Only add update-able items to the update queue.
2876
+
if ( 'update-selected' === bulkAction && ( ! $itemRow.hasClass( 'update' ) || $itemRow.find( 'notice-error' ).length ) ) {
2877
+
2878
+
// Un-check the box.
2879
+
$checkbox.prop( 'checked', false );
2880
+
return;
2881
+
}
2882
+
2883
+
// Don't add items to the update queue again, even if the user clicks the update button several times.
2884
+
if ( 'update-selected' === bulkAction && $itemRow.hasClass( 'is-enqueued' ) ) {
2885
+
return;
2886
+
}
2887
+
2888
+
$itemRow.addClass( 'is-enqueued' );
2889
+
2890
+
// Add it to the queue.
2891
+
wp.updates.queue.push( {
2892
+
action: action,
2893
+
data: {
2894
+
plugin: $itemRow.data( 'plugin' ),
2895
+
slug: $itemRow.data( 'slug' )
2896
+
}
2897
+
} );
2898
+
} );
2899
+
2900
+
// Display bulk notification for updates of any kind.
2901
+
$document.on( 'wp-plugin-update-success wp-plugin-update-error wp-theme-update-success wp-theme-update-error', function( event, response ) {
2902
+
var $itemRow = $( '[data-slug="' + response.slug + '"]' ),
2903
+
$bulkActionNotice, itemName;
2904
+
2905
+
if ( 'wp-' + response.update + '-update-success' === event.type ) {
2906
+
success++;
2907
+
} else {
2908
+
itemName = response.pluginName ? response.pluginName : $itemRow.find( '.column-primary strong' ).text();
2909
+
2910
+
error++;
2911
+
errorMessages.push( itemName + ': ' + response.errorMessage );
2912
+
}
2913
+
2914
+
$itemRow.find( 'input[name="checked[]"]:checked' ).prop( 'checked', false );
2915
+
2916
+
wp.updates.adminNotice = wp.template( 'wp-bulk-updates-admin-notice' );
2917
+
2918
+
var successMessage = null;
2919
+
2920
+
if ( success ) {
2921
+
if ( 'plugin' === response.update ) {
2922
+
successMessage = sprintf(
2923
+
/* translators: %s: Number of plugins. */
2924
+
_n( '%s plugin successfully updated.', '%s plugins successfully updated.', success ),
2925
+
success
2926
+
);
2927
+
} else {
2928
+
successMessage = sprintf(
2929
+
/* translators: %s: Number of themes. */
2930
+
_n( '%s theme successfully updated.', '%s themes successfully updated.', success ),
2931
+
success
2932
+
);
2933
+
}
2934
+
}
2935
+
2936
+
var errorMessage = null;
2937
+
2938
+
if ( error ) {
2939
+
errorMessage = sprintf(
2940
+
/* translators: %s: Number of failed updates. */
2941
+
_n( '%s update failed.', '%s updates failed.', error ),
2942
+
error
2943
+
);
2944
+
}
2945
+
2946
+
wp.updates.addAdminNotice( {
2947
+
id: 'bulk-action-notice',
2948
+
className: 'bulk-action-notice',
2949
+
successMessage: successMessage,
2950
+
errorMessage: errorMessage,
2951
+
errorMessages: errorMessages,
2952
+
type: response.update
2953
+
} );
2954
+
2955
+
$bulkActionNotice = $( '#bulk-action-notice' ).on( 'click', 'button', function() {
2956
+
// $( this ) is the clicked button, no need to get it again.
2957
+
$( this )
2958
+
.toggleClass( 'bulk-action-errors-collapsed' )
2959
+
.attr( 'aria-expanded', ! $( this ).hasClass( 'bulk-action-errors-collapsed' ) );
2960
+
// Show the errors list.
2961
+
$bulkActionNotice.find( '.bulk-action-errors' ).toggleClass( 'hidden' );
2962
+
} );
2963
+
2964
+
if ( error > 0 && ! wp.updates.queue.length ) {
2965
+
$( 'html, body' ).animate( { scrollTop: 0 } );
2966
+
}
2967
+
} );
2968
+
2969
+
// Reset admin notice template after #bulk-action-notice was added.
2970
+
$document.on( 'wp-updates-notice-added', function() {
2971
+
wp.updates.adminNotice = wp.template( 'wp-updates-admin-notice' );
2972
+
} );
2973
+
2974
+
// Check the queue, now that the event handlers have been added.
2975
+
wp.updates.queueChecker();
2976
+
} );
2977
+
2978
+
if ( $pluginInstallSearch.length ) {
2979
+
$pluginInstallSearch.attr( 'aria-describedby', 'live-search-desc' );
2980
+
}
2981
+
2982
+
// Track the previous search string length.
2983
+
var previousSearchStringLength = 0;
2984
+
wp.updates.shouldSearch = function( searchStringLength ) {
2985
+
var shouldSearch = searchStringLength >= wp.updates.searchMinCharacters ||
2986
+
previousSearchStringLength > wp.updates.searchMinCharacters;
2987
+
previousSearchStringLength = searchStringLength;
2988
+
return shouldSearch;
2989
+
};
2990
+
2991
+
/**
2992
+
* Handles changes to the plugin search box on the new-plugin page,
2993
+
* searching the repository dynamically.
2994
+
*
2995
+
* @since 4.6.0
2996
+
*/
2997
+
$pluginInstallSearch.on( 'keyup input', _.debounce( function( event, eventtype ) {
2998
+
var $searchTab = $( '.plugin-install-search' ), data, searchLocation,
2999
+
searchStringLength = $pluginInstallSearch.val().length;
3000
+
3001
+
data = {
3002
+
_ajax_nonce: wp.updates.ajaxNonce,
3003
+
s: encodeURIComponent( event.target.value ),
3004
+
tab: 'search',
3005
+
type: $( '#typeselector' ).val(),
3006
+
pagenow: pagenow
3007
+
};
3008
+
searchLocation = location.href.split( '?' )[ 0 ] + '?' + $.param( _.omit( data, [ '_ajax_nonce', 'pagenow' ] ) );
3009
+
3010
+
// Set the autocomplete attribute, turning off autocomplete 1 character before ajax search kicks in.
3011
+
if ( wp.updates.shouldSearch( searchStringLength ) ) {
3012
+
$pluginInstallSearch.attr( 'autocomplete', 'off' );
3013
+
} else {
3014
+
$pluginInstallSearch.attr( 'autocomplete', 'on' );
3015
+
return;
3016
+
}
3017
+
3018
+
// Clear on escape.
3019
+
if ( 'keyup' === event.type && 27 === event.which ) {
3020
+
event.target.value = '';
3021
+
}
3022
+
3023
+
if ( wp.updates.searchTerm === data.s && 'typechange' !== eventtype ) {
3024
+
return;
3025
+
} else {
3026
+
$pluginFilter.empty();
3027
+
wp.updates.searchTerm = data.s;
3028
+
}
3029
+
3030
+
if ( window.history && window.history.replaceState ) {
3031
+
window.history.replaceState( null, '', searchLocation );
3032
+
}
3033
+
3034
+
if ( ! $searchTab.length ) {
3035
+
$searchTab = $( '<li class="plugin-install-search" />' )
3036
+
.append( $( '<a />', {
3037
+
'class': 'current',
3038
+
'href': searchLocation,
3039
+
'text': __( 'Search Results' )
3040
+
} ) );
3041
+
3042
+
$( '.wp-filter .filter-links .current' )
3043
+
.removeClass( 'current' )
3044
+
.parents( '.filter-links' )
3045
+
.prepend( $searchTab );
3046
+
3047
+
$pluginFilter.prev( 'p' ).remove();
3048
+
$( '.plugins-popular-tags-wrapper' ).remove();
3049
+
}
3050
+
3051
+
if ( 'undefined' !== typeof wp.updates.searchRequest ) {
3052
+
wp.updates.searchRequest.abort();
3053
+
}
3054
+
$( 'body' ).addClass( 'loading-content' );
3055
+
3056
+
wp.updates.searchRequest = wp.ajax.post( 'search-install-plugins', data ).done( function( response ) {
3057
+
$( 'body' ).removeClass( 'loading-content' );
3058
+
$pluginFilter.append( response.items );
3059
+
delete wp.updates.searchRequest;
3060
+
3061
+
if ( 0 === response.count ) {
3062
+
wp.a11y.speak( __( 'You do not appear to have any plugins available at this time.' ) );
3063
+
} else {
3064
+
wp.a11y.speak(
3065
+
sprintf(
3066
+
/* translators: %s: Number of plugins. */
3067
+
__( 'Number of plugins found: %d' ),
3068
+
response.count
3069
+
)
3070
+
);
3071
+
}
3072
+
} );
3073
+
}, 1000 ) );
3074
+
3075
+
if ( $pluginSearch.length ) {
3076
+
$pluginSearch.attr( 'aria-describedby', 'live-search-desc' );
3077
+
3078
+
}
3079
+
3080
+
/**
3081
+
* Handles changes to the plugin search box on the Installed Plugins screen,
3082
+
* searching the plugin list dynamically.
3083
+
*
3084
+
* @since 4.6.0
3085
+
*/
3086
+
$pluginSearch.on( 'keyup input', _.debounce( function( event ) {
3087
+
var data = {
3088
+
_ajax_nonce: wp.updates.ajaxNonce,
3089
+
s: encodeURIComponent( event.target.value ),
3090
+
pagenow: pagenow,
3091
+
plugin_status: 'all'
3092
+
},
3093
+
queryArgs,
3094
+
searchStringLength = $pluginSearch.val().length;
3095
+
3096
+
// Set the autocomplete attribute, turning off autocomplete 1 character before ajax search kicks in.
3097
+
if ( wp.updates.shouldSearch( searchStringLength ) ) {
3098
+
$pluginSearch.attr( 'autocomplete', 'off' );
3099
+
} else {
3100
+
$pluginSearch.attr( 'autocomplete', 'on' );
3101
+
return;
3102
+
}
3103
+
3104
+
// Clear on escape.
3105
+
if ( 'keyup' === event.type && 27 === event.which ) {
3106
+
event.target.value = '';
3107
+
}
3108
+
3109
+
if ( wp.updates.searchTerm === data.s ) {
3110
+
return;
3111
+
} else {
3112
+
wp.updates.searchTerm = data.s;
3113
+
}
3114
+
3115
+
queryArgs = _.object( _.compact( _.map( location.search.slice( 1 ).split( '&' ), function( item ) {
3116
+
if ( item ) return item.split( '=' );
3117
+
} ) ) );
3118
+
3119
+
data.plugin_status = queryArgs.plugin_status || 'all';
3120
+
3121
+
if ( window.history && window.history.replaceState ) {
3122
+
window.history.replaceState( null, '', location.href.split( '?' )[ 0 ] + '?s=' + data.s + '&plugin_status=' + data.plugin_status );
3123
+
}
3124
+
3125
+
if ( 'undefined' !== typeof wp.updates.searchRequest ) {
3126
+
wp.updates.searchRequest.abort();
3127
+
}
3128
+
3129
+
$bulkActionForm.empty();
3130
+
$( 'body' ).addClass( 'loading-content' );
3131
+
$( '.subsubsub .current' ).removeClass( 'current' );
3132
+
3133
+
wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) {
3134
+
3135
+
// Can we just ditch this whole subtitle business?
3136
+
var $subTitle = $( '<span />' ).addClass( 'subtitle' ).html(
3137
+
sprintf(
3138
+
/* translators: %s: Search query. */
3139
+
__( 'Search results for: %s' ),
3140
+
'<strong>' + _.escape( decodeURIComponent( data.s ) ) + '</strong>'
3141
+
) ),
3142
+
$oldSubTitle = $( '.wrap .subtitle' );
3143
+
3144
+
if ( ! data.s.length ) {
3145
+
$oldSubTitle.remove();
3146
+
$( '.subsubsub .' + data.plugin_status + ' a' ).addClass( 'current' );
3147
+
} else if ( $oldSubTitle.length ) {
3148
+
$oldSubTitle.replaceWith( $subTitle );
3149
+
} else {
3150
+
$( '.wp-header-end' ).before( $subTitle );
3151
+
}
3152
+
3153
+
$( 'body' ).removeClass( 'loading-content' );
3154
+
$bulkActionForm.append( response.items );
3155
+
delete wp.updates.searchRequest;
3156
+
3157
+
if ( 0 === response.count ) {
3158
+
wp.a11y.speak( __( 'No plugins found. Try a different search.' ) );
3159
+
} else {
3160
+
wp.a11y.speak(
3161
+
sprintf(
3162
+
/* translators: %s: Number of plugins. */
3163
+
__( 'Number of plugins found: %d' ),
3164
+
response.count
3165
+
)
3166
+
);
3167
+
}
3168
+
} );
3169
+
}, 500 ) );
3170
+
3171
+
/**
3172
+
* Trigger a search event when the search form gets submitted.
3173
+
*
3174
+
* @since 4.6.0
3175
+
*/
3176
+
$document.on( 'submit', '.search-plugins', function( event ) {
3177
+
event.preventDefault();
3178
+
3179
+
$( 'input.wp-filter-search' ).trigger( 'input' );
3180
+
} );
3181
+
3182
+
/**
3183
+
* Trigger a search event when the "Try Again" button is clicked.
3184
+
*
3185
+
* @since 4.9.0
3186
+
*/
3187
+
$document.on( 'click', '.try-again', function( event ) {
3188
+
event.preventDefault();
3189
+
$pluginInstallSearch.trigger( 'input' );
3190
+
} );
3191
+
3192
+
/**
3193
+
* Trigger a search event when the search type gets changed.
3194
+
*
3195
+
* @since 4.6.0
3196
+
*/
3197
+
$( '#typeselector' ).on( 'change', function() {
3198
+
var $search = $( 'input[name="s"]' );
3199
+
3200
+
if ( $search.val().length ) {
3201
+
$search.trigger( 'input', 'typechange' );
3202
+
}
3203
+
} );
3204
+
3205
+
/**
3206
+
* Click handler for updating a plugin from the details modal on `plugin-install.php`.
3207
+
*
3208
+
* @since 4.2.0
3209
+
*
3210
+
* @param {Event} event Event interface.
3211
+
*/
3212
+
$( '#plugin_update_from_iframe' ).on( 'click', function( event ) {
3213
+
var target = window.parent === window ? null : window.parent,
3214
+
update;
3215
+
3216
+
$.support.postMessage = !! window.postMessage;
3217
+
3218
+
if ( false === $.support.postMessage || null === target || -1 !== window.parent.location.pathname.indexOf( 'update-core.php' ) ) {
3219
+
return;
3220
+
}
3221
+
3222
+
event.preventDefault();
3223
+
3224
+
update = {
3225
+
action: 'update-plugin',
3226
+
data: {
3227
+
plugin: $( this ).data( 'plugin' ),
3228
+
slug: $( this ).data( 'slug' )
3229
+
}
3230
+
};
3231
+
3232
+
target.postMessage( JSON.stringify( update ), window.location.origin );
3233
+
} );
3234
+
3235
+
/**
3236
+
* Handles postMessage events.
3237
+
*
3238
+
* @since 4.2.0
3239
+
* @since 4.6.0 Switched `update-plugin` action to use the queue.
3240
+
*
3241
+
* @param {Event} event Event interface.
3242
+
*/
3243
+
$( window ).on( 'message', function( event ) {
3244
+
var originalEvent = event.originalEvent,
3245
+
expectedOrigin = document.location.protocol + '//' + document.location.host,
3246
+
message;
3247
+
3248
+
if ( originalEvent.origin !== expectedOrigin ) {
3249
+
return;
3250
+
}
3251
+
3252
+
try {
3253
+
message = JSON.parse( originalEvent.data );
3254
+
} catch ( e ) {
3255
+
return;
3256
+
}
3257
+
3258
+
if ( ! message ) {
3259
+
return;
3260
+
}
3261
+
3262
+
if (
3263
+
'undefined' !== typeof message.status &&
3264
+
'undefined' !== typeof message.slug &&
3265
+
'undefined' !== typeof message.text &&
3266
+
'undefined' !== typeof message.ariaLabel
3267
+
) {
3268
+
var $card = $( '.plugin-card-' + message.slug ),
3269
+
$message = $card.find( '[data-slug="' + message.slug + '"]' );
3270
+
3271
+
if ( 'undefined' !== typeof message.removeClasses ) {
3272
+
$message.removeClass( message.removeClasses );
3273
+
}
3274
+
3275
+
if ( 'undefined' !== typeof message.addClasses ) {
3276
+
$message.addClass( message.addClasses );
3277
+
}
3278
+
3279
+
if ( '' === message.ariaLabel ) {
3280
+
$message.removeAttr( 'aria-label' );
3281
+
} else {
3282
+
$message.attr( 'aria-label', message.ariaLabel );
3283
+
}
3284
+
3285
+
if ( 'dependencies-check-success' === message.status ) {
3286
+
$message
3287
+
.attr( 'data-name', message.pluginName )
3288
+
.attr( 'data-slug', message.slug )
3289
+
.attr( 'data-plugin', message.plugin )
3290
+
.attr( 'href', message.href );
3291
+
}
3292
+
3293
+
$message.text( message.text );
3294
+
}
3295
+
3296
+
if ( 'undefined' === typeof message.action ) {
3297
+
return;
3298
+
}
3299
+
3300
+
switch ( message.action ) {
3301
+
3302
+
// Called from `wp-admin/includes/class-wp-upgrader-skins.php`.
3303
+
case 'decrementUpdateCount':
3304
+
/** @property {string} message.upgradeType */
3305
+
wp.updates.decrementCount( message.upgradeType );
3306
+
break;
3307
+
3308
+
case 'install-plugin':
3309
+
case 'update-plugin':
3310
+
if ( 'undefined' === typeof message.data || 'undefined' === typeof message.data.slug ) {
3311
+
return;
3312
+
}
3313
+
3314
+
message.data = wp.updates._addCallbacks( message.data, message.action );
3315
+
3316
+
wp.updates.queue.push( message );
3317
+
wp.updates.queueChecker();
3318
+
break;
3319
+
}
3320
+
} );
3321
+
3322
+
/**
3323
+
* Adds a callback to display a warning before leaving the page.
3324
+
*
3325
+
* @since 4.2.0
3326
+
*/
3327
+
$( window ).on( 'beforeunload', wp.updates.beforeunload );
3328
+
3329
+
/**
3330
+
* Prevents the page form scrolling when activating auto-updates with the Spacebar key.
3331
+
*
3332
+
* @since 5.5.0
3333
+
*/
3334
+
$document.on( 'keydown', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) {
3335
+
if ( 32 === event.which ) {
3336
+
event.preventDefault();
3337
+
}
3338
+
} );
3339
+
3340
+
/**
3341
+
* Click and keyup handler for enabling and disabling plugin and theme auto-updates.
3342
+
*
3343
+
* These controls can be either links or buttons. When JavaScript is enabled,
3344
+
* we want them to behave like buttons. An ARIA role `button` is added via
3345
+
* the JavaScript that targets elements with the CSS class `aria-button-if-js`.
3346
+
*
3347
+
* @since 5.5.0
3348
+
*/
3349
+
$document.on( 'click keyup', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) {
3350
+
var data, asset, type, $parent,
3351
+
$toggler = $( this ),
3352
+
action = $toggler.attr( 'data-wp-action' ),
3353
+
$label = $toggler.find( '.label' );
3354
+
3355
+
if ( 'keyup' === event.type && 32 !== event.which ) {
3356
+
return;
3357
+
}
3358
+
3359
+
if ( 'themes' !== pagenow ) {
3360
+
$parent = $toggler.closest( '.column-auto-updates' );
3361
+
} else {
3362
+
$parent = $toggler.closest( '.theme-autoupdate' );
3363
+
}
3364
+
3365
+
event.preventDefault();
3366
+
3367
+
// Prevent multiple simultaneous requests.
3368
+
if ( $toggler.attr( 'data-doing-ajax' ) === 'yes' ) {
3369
+
return;
3370
+
}
3371
+
3372
+
$toggler.attr( 'data-doing-ajax', 'yes' );
3373
+
3374
+
switch ( pagenow ) {
3375
+
case 'plugins':
3376
+
case 'plugins-network':
3377
+
type = 'plugin';
3378
+
asset = $toggler.closest( 'tr' ).attr( 'data-plugin' );
3379
+
break;
3380
+
case 'themes-network':
3381
+
type = 'theme';
3382
+
asset = $toggler.closest( 'tr' ).attr( 'data-slug' );
3383
+
break;
3384
+
case 'themes':
3385
+
type = 'theme';
3386
+
asset = $toggler.attr( 'data-slug' );
3387
+
break;
3388
+
}
3389
+
3390
+
// Clear any previous errors.
3391
+
$parent.find( '.notice.notice-error' ).addClass( 'hidden' );
3392
+
3393
+
// Show loading status.
3394
+
if ( 'enable' === action ) {
3395
+
$label.text( __( 'Enabling...' ) );
3396
+
} else {
3397
+
$label.text( __( 'Disabling...' ) );
3398
+
}
3399
+
3400
+
$toggler.find( '.dashicons-update' ).removeClass( 'hidden' );
3401
+
3402
+
data = {
3403
+
action: 'toggle-auto-updates',
3404
+
_ajax_nonce: settings.ajax_nonce,
3405
+
state: action,
3406
+
type: type,
3407
+
asset: asset
3408
+
};
3409
+
3410
+
$.post( window.ajaxurl, data )
3411
+
.done( function( response ) {
3412
+
var $enabled, $disabled, enabledNumber, disabledNumber, errorMessage,
3413
+
href = $toggler.attr( 'href' );
3414
+
3415
+
if ( ! response.success ) {
3416
+
// if WP returns 0 for response (which can happen in a few cases),
3417
+
// output the general error message since we won't have response.data.error.
3418
+
if ( response.data && response.data.error ) {
3419
+
errorMessage = response.data.error;
3420
+
} else {
3421
+
errorMessage = __( 'The request could not be completed.' );
3422
+
}
3423
+
3424
+
$parent.find( '.notice.notice-error' ).removeClass( 'hidden' ).find( 'p' ).text( errorMessage );
3425
+
wp.a11y.speak( errorMessage, 'assertive' );
3426
+
return;
3427
+
}
3428
+
3429
+
// Update the counts in the enabled/disabled views if on a screen
3430
+
// with a list table.
3431
+
if ( 'themes' !== pagenow ) {
3432
+
$enabled = $( '.auto-update-enabled span' );
3433
+
$disabled = $( '.auto-update-disabled span' );
3434
+
enabledNumber = parseInt( $enabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0;
3435
+
disabledNumber = parseInt( $disabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0;
3436
+
3437
+
switch ( action ) {
3438
+
case 'enable':
3439
+
++enabledNumber;
3440
+
--disabledNumber;
3441
+
break;
3442
+
case 'disable':
3443
+
--enabledNumber;
3444
+
++disabledNumber;
3445
+
break;
3446
+
}
3447
+
3448
+
enabledNumber = Math.max( 0, enabledNumber );
3449
+
disabledNumber = Math.max( 0, disabledNumber );
3450
+
3451
+
$enabled.text( '(' + enabledNumber + ')' );
3452
+
$disabled.text( '(' + disabledNumber + ')' );
3453
+
}
3454
+
3455
+
if ( 'enable' === action ) {
3456
+
// The toggler control can be either a link or a button.
3457
+
if ( $toggler[ 0 ].hasAttribute( 'href' ) ) {
3458
+
href = href.replace( 'action=enable-auto-update', 'action=disable-auto-update' );
3459
+
$toggler.attr( 'href', href );
3460
+
}
3461
+
$toggler.attr( 'data-wp-action', 'disable' );
3462
+
3463
+
$label.text( __( 'Disable auto-updates' ) );
3464
+
$parent.find( '.auto-update-time' ).removeClass( 'hidden' );
3465
+
wp.a11y.speak( __( 'Auto-updates enabled' ) );
3466
+
} else {
3467
+
// The toggler control can be either a link or a button.
3468
+
if ( $toggler[ 0 ].hasAttribute( 'href' ) ) {
3469
+
href = href.replace( 'action=disable-auto-update', 'action=enable-auto-update' );
3470
+
$toggler.attr( 'href', href );
3471
+
}
3472
+
$toggler.attr( 'data-wp-action', 'enable' );
3473
+
3474
+
$label.text( __( 'Enable auto-updates' ) );
3475
+
$parent.find( '.auto-update-time' ).addClass( 'hidden' );
3476
+
wp.a11y.speak( __( 'Auto-updates disabled' ) );
3477
+
}
3478
+
3479
+
$document.trigger( 'wp-auto-update-setting-changed', { state: action, type: type, asset: asset } );
3480
+
} )
3481
+
.fail( function() {
3482
+
$parent.find( '.notice.notice-error' )
3483
+
.removeClass( 'hidden' )
3484
+
.find( 'p' )
3485
+
.text( __( 'The request could not be completed.' ) );
3486
+
3487
+
wp.a11y.speak( __( 'The request could not be completed.' ), 'assertive' );
3488
+
} )
3489
+
.always( function() {
3490
+
$toggler.removeAttr( 'data-doing-ajax' ).find( '.dashicons-update' ).addClass( 'hidden' );
3491
+
} );
3492
+
}
3493
+
);
3494
+
} );
3495
+
})( jQuery, window.wp, window._wpUpdatesSettings );
3496
+