]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/js/updates.js
WordPress 4.4.1
[autoinstalls/wordpress.git] / wp-admin / js / updates.js
1 /* global tb_remove */
2 window.wp = window.wp || {};
3
4 (function( $, wp, pagenow ) {
5         wp.updates = {};
6
7         /**
8          * User nonce for ajax calls.
9          *
10          * @since 4.2.0
11          *
12          * @var string
13          */
14         wp.updates.ajaxNonce = window._wpUpdatesSettings.ajax_nonce;
15
16         /**
17          * Localized strings.
18          *
19          * @since 4.2.0
20          *
21          * @var object
22          */
23         wp.updates.l10n = window._wpUpdatesSettings.l10n;
24
25         /**
26          * Whether filesystem credentials need to be requested from the user.
27          *
28          * @since 4.2.0
29          *
30          * @var bool
31          */
32         wp.updates.shouldRequestFilesystemCredentials = null;
33
34         /**
35          * Filesystem credentials to be packaged along with the request.
36          *
37          * @since 4.2.0
38          *
39          * @var object
40          */
41         wp.updates.filesystemCredentials = {
42                 ftp: {
43                         host: null,
44                         username: null,
45                         password: null,
46                         connectionType: null
47                 },
48                 ssh: {
49                         publicKey: null,
50                         privateKey: null
51                 }
52         };
53
54         /**
55          * Flag if we're waiting for an update to complete.
56          *
57          * @since 4.2.0
58          *
59          * @var bool
60          */
61         wp.updates.updateLock = false;
62
63         /**
64          * * Flag if we've done an update successfully.
65          *
66          * @since 4.2.0
67          *
68          * @var bool
69          */
70         wp.updates.updateDoneSuccessfully = false;
71
72         /**
73          * If the user tries to update a plugin while an update is
74          * already happening, it can be placed in this queue to perform later.
75          *
76          * @since 4.2.0
77          *
78          * @var array
79          */
80         wp.updates.updateQueue = [];
81
82         /**
83          * Store a jQuery reference to return focus to when exiting the request credentials modal.
84          *
85          * @since 4.2.0
86          *
87          * @var jQuery object
88          */
89         wp.updates.$elToReturnFocusToFromCredentialsModal = null;
90
91         /**
92          * Decrement update counts throughout the various menus.
93          *
94          * @since 3.9.0
95          *
96          * @param {string} upgradeType
97          */
98         wp.updates.decrementCount = function( upgradeType ) {
99                 var count,
100                         pluginCount,
101                         $adminBarUpdateCount = $( '#wp-admin-bar-updates .ab-label' ),
102                         $dashboardNavMenuUpdateCount = $( 'a[href="update-core.php"] .update-plugins' ),
103                         $pluginsMenuItem = $( '#menu-plugins' );
104
105
106                 count = $adminBarUpdateCount.text();
107                 count = parseInt( count, 10 ) - 1;
108                 if ( count < 0 || isNaN( count ) ) {
109                         return;
110                 }
111                 $( '#wp-admin-bar-updates .ab-item' ).removeAttr( 'title' );
112                 $adminBarUpdateCount.text( count );
113
114
115                 $dashboardNavMenuUpdateCount.each( function( index, elem ) {
116                         elem.className = elem.className.replace( /count-\d+/, 'count-' + count );
117                 } );
118                 $dashboardNavMenuUpdateCount.removeAttr( 'title' );
119                 $dashboardNavMenuUpdateCount.find( '.update-count' ).text( count );
120
121                 if ( 'plugin' === upgradeType ) {
122                         pluginCount = $pluginsMenuItem.find( '.plugin-count' ).eq(0).text();
123                         pluginCount = parseInt( pluginCount, 10 ) - 1;
124                         if ( pluginCount < 0 || isNaN( pluginCount ) ) {
125                                 return;
126                         }
127                         $pluginsMenuItem.find( '.plugin-count' ).text( pluginCount );
128                         $pluginsMenuItem.find( '.update-plugins' ).each( function( index, elem ) {
129                                 elem.className = elem.className.replace( /count-\d+/, 'count-' + pluginCount );
130                         } );
131
132                         if (pluginCount > 0 ) {
133                                 $( '.subsubsub .upgrade .count' ).text( '(' + pluginCount + ')' );
134                         } else {
135                                 $( '.subsubsub .upgrade' ).remove();
136                         }
137                 }
138         };
139
140         /**
141          * Send an Ajax request to the server to update a plugin.
142          *
143          * @since 4.2.0
144          *
145          * @param {string} plugin
146          * @param {string} slug
147          */
148         wp.updates.updatePlugin = function( plugin, slug ) {
149                 var $message, name,
150                         $card = $( '.plugin-card-' + slug );
151
152                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
153                         $message = $( '[data-slug="' + slug + '"]' ).next().find( '.update-message' );
154                 } else if ( 'plugin-install' === pagenow ) {
155                         $message = $card.find( '.update-now' );
156                         name = $message.data( 'name' );
157                         $message.attr( 'aria-label', wp.updates.l10n.updatingLabel.replace( '%s', name ) );
158                         // Remove previous error messages, if any.
159                         $card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove();
160                 }
161
162                 $message.addClass( 'updating-message' );
163                 if ( $message.html() !== wp.updates.l10n.updating ){
164                         $message.data( 'originaltext', $message.html() );
165                 }
166
167                 $message.text( wp.updates.l10n.updating );
168                 wp.a11y.speak( wp.updates.l10n.updatingMsg );
169
170                 if ( wp.updates.updateLock ) {
171                         wp.updates.updateQueue.push( {
172                                 type: 'update-plugin',
173                                 data: {
174                                         plugin: plugin,
175                                         slug: slug
176                                 }
177                         } );
178                         return;
179                 }
180
181                 wp.updates.updateLock = true;
182
183                 var data = {
184                         _ajax_nonce:     wp.updates.ajaxNonce,
185                         plugin:          plugin,
186                         slug:            slug,
187                         username:        wp.updates.filesystemCredentials.ftp.username,
188                         password:        wp.updates.filesystemCredentials.ftp.password,
189                         hostname:        wp.updates.filesystemCredentials.ftp.hostname,
190                         connection_type: wp.updates.filesystemCredentials.ftp.connectionType,
191                         public_key:      wp.updates.filesystemCredentials.ssh.publicKey,
192                         private_key:     wp.updates.filesystemCredentials.ssh.privateKey
193                 };
194
195                 wp.ajax.post( 'update-plugin', data )
196                         .done( wp.updates.updateSuccess )
197                         .fail( wp.updates.updateError );
198         };
199
200         /**
201          * On a successful plugin update, update the UI with the result.
202          *
203          * @since 4.2.0
204          *
205          * @param {object} response
206          */
207         wp.updates.updateSuccess = function( response ) {
208                 var $updateMessage, name, $pluginRow, newText;
209                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
210                         $pluginRow = $( '[data-slug="' + response.slug + '"]' ).first();
211                         $updateMessage = $pluginRow.next().find( '.update-message' );
212                         $pluginRow.addClass( 'updated' ).removeClass( 'update' );
213
214                         // Update the version number in the row.
215                         newText = $pluginRow.find('.plugin-version-author-uri').html().replace( response.oldVersion, response.newVersion );
216                         $pluginRow.find('.plugin-version-author-uri').html( newText );
217
218                         // Add updated class to update message parent tr
219                         $pluginRow.next().addClass( 'updated' );
220                 } else if ( 'plugin-install' === pagenow ) {
221                         $updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' );
222                         $updateMessage.addClass( 'button-disabled' );
223                         name = $updateMessage.data( 'name' );
224                         $updateMessage.attr( 'aria-label', wp.updates.l10n.updatedLabel.replace( '%s', name ) );
225                 }
226
227                 $updateMessage.removeClass( 'updating-message' ).addClass( 'updated-message' );
228                 $updateMessage.text( wp.updates.l10n.updated );
229                 wp.a11y.speak( wp.updates.l10n.updatedMsg );
230
231                 wp.updates.decrementCount( 'plugin' );
232
233                 wp.updates.updateDoneSuccessfully = true;
234
235                 /*
236                  * The lock can be released since the update was successful,
237                  * and any other updates can commence.
238                  */
239                 wp.updates.updateLock = false;
240
241                 $(document).trigger( 'wp-plugin-update-success', response );
242
243                 wp.updates.queueChecker();
244         };
245
246
247         /**
248          * On a plugin update error, update the UI appropriately.
249          *
250          * @since 4.2.0
251          *
252          * @param {object} response
253          */
254         wp.updates.updateError = function( response ) {
255                 var $card = $( '.plugin-card-' + response.slug ),
256                         $message,
257                         $button,
258                         name,
259                         error_message;
260
261                 wp.updates.updateDoneSuccessfully = false;
262
263                 if ( response.errorCode && response.errorCode == 'unable_to_connect_to_filesystem' && wp.updates.shouldRequestFilesystemCredentials ) {
264                         wp.updates.credentialError( response, 'update-plugin' );
265                         return;
266                 }
267
268                 error_message = wp.updates.l10n.updateFailed.replace( '%s', response.error );
269
270                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
271                         $message = $( '[data-slug="' + response.slug + '"]' ).next().find( '.update-message' );
272                         $message.html( error_message ).removeClass( 'updating-message' );
273                 } else if ( 'plugin-install' === pagenow ) {
274                         $button = $card.find( '.update-now' );
275                         name = $button.data( 'name' );
276
277                         $card
278                                 .addClass( 'plugin-card-update-failed' )
279                                 .append( '<div class="notice notice-error is-dismissible"><p>' + error_message + '</p></div>' );
280
281                         $button
282                                 .attr( 'aria-label', wp.updates.l10n.updateFailedLabel.replace( '%s', name ) )
283                                 .html( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' );
284
285                         $card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
286                                 // Use same delay as the total duration of the notice fadeTo + slideUp animation.
287                                 setTimeout( function() {
288                                         $card
289                                                 .removeClass( 'plugin-card-update-failed' )
290                                                 .find( '.column-name a' ).focus();
291                                 }, 200 );
292                         });
293                 }
294
295                 wp.a11y.speak( error_message, 'assertive' );
296
297                 /*
298                  * The lock can be released since this failure was
299                  * after the credentials form.
300                  */
301                 wp.updates.updateLock = false;
302
303                 $(document).trigger( 'wp-plugin-update-error', response );
304
305                 wp.updates.queueChecker();
306         };
307
308         /**
309          * Show an error message in the request for credentials form.
310          *
311          * @param {string} message
312          * @since 4.2.0
313          */
314         wp.updates.showErrorInCredentialsForm = function( message ) {
315                 var $modal = $( '.notification-dialog' );
316
317                 // Remove any existing error.
318                 $modal.find( '.error' ).remove();
319
320                 $modal.find( 'h3' ).after( '<div class="error">' + message + '</div>' );
321         };
322
323         /**
324          * Events that need to happen when there is a credential error
325          *
326          * @since 4.2.0
327          */
328         wp.updates.credentialError = function( response, type ) {
329                 wp.updates.updateQueue.push( {
330                         'type': type,
331                         'data': {
332                                 // Not cool that we're depending on response for this data.
333                                 // This would feel more whole in a view all tied together.
334                                 plugin: response.plugin,
335                                 slug: response.slug
336                         }
337                 } );
338                 wp.updates.showErrorInCredentialsForm( response.error );
339                 wp.updates.requestFilesystemCredentials();
340         };
341
342         /**
343          * If an update job has been placed in the queue, queueChecker pulls it out and runs it.
344          *
345          * @since 4.2.0
346          */
347         wp.updates.queueChecker = function() {
348                 if ( wp.updates.updateLock || wp.updates.updateQueue.length <= 0 ) {
349                         return;
350                 }
351
352                 var job = wp.updates.updateQueue.shift();
353
354                 wp.updates.updatePlugin( job.data.plugin, job.data.slug );
355         };
356
357
358         /**
359          * Request the users filesystem credentials if we don't have them already.
360          *
361          * @since 4.2.0
362          */
363         wp.updates.requestFilesystemCredentials = function( event ) {
364                 if ( wp.updates.updateDoneSuccessfully === false ) {
365                         /*
366                          * For the plugin install screen, return the focus to the install button
367                          * after exiting the credentials request modal.
368                          */
369                         if ( 'plugin-install' === pagenow && event ) {
370                                 wp.updates.$elToReturnFocusToFromCredentialsModal = $( event.target );
371                         }
372
373                         wp.updates.updateLock = true;
374
375                         wp.updates.requestForCredentialsModalOpen();
376                 }
377         };
378
379         /**
380          * Keydown handler for the request for credentials modal.
381          *
382          * Close the modal when the escape key is pressed.
383          * Constrain keyboard navigation to inside the modal.
384          *
385          * @since 4.2.0
386          */
387         wp.updates.keydown = function( event ) {
388                 if ( 27 === event.keyCode ) {
389                         wp.updates.requestForCredentialsModalCancel();
390                 } else if ( 9 === event.keyCode ) {
391                         // #upgrade button must always be the last focusable element in the dialog.
392                         if ( event.target.id === 'upgrade' && ! event.shiftKey ) {
393                                 $( '#hostname' ).focus();
394                                 event.preventDefault();
395                         } else if ( event.target.id === 'hostname' && event.shiftKey ) {
396                                 $( '#upgrade' ).focus();
397                                 event.preventDefault();
398                         }
399                 }
400         };
401
402         /**
403          * Open the request for credentials modal.
404          *
405          * @since 4.2.0
406          */
407         wp.updates.requestForCredentialsModalOpen = function() {
408                 var $modal = $( '#request-filesystem-credentials-dialog' );
409                 $( 'body' ).addClass( 'modal-open' );
410                 $modal.show();
411
412                 $modal.find( 'input:enabled:first' ).focus();
413                 $modal.keydown( wp.updates.keydown );
414         };
415
416         /**
417          * Close the request for credentials modal.
418          *
419          * @since 4.2.0
420          */
421         wp.updates.requestForCredentialsModalClose = function() {
422                 $( '#request-filesystem-credentials-dialog' ).hide();
423                 $( 'body' ).removeClass( 'modal-open' );
424                 wp.updates.$elToReturnFocusToFromCredentialsModal.focus();
425         };
426
427         /**
428          * The steps that need to happen when the modal is canceled out
429          *
430          * @since 4.2.0
431          */
432         wp.updates.requestForCredentialsModalCancel = function() {
433                 // no updateLock and no updateQueue means we already have cleared things up
434                 var slug, $message;
435
436                 if( wp.updates.updateLock === false && wp.updates.updateQueue.length === 0 ){
437                         return;
438                 }
439
440                 slug = wp.updates.updateQueue[0].data.slug,
441
442                 // remove the lock, and clear the queue
443                 wp.updates.updateLock = false;
444                 wp.updates.updateQueue = [];
445
446                 wp.updates.requestForCredentialsModalClose();
447                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
448                         $message = $( '[data-slug="' + slug + '"]' ).next().find( '.update-message' );
449                 } else if ( 'plugin-install' === pagenow ) {
450                         $message = $( '.plugin-card-' + slug ).find( '.update-now' );
451                 }
452
453                 $message.removeClass( 'updating-message' );
454                 $message.html( $message.data( 'originaltext' ) );
455                 wp.a11y.speak( wp.updates.l10n.updateCancel );
456         };
457         /**
458          * Potentially add an AYS to a user attempting to leave the page
459          *
460          * If an update is on-going and a user attempts to leave the page,
461          * open an "Are you sure?" alert.
462          *
463          * @since 4.2.0
464          */
465
466         wp.updates.beforeunload = function() {
467                 if ( wp.updates.updateLock ) {
468                         return wp.updates.l10n.beforeunload;
469                 }
470         };
471
472
473         $( document ).ready( function() {
474                 /*
475                  * Check whether a user needs to submit filesystem credentials based on whether
476                  * the form was output on the page server-side.
477                  *
478                  * @see {wp_print_request_filesystem_credentials_modal() in PHP}
479                  */
480                 wp.updates.shouldRequestFilesystemCredentials = ( $( '#request-filesystem-credentials-dialog' ).length <= 0 ) ? false : true;
481
482                 // File system credentials form submit noop-er / handler.
483                 $( '#request-filesystem-credentials-dialog form' ).on( 'submit', function() {
484                         // Persist the credentials input by the user for the duration of the page load.
485                         wp.updates.filesystemCredentials.ftp.hostname = $('#hostname').val();
486                         wp.updates.filesystemCredentials.ftp.username = $('#username').val();
487                         wp.updates.filesystemCredentials.ftp.password = $('#password').val();
488                         wp.updates.filesystemCredentials.ftp.connectionType = $('input[name="connection_type"]:checked').val();
489                         wp.updates.filesystemCredentials.ssh.publicKey = $('#public_key').val();
490                         wp.updates.filesystemCredentials.ssh.privateKey = $('#private_key').val();
491
492                         wp.updates.requestForCredentialsModalClose();
493
494                         // Unlock and invoke the queue.
495                         wp.updates.updateLock = false;
496                         wp.updates.queueChecker();
497
498                         return false;
499                 });
500
501                 // Close the request credentials modal when
502                 $( '#request-filesystem-credentials-dialog [data-js-action="close"], .notification-dialog-background' ).on( 'click', function() {
503                         wp.updates.requestForCredentialsModalCancel();
504                 });
505
506                 // Hide SSH fields when not selected
507                 $( '#request-filesystem-credentials-dialog input[name="connection_type"]' ).on( 'change', function() {
508                         $( this ).parents( 'form' ).find( '#private_key, #public_key' ).parents( 'label' ).toggle( ( 'ssh' == $( this ).val() ) );
509                 }).change();
510
511                 // Click handler for plugin updates in List Table view.
512                 $( '.plugin-update-tr' ).on( 'click', '.update-link', function( e ) {
513                         e.preventDefault();
514                         if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.updateLock ) {
515                                 wp.updates.requestFilesystemCredentials( e );
516                         }
517                         var updateRow = $( e.target ).parents( '.plugin-update-tr' );
518                         // Return the user to the input box of the plugin's table row after closing the modal.
519                         wp.updates.$elToReturnFocusToFromCredentialsModal = $( '#' + updateRow.data( 'slug' ) ).find( '.check-column input' );
520                         wp.updates.updatePlugin( updateRow.data( 'plugin' ), updateRow.data( 'slug' ) );
521                 } );
522
523                 $( '.plugin-card' ).on( 'click', '.update-now', function( e ) {
524                         e.preventDefault();
525                         var $button = $( e.target );
526
527                         if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.updateLock ) {
528                                 wp.updates.requestFilesystemCredentials( e );
529                         }
530
531                         wp.updates.updatePlugin( $button.data( 'plugin' ), $button.data( 'slug' ) );
532                 } );
533
534                 $( '#plugin_update_from_iframe' ).on( 'click' , function( e ) {
535                         var target,     data;
536
537                         target = window.parent == window ? null : window.parent,
538                         $.support.postMessage = !! window.postMessage;
539
540                         if ( $.support.postMessage === false || target === null || window.parent.location.pathname.indexOf( 'update-core.php' ) !== -1 )
541                                 return;
542
543                         e.preventDefault();
544
545                         data = {
546                                 'action' : 'updatePlugin',
547                                 'slug'   : $(this).data('slug')
548                         };
549
550                         target.postMessage( JSON.stringify( data ), window.location.origin );
551                 });
552
553         } );
554
555         $( window ).on( 'message', function( e ) {
556                 var event = e.originalEvent,
557                         message,
558                         loc = document.location,
559                         expectedOrigin = loc.protocol + '//' + loc.hostname;
560
561                 if ( event.origin !== expectedOrigin ) {
562                         return;
563                 }
564
565                 message = $.parseJSON( event.data );
566
567                 if ( typeof message.action === 'undefined' ) {
568                         return;
569                 }
570
571                 switch (message.action){
572                         case 'decrementUpdateCount' :
573                                 wp.updates.decrementCount( message.upgradeType );
574                                 break;
575                         case 'updatePlugin' :
576                                 tb_remove();
577                                 if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
578                                         // Return the user to the input box of the plugin's table row after closing the modal.
579                                         $( '#' + message.slug ).find( '.check-column input' ).focus();
580                                         // trigger the update
581                                         $( '.plugin-update-tr[data-slug="' + message.slug + '"]' ).find( '.update-link' ).trigger( 'click' );
582                                 } else if ( 'plugin-install' === pagenow ) {
583                                         $( '.plugin-card-' + message.slug ).find( '.column-name a' ).focus();
584                                         $( '.plugin-card-' + message.slug ).find( '[data-slug="' + message.slug + '"]' ).trigger( 'click' );
585                                 }
586                                 break;
587                 }
588
589         } );
590
591         $( window ).on( 'beforeunload', wp.updates.beforeunload );
592
593 })( jQuery, window.wp, window.pagenow, window.ajaxurl );