PluginProbe
UpdraftCentral Dashboard / 0.8.13
UpdraftCentral Dashboard v0.8.13
0.8.33 0.7.2 0.7.3 0.7.4 0.8.0 0.8.1 0.8.10 0.8.11 0.8.12 0.8.13 0.8.14 0.8.15 0.8.16 0.8.17 0.8.18 0.8.19 0.8.2 0.8.20 0.8.21 0.8.22 0.8.23 0.8.24 0.8.25 0.8.26 0.8.27 All 51 releases
updraftcentral / modules / updates / updates.js

updates.js in UpdraftCentral Dashboard 0.8.13, at modules/updates/updates.js

3,141 lines 110.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 jQuery(document).ready(function() {
2 new UpdraftCentral_Updates();
3 });
4
5 /**
6 * Updates Class for Managing Updates
7 *
8 * @constructor
9 */
10 function UpdraftCentral_Updates() {
11 var self = this;
12 var $ = jQuery;
13 var updates_button_text;
14 var settings_css_sub_prefix = '.updraftcentral_row_extracontents';
15 var settings_css_prefix = '#updraftcentral_dashboard_existingsites '+settings_css_sub_prefix;
16
17 var credentials;
18 var sites;
19 var debug_level = UpdraftCentral.get_debug_level();
20 var uc_runners;
21 var cache;
22 var site_runner;
23 var sites_for_backup;
24 var mass_updates_processing;
25 var mass_updates_loading;
26 var queued_items;
27 var selected_sites;
28 var default_items_per_page = 50;
29
30 /**
31 * Initializes Credentials ans Sites objects
32 *
33 * @return {void}
34 */
35 var init = function() {
36 credentials = new UpdraftCentral_Credentials();
37 sites = new UpdraftCentral_Collection();
38 uc_runners = new UpdraftCentral_Collection();
39 cache = new UpdraftCentral_Collection();
40 site_runner = new UpdraftCentral_Tasks_Runner({
41 concurrency: 1
42 });
43 sites_for_backup = new UpdraftCentral_Collection();
44 mass_updates_processing = false;
45 mass_updates_loading = false;
46 queued_items = new UpdraftCentral_Collection();
47 selected_sites = new UpdraftCentral_Collection();
48 }
49 init();
50
51 /**
52 * Highlights the first tab
53 *
54 * @return {void}
55 */
56 var post_modal_open = function() {
57 jQuery('#updraftcentral_modal ul#updates-sections-list li:first').addClass('selected');
58 }
59
60 /**
61 * For any plugins or themes in the passed array of objects, if they are from wordpress.org, use the wordpress.org API to fill in information about them.
62 *
63 * @param {Object} $site_row - the jQuery object for the site
64 * @param {array} objects - an array of plugin or theme update objects
65 * @param {string} type - either 'plugin' or 'theme', according to which type of objects has been passed
66 * @param {Object} $container - An optional jQuery object used primarily for mass update info retrieval
67 *
68 * @return {void}
69 */
70 function fill_wporg_metadata($site_row, objects, type, $container) {
71
72 for (var i=0, j=objects.length; i<j; i++) {
73 var object = objects[i];
74 if (object && object.hasOwnProperty('update') && object.update.hasOwnProperty('package') && object.update.package) {
75 var url = object.update.package;
76 if ('string' != typeof url) { continue; }
77 var is_wp_org = url.match(/^https?:\/\/((downloads|www)\.)?wordpress\.org\//);
78 if (is_wp_org) {
79
80 if ('plugin' == type) {
81 UpdraftCentral.get_wporg_metadata(type, object.update.slug, object, function(data, object) {
82 if (data && data.hasOwnProperty('sections')) {
83
84 var $the_update = $site_row.find('.updraftcentral_row_extracontents .updates-plugin-update.updates-update[data-plugin-file="'+object.update.plugin+'"]');
85 if (mass_updates_loading && typeof $container !== 'undefined') {
86 $the_update = $container.find('.row.update-item[data-plugin-file="'+object.update.plugin+'"][data-website_id="'+$site_row.data('site_id')+'"]');
87 }
88 var existing_data = $the_update.data('plugin-info');
89
90 if (!existing_data) { return; }
91 if (!existing_data.update.hasOwnProperty('sections') || !existing_data.update.sections) { existing_data.update.sections = {} };
92
93 if (data.sections.hasOwnProperty('changelog')) {
94 existing_data.update.sections.changelog = data.sections.changelog;
95 }
96 if (data.sections.hasOwnProperty('description')) {
97 existing_data.update.sections.description = data.sections.description;
98 }
99
100 $the_update.attr('data-plugin-info', JSON.stringify(existing_data));
101 }
102 });
103 } else if ('theme' == type) {
104 UpdraftCentral.get_wporg_metadata(type, object.update.theme, object, function(data, object) {
105 if (data && data.hasOwnProperty('sections')) {
106
107 var $the_update = $site_row.find('.updraftcentral_row_extracontents .updates-theme-update.updates-update[data-theme="'+object.update.theme+'"]');
108 if (mass_updates_loading && typeof $container !== 'undefined') {
109 $the_update = $container.find('.row.update-item[data-theme="'+object.update.theme+'"][data-website_id="'+$site_row.data('site_id')+'"]');
110 }
111 var existing_data = $the_update.data('theme-info');
112
113 if (!existing_data) { return; }
114 if (data.sections.hasOwnProperty('changelog')) {
115 if (!existing_data.update.hasOwnProperty('sections') || !existing_data.update.sections) { existing_data.update.sections = {} };
116 existing_data.update.sections.changelog = data.sections.changelog;
117 }
118
119 $the_update.attr('data-theme-info', JSON.stringify(existing_data));
120 }
121 });
122 }
123
124 }
125 }
126 }
127
128 }
129
130 /**
131 * A helper method used to check if the updraftcentral notice
132 * container is currently empty.
133 *
134 * N.B.: This method was created to fix an issue where the error notices/messages
135 * as a result of the update process are not displayed when after the automatic backup process.
136 *
137 * @param {object} $container - A jQuery object used to represent the notice container
138 * @return {object} - A jQuery promise object
139 */
140 function empty_container($container) {
141 var deferred = jQuery.Deferred();
142
143 var interval = setInterval(function() {
144 if (jQuery.trim($container.html()).length < 1) {
145 clearInterval(interval);
146 deferred.resolve(true);
147 }
148 }, 1000);
149
150 // If for some reason, the container maintains its
151 // content (meaning, it didn't cleared up after a 10 seconds window)
152 // then we're going to force to clear the interval in order not to leave
153 // the promise subscriber(s) hanging.
154 setTimeout(function() {
155 clearInterval(interval);
156 deferred.reject(false);
157 }, 10000);
158
159 return deferred.promise();
160 }
161
162 /**
163 * Reports an error when updating something
164 *
165 * @param {Object} entity - the update object returned from the remote call
166 * @param {Object} $site_row - the jQuery object for the affected site
167 * @param {Object} $update_row - the jQuery object for the relevant update
168 * @param {Object} entity_info - the object with information about the updatable entity
169 *
170 * @return {void}
171 */
172 function update_error(entity, $site_row, $update_row, entity_info) {
173
174 var site_id = $site_row.data('site_id');
175 var site = sites.item(site_id);
176
177 if (debug_level > 0) {
178 console.log("UpdraftCentral: update error (follows)");
179 console.log(entity);
180 }
181
182 $update_row.find('.update-go').show();
183 $update_row.find('.update-in-progress').hide();
184
185 var site_description = site.site_description;
186 var site_url = site.site_url;
187
188 if (site_description == site_url) { site_description = ''; }
189
190 var messages = '';
191 if (entity.hasOwnProperty('messages')) {
192 jQuery(entity.messages).each(function(ind, message) {
193 messages += message+"\n";
194 });
195 }
196
197 if (debug_level > 0) {
198 if ('unable_to_connect_to_filesystem' == entity.error) {
199 console.log("UpdraftCentral: unable_to_connect_to_filesystem: filesystem credentials apparently wrong; will clear");
200 } else {
201 console.log("UpdraftCentral: update failed: will clear filesystem credentials in case they were wrong");
202 }
203 }
204
205 site.update_processing = false;
206 site.site_credentials = false;
207 sites.update(site_id, site);
208
209 var error_message;
210 if (!entity.hasOwnProperty('error_message') || '' == entity.error_message) {
211
212 if (udclion.updates.update_error_messages.hasOwnProperty(entity.error)) {
213 error_message = udclion.updates.update_error_messages[entity.error];
214 } else {
215 error_message = udclion.updates.update_error_messages.update_failed;
216 if (entity.error != 'update_failed') { error_message += ' ('+entity.error+')'; }
217 }
218 } else {
219 error_message = entity.error_message;
220 }
221
222 // If an existing container for this site exists, then we add the information in there, instead of creating a new one.
223 var $notice_container = jQuery('#updraftcentral_notice_container');
224 var $existing_notice = jQuery('#updraftcentral_notice_container > .updraftcentral_notice[data-site_id="'+site_id+'"]:first');
225
226 empty_container($notice_container).then(function() {
227 $existing_notice = UpdraftCentral.add_dashboard_notice(
228 UpdraftCentral.template_replace('updates-update-error-container', {
229 site_url: site_url,
230 site_description: site_description
231 }),
232 'error',
233 false,
234 { site_id: site_id }
235 );
236 }).always(function() {
237 $existing_notice.find('.update-errors').append(
238 UpdraftCentral.template_replace('updates-update-error', {
239 error_message: error_message,
240 error: entity.error,
241 messages: messages,
242 entity_info: entity_info,
243 entity: entity
244 })
245 );
246 });
247
248 }
249
250 /**
251 * Preloads Update Informations of Items that needs updating and
252 * the current Site Credentials respectively.
253 *
254 * @param {object} site - An UpdraftCentral_Site object representing the current site for processing.
255 * @borrows {UpdraftCentral_Library.md5}
256 * @borrows {UpdraftCentral_Credentials.load_credentials}
257 * @return {void}
258 */
259 function load_site_updates(site) {
260
261 // Preparing initial state of updates. This is to ensure
262 // we don't have any phantom entries that will cause some issues
263 // when the show updates button is clicked or is triggered again.
264 site.updates.plugin.clear();
265 site.updates.theme.clear();
266 site.updates.core.clear();
267 site.updates.translation.clear();
268
269 jQuery('.updraftcentral_row_extracontents button.update-go').each(function() {
270 var $container = jQuery(this).closest('.row.update-item');
271 var entity = $container.data('entity');
272 var name = $container.data('name');
273 var info = $container.data(entity + '-info');
274
275 // Bypass check for non translation entity. Since we don't usually send
276 // a request to wordpress.org for the translation info therefore, the below check
277 // is only applicable to those entities/items that are not translation.
278 if ('translation' !== entity) {
279 if ((entity !== 'core' && (!info || !info.hasOwnProperty('update'))) || (entity === 'core' && (!info || !info.hasOwnProperty('version')))) {
280 return;
281 }
282 }
283
284 var is_mysql = true;
285 if (info.hasOwnProperty('installed') && info.installed.hasOwnProperty('is_mysql') && !info.installed.is_mysql) {
286 is_mysql = false;
287 }
288
289 if (info.hasOwnProperty('sufficient')) {
290 var mysql_sufficient = (!is_mysql || info.sufficient.mysql);
291 var php_sufficient = info.sufficient.php;
292 if (!mysql_sufficient) {
293 if (!php_sufficient) {
294 mysql_error = '<h2>'+udclion.updates.cannot_update+'</h2>'+sprintf(udclion.updates.insufficient_php_and_mysql, info.version, info.php_version, info.mysql_version, info.installed.php, info.installed.mysql);
295 } else {
296 mysql_error = '<h2>'+udclion.updates.cannot_update+'</h2>'+sprintf(udclion.updates.insufficient_mysql, info.version, info.mysql_version, info.installed.mysql);
297 }
298 } else if (!php_sufficient) {
299 mysql_error = '<h2>'+udclion.updates.cannot_update+'</h2>'+sprintf(udclion.updates.insufficient_php, info.version, info.php_version, info.installed.php);
300 }
301 }
302
303 if ('undefined' === typeof name || !name || !name.length) {
304 switch (entity) {
305 case 'plugin':
306 case 'theme':
307 name = info.update[entity];
308 break;
309 case 'core':
310 name = udclion.updates.wordpress;
311 break;
312 case 'translation':
313 name = udclion.updates.translations+' ('+site.site_description+')';
314 break;
315 default:
316 break;
317 }
318 }
319
320 if ('undefined' !== typeof name && name && name.length) {
321 var details = {};
322 var errors = false;
323
324 switch (entity) {
325 case 'plugin':
326 details.plugin = info.update.plugin;
327 details.slug = info.update.slug;
328 details.label = info.name;
329 details.name = name;
330 details.old_version = info.version;
331 details.new_version = info.update.new_version;
332
333 if (!details.plugin || !details.plugin.length || !details.slug || !details.slug.length) errors = true;
334 break;
335 case 'theme':
336 details.theme = info.update.theme;
337 details.label = info.name;
338 details.name = name;
339 details.old_version = info.version;
340 details.new_version = info.update.new_version;
341
342 if (!details.theme || !details.theme.length) errors = true;
343 break;
344 case 'core':
345 if (typeof mysql_error !== 'undefined') {
346 details.mysql_error = mysql_error;
347 }
348 details.core = info.version;
349 details.label = name;
350 details.name = name;
351 details.old_version = info.installed.version;
352 details.new_version = info.version;
353
354 if (!details.core || !details.core.length) errors = true;
355 break;
356 case 'translation':
357 details.translation = 1;
358 details.label = udclion.updates.translations;
359 details.name = name;
360 break;
361 default:
362 break;
363 }
364
365 if (!errors && details && details.hasOwnProperty('name') && details.name) {
366 var key = UpdraftCentral_Library.md5(site.id+'_'+details.name);
367 details.entity = entity;
368 site.updates[entity].add(key, details);
369 } else {
370 if (debug_level > 0) {
371 console.log("UpdraftCentral: load_site_updates(): update ("+entity+") without expected info (details follow)");
372 console.log(details);
373 }
374 }
375 }
376 });
377
378 // Loads Remote Site Credentials
379 credentials.load_credentials(site).then(function(response) {
380 var requests = response.request_filesystem_credentials;
381 var credentials_required = false;
382
383 for (var entity in requests) {
384 if (requests[entity]) {
385 credentials_required = true;
386 }
387 }
388
389 // Update site information from sites collection
390 site.credentials_required = credentials_required;
391 sites.update(site.id, site);
392
393 if (debug_level > 0) {
394 console.log("UpdraftCentral: remote site credentials has been loaded");
395 }
396 });
397 }
398
399 /**
400 * Processes automatic backup before doing the update(s)
401 *
402 * @param {object} site - An UpdraftCentral_Site object containing the current site
403 * information needed by the process.
404 * @param {object} progress - An UpdraftCentral_Task_Progress object from the current task runner instance
405 * @borrows {UpdraftCentral_Module_UpdraftPlus.backupnow_go}
406 * @return {void}
407 */
408 function process_auto_backup(site, progress) {
409
410 // Sets current backup status
411 if (mass_updates_processing) {
412 progress.set_custom_status(udclion.updates.backup_currently_running_for + ' ' + site.site_description);
413 } else {
414 progress.set_custom_status(udclion.updates.backup_currently_running);
415 }
416
417 var autobackup_requested = site.hasOwnProperty('autobackup_requested') && site.autobackup_requested;
418 if (autobackup_requested) {
419 var autobackup_complete = site.hasOwnProperty('autobackup_complete') && site.autobackup_complete;
420 if (!autobackup_complete) {
421 if (debug_level > 0) {
422 console.log("UpdraftCentral: automatic pre-upgrade backup was already requested, but is not yet complete");
423 }
424 } else {
425 if (debug_level > 0) {
426 console.log("UpdraftCentral: automatic pre-upgrade backup was already requested, and is complete");
427 }
428 }
429 } else {
430 if (debug_level > 0) {
431 console.log("UpdraftCentral: automatic pre-upgrade backup indicated - will request");
432 }
433
434 // Add backup properties/flag
435 site.autobackup_requested = true;
436 site.autobackup_complete = false;
437
438 // Update site information from sites collection
439 sites.update(site.id, site);
440
441 // Check and prepare what to backup
442 var only_these_file_entities = '';
443 if (site.updates.plugin.count()) {
444 only_these_file_entities = 'plugins';
445 }
446
447 if (site.updates.theme.count()) {
448 only_these_file_entities = (only_these_file_entities) ? only_these_file_entities + ',themes' : 'themes';
449 }
450
451 if (site.updates.core.count()) {
452 only_these_file_entities = (only_these_file_entities) ? only_these_file_entities + ',wpcore' : 'wpcore';
453 }
454
455 extra_data = {
456 _listener_title: '<h2>' + site.site_description + ' - ' + udclion.updates.automatic_backup + '</h2>'
457 }
458
459 // Setting the current site row manually, as it will
460 // be called by the backup process.
461 UpdraftCentral.set_current_site_row(site.site_row);
462
463 UpdraftCentral_Module_UpdraftPlus.backupnow_go(false, false, false, only_these_file_entities, extra_data, udclion.updates.automatic_backup);
464 }
465 }
466
467 /**
468 * Function called by the task runner for processing an item for update/upgrade
469 *
470 * N.B.: Not to be called directly but by the UpdraftCentral_Tasks_Runner instance, since we're using
471 * the d3 queue library to run each tasks (for a site-wide updates).
472 *
473 * @param {object} update_info - The update information to be sent to the server for processing
474 * @param {object} site - An UpdraftCentral_Site instance containing information of the current Site to process.
475 * @param {object} uc_runner - An UpdraftCentral_Tasks_Runner instance primarily used as a context for the current process.
476 * It is automatically added by the UpdraftCentral_Tasks_Runner class.
477 * @return {object} - A jQuery promise object
478 */
479 function process_update_info(update_info, site, uc_runner) {
480 var deferred = jQuery.Deferred();
481
482 site.additional_options = {
483 progress_bar: uc_runner.progress,
484 update_info: update_info,
485 items_count: uc_runner.tasks_count()
486 }
487
488 run_process(site).then(function(result) {
489 deferred.resolve(result);
490 }).fail(function(result) {
491 deferred.reject(result);
492 });
493
494 return deferred.promise();
495 }
496
497 /**
498 * Runs the process of updating/upgrading an item
499 *
500 * @param {object} site - An UpdraftCentral_Site instance representing the current Site to process.
501 * @uses {UpdraftCentral_Updates#send_remote_updates}
502 * @return {object} - A jQuery promise object
503 */
504 function run_process(site) {
505 var deferred = jQuery.Deferred();
506 var progress_bar = site.additional_options.progress_bar;
507 var update_info = site.additional_options.update_info;
508
509 // Set the current label of the currently processed item.
510 // It uses the following display format: "Processing {label} ..."
511 // If you want to use your own label, you can set the second argument "use_custom"
512 // to true, and anything you put as a label will use instead.
513
514 var label = ('undefined' !== typeof update_info.translation && update_info.translation) ? update_info.label : update_info.name;
515 if (mass_updates_processing) {
516 progress_bar.set_current_label(label + ' (' + site.site_description + ')');
517 } else {
518 progress_bar.set_current_label(label);
519 }
520
521 // We make sure that we will only continue if we have the
522 // the needed credentials to proceed.
523 //
524 // NOTE: If we don't have a valid credentials yet, the credential form
525 // will keep popping until we extract a valid creds to be used during the
526 // update process. Allowing the user to enter the correct credentials if he or she
527 // misses it the first time.
528 if (!site.site_credentials && site.credentials_required) {
529
530 // Updates the site.site_credentials with the actual credentials
531 // for the current site
532 credentials.get_credentials(site).then(function(response) {
533
534 // If we now have a valid credentials, we copy the relevant fields/flags
535 // to the current site before we proceed with the process.
536 site.site_credentials = response.site_credentials;
537 site.save_credentials_in_browser = response.save_credentials_in_browser;
538
539 // Re-run updates now that we already have a valid credentials to use
540 run_process(site).then(function(result) {
541 deferred.resolve(result);
542 }).fail(function(result) {
543 if (mass_updates_processing) {
544 // We need to return this area as "resolve"
545 // but with a "skip" response to true, so that the overall process can continue with the
546 // rest of the websites currently being queued. Otherwise, the entire process will be aborted
547 // when 1 website fails (D3 Queue Library feature).
548 deferred.resolve({
549 skip: true,
550 response: udclion.failed_credentials,
551 site: site.site_description
552 });
553 } else {
554 deferred.reject(result);
555 }
556 });
557
558 }).fail(function(response) {
559 // We need to return this area as "resolve"
560 // but with a "skip" response to true, so that the overall process can continue with the
561 // rest of the websites currently being queued. Otherwise, the entire process will be aborted
562 // when 1 website fails (D3 Queue Library feature).
563 deferred.resolve({
564 skip: true,
565 response: udclion.failed_credentials,
566 site: site.site_description
567 });
568 });
569
570 } else {
571
572 // Starts the progress bar routine.
573 progress_bar.start();
574
575 // Send Update Request to the Server:
576 send_remote_updates(site, update_info).then(function(result) {
577
578 // Calling the update method of the progress bar will signal
579 // that the current item being processed has been completed, thus,
580 // re-evaluating and re-calculating the actual progress percentage.
581 progress_bar.update();
582
583
584 deferred.resolve(result);
585
586 }).fail(function(result) {
587
588 progress_bar.update();
589
590 // Remove failed update from task queues so that we can re-add them
591 // again if need be.
592 var key = UpdraftCentral_Library.md5(site.id+'_'+update_info.name);
593 var queue_info = site.update_queue.item(key);
594
595 if ('undefined' !== typeof queue_info && queue_info && queue_info.hasOwnProperty('key')) {
596 site_runner.remove_task(queue_info.key);
597 queued_items.remove(key);
598 site.update_queue.remove(key);
599 }
600
601 // We're letting the whole process to continue even if an item failed to update, so that
602 // the rest of the items will proceed as scheduled.
603 deferred.resolve({
604 skip: true,
605 item: true,
606 response: update_info,
607 site: site.site_description
608 });
609
610 });
611 }
612
613 return deferred.promise();
614 }
615
616 /**
617 * Checks to see if the failed update respond got something to do with the item
618 * being updated is already up to date.
619 *
620 * @param {string} code The error code
621 * @param {string|array} message The messages return describing the error(s) encountered
622 * @param {object} versions Holds the current (old) and latest versions of the item being updated
623 *
624 * @return {boolean}
625 */
626 function is_latest_version(code, message, versions) {
627 if ('up_to_date' == code) return true;
628 if ('undefined' !== typeof versions) {
629 if (versions.latest == versions.old) return true;
630 }
631
632 // Normally, the "up_to_date" code check above will suffice, but in case
633 // it gets pass through this line then we will check the return message
634 // whether it contains the "latest version" or "up to date" strings.
635 if ('undefined' !== typeof message && message) {
636 var latest_version = udclion.updates.latest_version;
637 if ('undefined' !== typeof latest_version && latest_version && latest_version.length) {
638 latest_version = latest_version.toLowerCase();
639 }
640
641 var up_to_date = udclion.updates.up_to_date;
642 if ('undefined' !== typeof up_to_date && up_to_date && up_to_date.length) {
643 up_to_date = up_to_date.toLowerCase();
644 }
645
646 // Make sure that the submitted message contains information we can use,
647 // meaning, it is not empty.
648 if (message.length) {
649 if ($.isArray(message)) {
650 var msgArr = message.filter(function (value, index, arr) {
651 if ('string' !== typeof value) value += '';
652 value = value.toLowerCase();
653 return ('update_failed' == code && -1 !== value.indexOf(latest_version) || -1 !== value.indexOf(up_to_date));
654 });
655
656 if (msgArr.length) {
657 return true;
658 }
659 } else {
660 if ('string' !== typeof message) message += '';
661 message = message.toLowerCase();
662 if ('update_failed' == code && (-1 !== message.indexOf(latest_version) || -1 !== message.indexOf(up_to_date))) {
663 return true;
664 }
665 }
666 }
667 }
668
669 return false;
670 }
671
672 /**
673 * Sends Update Request to the Remote Server
674 *
675 * @param {object} site - An UpdraftCentral_Site object containing the current site
676 * information needed by the process.
677 * @see {UpdraftCentral_Site}
678 * @borrows {UpdraftCentral.send_site_rpc}
679 * @borrows {UpdraftCentral.storage_set}
680 * @borrows {UpdraftCentral_Updates.update_error}
681 *
682 * @return {object} - A jQuery promise object
683 */
684 function send_remote_updates(site, update_info) {
685 var deferred = jQuery.Deferred();
686 var $site_row = site.site_row;
687 var uc_updates = {};
688 var $container = jQuery('#updates_container div.updraftcentral_table > .tbody');
689 var count = site.additional_options.items_count,
690 default_timeout = 90,
691 max_timeout = 400,
692 timeout = count * default_timeout;
693
694 // We need to limit or set a maximum timeout in place,
695 // otherwise, the timeout result from the computation above
696 // will go beyond the desired timeout that we intended to
697 // especially when the user has around 50 or 100+ updates to run.
698 //
699 // You can adjust the above default_timeout and max_timeout to your
700 // preferrable values whenever it is applicable.
701 if (timeout > max_timeout) timeout = max_timeout;
702
703 // Make sure to remove the mysql_error field
704 // before sending the request to the server. This field
705 // isn't needed for the update request.
706 if (typeof update_info.mysql_error !== 'undefined') {
707 delete update_info['mysql_error'];
708 }
709
710 switch (update_info.entity) {
711 case 'plugin':
712 uc_updates.plugins = [{
713 plugin: update_info.plugin,
714 slug: update_info.slug
715 }];
716 break;
717 case 'theme':
718 uc_updates.themes = [{
719 theme: update_info.theme
720 }];
721 break;
722 case 'core':
723 uc_updates.core = [{
724 core: update_info.core
725 }];
726 timeout += default_timeout * 5;
727 break;
728 case 'translation':
729 uc_updates.translations = [{
730 translation: update_info.translation
731 }];
732 break;
733 default:
734 break;
735 }
736
737 if (site.site_credentials) {
738 uc_updates.meta = {
739 filesystem_credentials: site.site_credentials
740 }
741 }
742
743 UpdraftCentral.send_site_rpc('updates.do_updates', uc_updates, $site_row, function(response, code, error_code) {
744
745 if ('ok' == code && response) {
746 var any_successes = false;
747 var any_errors = false;
748
749 jQuery(response.data.plugins).each(function(index, plugin) {
750
751 var plugin_file = plugin.plugin;
752 var $update_row = $site_row.find('.updraft_updates_output .updates-plugin-update[data-plugin-file="'+plugin_file+'"]');
753 if (mass_updates_processing) {
754 $update_row = $container.find('.row.update-item.updates-plugin-update[data-plugin-file="'+plugin_file+'"][data-website_id="' + site.id + '"]');
755 }
756 var entity_info = $update_row.data('plugin-info');
757 var latest_version = $update_row.data('new_version');
758
759 // Making sure that we don't treat the up to date status as error by
760 // checking the return error code for the "up_to_date" value and the requested
761 // new version is not equal to the item's old version which signifies that its
762 // already at its latest, thus, the reason why the request failed.
763 var versions = { latest: latest_version, old: plugin.oldVersion };
764 if (plugin.hasOwnProperty('error') && !is_latest_version(plugin.error, plugin.messages, versions)) {
765 if (debug_level > 0) {
766 console.log('send_remote_updates (updates.do_updates): error_code='+plugin.error+',latest_version='+latest_version+',old_version='+plugin.oldVersion);
767 console.log('send_remote_updates (failed update): response object (follows):');
768 console.log(plugin);
769 }
770 any_errors = true;
771 update_error(plugin, $site_row, $update_row, entity_info);
772 } else {
773 any_successes = true;
774 if (debug_level > 0 && plugin.hasOwnProperty('messages')) {
775 console.log(plugin.messages)
776 }
777 $update_row.slideUp('slow', function() {
778 $update_row.remove();
779 });
780 }
781
782 });
783
784 jQuery(response.data.themes).each(function(index, theme) {
785 var theme_slug = theme.theme;
786 var $update_row = $site_row.find('.updraft_updates_output .updates-theme-update[data-theme="'+theme_slug+'"]');
787 if (mass_updates_processing) {
788 $update_row = $container.find('.row.update-item.updates-theme-update[data-theme="'+theme_slug+'"][data-website_id="' + site.id + '"]');
789 }
790 var entity_info = $update_row.data('theme-info');
791 var latest_version = $update_row.data('new_version');
792
793 // Making sure that we don't treat the up to date status as error by
794 // checking the return error code for the "up_to_date" value and the requested
795 // new version is not equal to the item's old version which signifies that its
796 // already at its latest, thus, the reason why the request failed.
797 var versions = { latest: latest_version, old: theme.oldVersion };
798 if (theme.hasOwnProperty('error') && !is_latest_version(theme.error, theme.messages, versions)) {
799 if (debug_level > 0) {
800 console.log('send_remote_updates (updates.do_updates): error_code='+theme.error+',latest_version='+latest_version+',old_version='+theme.oldVersion);
801 console.log('send_remote_updates (failed update): response object (follows):');
802 console.log(theme);
803 }
804 any_errors = true;
805 update_error(theme, $site_row, $update_row, entity_info);
806 } else {
807 any_successes = true;
808 if (debug_level > 0 && theme.hasOwnProperty('messages')) {
809 console.log(theme.messages)
810 }
811 $update_row.slideUp('slow', function() {
812 $update_row.remove();
813 });
814 }
815 });
816
817 jQuery(response.data.core).each(function(index, core) {
818 var $update_row = $site_row.find('.updraft_updates_output .updates-core-update');
819 if (mass_updates_processing) {
820 $update_row = $container.find('.row.update-item.updates-core-update[data-website_id="' + site.id + '"]');
821 }
822 var entity_info = $update_row.data('core-info');
823 var latest_version = $update_row.data('new_version');
824 entity_info.name = udclion.updates.wordpress;
825
826 // Making sure that we don't treat the up to date status as error by
827 // checking the return error code for the "up_to_date" value and the requested
828 // new version is not equal to the item's old version which signifies that it's
829 // already at its latest, thus, the reason why the request failed.
830 var versions = { latest: latest_version, old: core.oldVersion };
831 if (core.hasOwnProperty('error') && !is_latest_version(core.error, core.messages, versions)) {
832 if (debug_level > 0) {
833 console.log('send_remote_updates (updates.do_updates): error_code='+core.error+',latest_version='+latest_version+',old_version='+core.oldVersion);
834 console.log('send_remote_updates (failed update): response object (follows):');
835 console.log(core);
836 }
837 any_errors = true;
838 update_error(core, $site_row, $update_row, entity_info);
839 } else {
840 any_successes = true;
841 if (debug_level > 0 && core.hasOwnProperty('messages')) {
842 console.log(core.messages)
843 }
844 $update_row.slideUp('slow', function() {
845 $update_row.remove();
846 });
847 }
848 });
849
850 jQuery(response.data.translations).each(function(index, translation) {
851 var $update_row = $site_row.find('.updraft_updates_output .updates-translation-update[data-website_id="'+site.id+'"]');
852 if (mass_updates_processing) {
853 $update_row = $container.find('.row.update-item.updates-translation-update[data-website_id="'+site.id+'"]');
854 }
855 var entity_info = $update_row.data('translation-info');
856
857 // N.B. Checking the latest version against the old version the ones applied to
858 // plugins, themes and core (in the logic above) is not applicable to the
859 // "translation" updates since a translation update request for a certain site will
860 // update all available translations it can find in one go and we don't received
861 // individual translations status for each translation being updated. Checking
862 // the return error code for the 'up_to_date' field value will suffice in this case.
863 if (translation.hasOwnProperty('error') && !is_latest_version(translation.error, translation.messages)) {
864 if (debug_level > 0) {
865 console.log('send_remote_updates (updates.do_updates): error_code='+translation.error);
866 console.log('send_remote_updates (failed update): response object (follows):');
867 console.log(translation);
868 }
869 any_errors = true;
870 update_error(translation, $site_row, $update_row, entity_info);
871 } else {
872 any_successes = true;
873 if (debug_level > 0 && translation.hasOwnProperty('messages')) {
874 console.log(translation.messages)
875 }
876 $update_row.slideUp('slow', function() {
877 $update_row.remove();
878 });
879 }
880 });
881
882 if (any_successes && !any_errors && site.save_credentials_in_browser) {
883 UpdraftCentral.storage_set('filesystem_credentials_'+site.site_hash, site.site_credentials, true);
884 }
885
886 // We're making sure that we're returning the promise object in
887 // order not to leave the subscriber of this promise object hanging, thus, allowing
888 // it to continue with other pending process.
889 if (any_errors) {
890 deferred.reject(response);
891 } else {
892 deferred.resolve(response);
893 }
894
895 } else {
896 // Error: still want to reset UI state
897 var errors = [];
898
899 if (uc_updates.hasOwnProperty('plugins')) {
900 jQuery(uc_updates.plugins).each(function(index, plugin) {
901 var plugin_file = plugin.plugin;
902 var $update_row = $site_row.find('.updraft_updates_output .updates-plugin-update[data-plugin-file="'+plugin_file+'"]');
903 if (mass_updates_processing) {
904 $update_row = $container.find('.row.update-item.updates-plugin-update[data-plugin-file="'+plugin_file+'"][data-website_id="' + site.id + '"]');
905 }
906 var entity_info = $update_row.data('plugin-info');
907 plugin.error = 'update_failed';
908 update_error(plugin, $site_row, $update_row, entity_info);
909 errors.push({
910 entity_info: entity_info,
911 error: plugin.error
912 });
913 });
914 }
915
916 if (uc_updates.hasOwnProperty('themes')) {
917 jQuery(uc_updates.themes).each(function(index, theme) {
918 var theme_slug = theme.theme;
919 var $update_row = $site_row.find('.updraft_updates_output .updates-theme-update[data-theme="'+theme_slug+'"]');
920 if (mass_updates_processing) {
921 $update_row = $container.find('.row.update-item.updates-theme-update[data-theme="'+theme_slug+'"][data-website_id="' + site.id + '"]');
922 }
923 var entity_info = $update_row.data('theme-info');
924 theme.error = 'update_failed';
925 update_error(theme, $site_row, $update_row, entity_info);
926 errors.push({
927 entity_info: entity_info,
928 error: theme.error
929 });
930 });
931 }
932
933 if (uc_updates.hasOwnProperty('core')) {
934 jQuery(uc_updates.core).each(function(index, core) {
935 var $update_row = $site_row.find('.updraft_updates_output .updates-core-update');
936 if (mass_updates_processing) {
937 $update_row = $container.find('.row.update-item.updates-core-update[data-website_id="' + site.id + '"]');
938 }
939 var entity_info = $update_row.data('core-info');
940 entity_info.name = udclion.updates.wordpress;
941 core.error = 'update_failed';
942 update_error(core, $site_row, $update_row, entity_info);
943 errors.push({
944 entity_info: entity_info,
945 error: core.error
946 });
947 });
948 }
949
950 if (uc_updates.hasOwnProperty('translations')) {
951 jQuery(uc_updates.translations).each(function(index, translation) {
952 var $update_row = $site_row.find('.updraft_updates_output .updates-translation-update[data-website_id="'+site.id+'"]');
953 if (mass_updates_processing) {
954 $update_row = $container.find('.row.update-item.updates-translation-update[data-website_id="'+site.id+'"]');
955 }
956 var entity_info = $update_row.data('translation-info');
957 translation.error = 'update_failed';
958 update_error(translation, $site_row, $update_row, entity_info);
959 errors.push({
960 entity_info: entity_info,
961 error: translation.error
962 });
963 });
964 }
965
966 // Return errors
967 deferred.reject(errors);
968 }
969 }, null, timeout);
970
971 return deferred.promise();
972 }
973
974 /**
975 * Updates the process button state (proxy button)
976 *
977 * @param {object} uc_runner - An UpdraftCentral_Tasks_Runner instance of the current request.
978 * @param {boolean} processing - An optional flag that indicates that the update process is currently running. Thus, changing the button's state accordingly for the processing purpose.
979 *
980 * @return {void}
981 */
982 function update_button_state(uc_runner, processing) {
983 var btn_update_all = jQuery('.updraftcentral_row_extracontents button#btn-update-all');
984 button_state_update(btn_update_all, uc_runner, processing);
985 }
986
987 /**
988 * Updates the process button state for mass update process (proxy button)
989 *
990 * @param {object} uc_runner - An UpdraftCentral_Tasks_Runner instance of the current request.
991 * @param {boolean} processing - An optional flag that indicates that the update process is currently running. Thus, changing the button's state accordingly for the processing purpose.
992 *
993 * @return {void}
994 */
995 function mass_update_button_state(uc_runner, processing) {
996 var btn_update_all = jQuery('.updraftcentral_row_extracontents button#btn-mass-update');
997 button_state_update(btn_update_all, uc_runner, processing);
998 }
999
1000 /**
1001 * Updates the process button state
1002 *
1003 * @param {object} uc_runner - An UpdraftCentral_Tasks_Runner instance of the current request.
1004 * @param {boolean} processing - An optional flag that indicates that the update process is currently running. Thus, changing the button's state accordingly for the processing purpose.
1005 *
1006 * @return {void}
1007 */
1008 function button_state_update(btn_update_all, uc_runner, processing) {
1009 var count = uc_runner.tasks_count();
1010 var update_text, btn_text;
1011
1012 if (typeof processing !== 'undefined' && processing) {
1013 btn_update_all.addClass('disabled');
1014 btn_text = udclion.updates.updates_in_progress;
1015 } else {
1016 if (count > 0) {
1017 btn_update_all.removeClass('disabled');
1018 } else {
1019 btn_update_all.addClass('disabled');
1020 }
1021
1022 update_text = (count == 1) ? udclion.updates.queued_update : udclion.updates.queued_updates;
1023 btn_text = '<span class="dashicons dashicons-controls-play"></span> ' + udclion.updates.process + ' ' + update_text + ' (' + count + ')';
1024 }
1025
1026 btn_update_all.html(btn_text);
1027 }
1028
1029 /**
1030 * Resets the updates process (buttons, cache, runners, etc.)
1031 *
1032 * @param {object} site - An UpdraftCentral_Site object containing the current site
1033 * information needed by the process.
1034 * @param {string} abort_message - An optional message that indicates whether the reset was called due to an error or due to a normal completion.
1035 *
1036 * @return {void}
1037 */
1038 function reset_updates_process(site, abort_message) {
1039 var uc_runner = uc_runners.item(site.id);
1040 if (typeof uc_runner !== 'undefined') {
1041 if (typeof abort_message !== 'undefined') {
1042 uc_runner.progress.abort(abort_message);
1043 }
1044
1045 // Clear all tasks to give room for a new process.
1046 uc_runner.clear_tasks();
1047 uc_runners.update(site.id, uc_runner);
1048
1049 // Clear task_key cache
1050 cache.clear();
1051
1052 // Reset processing status
1053 site.update_processing = false;
1054 if (typeof sites !== 'undefined') {
1055 sites.update(site.id, site);
1056 }
1057
1058 // Update button state:
1059 update_button_state(uc_runner);
1060
1061 // Restoring the buttons to their initial states
1062 jQuery('.updraftcentral_row_extracontents button.update-go').removeClass('disabled').show();
1063 jQuery('.updraftcentral_row_extracontents button.update-in-progress').removeClass('disabled').hide();
1064
1065 // We're giving the user a two seconds window before completely removing
1066 // the progress var from view.
1067 setTimeout(function() {
1068 // Reset the progress bar to its original state
1069 uc_runner.progress.reset();
1070 }, 2000);
1071 }
1072 }
1073
1074 /**
1075 * Preloads Update Informations of Items that needs updating and
1076 * the current Site Credentials respectively.
1077 *
1078 * @param {object} site - An UpdraftCentral_Site object representing the current site for processing.
1079 * @param {object} output - A jQuery object/element that contains the items for update for the current site.
1080 *
1081 * @borrows {UpdraftCentral_Library.md5}
1082 * @borrows {UpdraftCentral_Credentials.load_credentials}
1083 * @return {object} - A jQuery promise object.
1084 */
1085 function preload_site_updates_credentials(site, output) {
1086 var deferred = jQuery.Deferred();
1087
1088 // Preparing initial state of updates. This is to ensure
1089 // we don't have any phantom entries that will cause some issues
1090 // when the show updates button is clicked or is triggered again.
1091 site.updates.plugin.clear();
1092 site.updates.theme.clear();
1093 site.updates.core.clear();
1094 site.updates.translation.clear();
1095
1096 // Make sure we don't show the nodes to the user, as we're doing these
1097 // process in the background.
1098 var html = jQuery('<table/>', {
1099 style: 'display:none;'
1100 }).html(output);
1101
1102
1103 html.find('.row.update-item').each(function() {
1104 var $container = jQuery(this);
1105 var entity = $container.data('entity');
1106 var name = $container.data('name');
1107 var info = $container.data(entity + '-info');
1108
1109 // Bypass check for non translation entity. Since we don't usually send
1110 // a request to wordpress.org for the translation info therefore, the below check
1111 // is only applicable to those entities/items that are not translation.
1112 if ('translation' !== entity) {
1113 if ((entity !== 'core' && (!info || !info.hasOwnProperty('update'))) || (entity === 'core' && (!info || !info.hasOwnProperty('version')))) {
1114 return;
1115 }
1116 }
1117
1118 var is_mysql = true;
1119 if (info.hasOwnProperty('installed') && info.installed.hasOwnProperty('is_mysql') && !info.installed.is_mysql) {
1120 is_mysql = false;
1121 }
1122
1123 if (info.hasOwnProperty('sufficient')) {
1124 var mysql_sufficient = (!is_mysql || info.sufficient.mysql);
1125 var php_sufficient = info.sufficient.php;
1126 if (!mysql_sufficient) {
1127 if (!php_sufficient) {
1128 mysql_error = '<h2>'+udclion.updates.cannot_update+'</h2>'+sprintf(udclion.updates.insufficient_php_and_mysql, info.version, info.php_version, info.mysql_version, info.installed.php, info.installed.mysql);
1129 } else {
1130 mysql_error = '<h2>'+udclion.updates.cannot_update+'</h2>'+sprintf(udclion.updates.insufficient_mysql, info.version, info.mysql_version, info.installed.mysql);
1131 }
1132 } else if (!php_sufficient) {
1133 mysql_error = '<h2>'+udclion.updates.cannot_update+'</h2>'+sprintf(udclion.updates.insufficient_php, info.version, info.php_version, info.installed.php);
1134 }
1135 }
1136
1137 if ('undefined' === typeof name || !name || !name.length) {
1138 switch (entity) {
1139 case 'plugin':
1140 case 'theme':
1141 name = info.update[entity];
1142 break;
1143 case 'core':
1144 name = udclion.updates.wordpress;
1145 break;
1146 case 'translation':
1147 name = udclion.updates.translations+' ('+site.site_description+')';
1148 break;
1149 default:
1150 break;
1151 }
1152 }
1153
1154 if ('undefined' !== typeof name && name && name.length) {
1155 var details = {};
1156 var errors = false;
1157
1158 switch (entity) {
1159 case 'plugin':
1160 details.plugin = info.update.plugin;
1161 details.slug = info.update.slug;
1162 details.label = info.name;
1163 details.name = name;
1164 details.old_version = info.version;
1165 details.new_version = info.update.new_version;
1166
1167 if (!details.plugin || !details.plugin.length || !details.slug || !details.slug.length) errors = true;
1168 break;
1169 case 'theme':
1170 details.theme = info.update.theme;
1171 details.label = info.name;
1172 details.name = name;
1173 details.old_version = info.version;
1174 details.new_version = info.update.new_version;
1175
1176 if (!details.theme || !details.theme.length) errors = true;
1177 break;
1178 case 'core':
1179 if (typeof mysql_error !== 'undefined') {
1180 details.mysql_error = mysql_error;
1181 }
1182 details.core = info.version;
1183 details.label = name;
1184 details.name = name;
1185 details.old_version = info.installed.version;
1186 details.new_version = info.version;
1187
1188 if (!details.core || !details.core.length) errors = true;
1189 break;
1190 case 'translation':
1191 details.translation = 1;
1192 details.label = udclion.updates.translations;
1193 details.name = name;
1194 break;
1195 default:
1196 break;
1197 }
1198
1199 if (!errors && details && details.hasOwnProperty('name') && details.name) {
1200 var key = UpdraftCentral_Library.md5(site.id+'_'+details.name);
1201
1202 details.entity = entity;
1203 site.updates[entity].add(key, details);
1204 } else {
1205 if (debug_level > 0) {
1206 console.log("UpdraftCentral: preload_site_updates_credentials(): update ("+entity+") without expected info (details follow)");
1207 console.log(details);
1208 }
1209 }
1210 }
1211 });
1212
1213 // Loads Remote Site Credentials
1214 credentials.load_credentials(site).then(function(response) {
1215 var requests = response.request_filesystem_credentials;
1216 var credentials_required = false;
1217
1218 for (var entity in requests) {
1219 if (requests[entity]) {
1220 credentials_required = true;
1221 }
1222 }
1223
1224 // Update site information from sites collection
1225 site.credentials_required = credentials_required;
1226 sites.update(site.id, site);
1227
1228 if (debug_level > 0) {
1229 console.log("UpdraftCentral: remote site credentials has been loaded");
1230 }
1231
1232 deferred.resolve({
1233 site: site,
1234 response: response
1235 });
1236 }).fail(function(response) {
1237 deferred.reject(response);
1238 });
1239
1240 return deferred.promise();
1241 }
1242
1243
1244 /**
1245 * Loads items for update from a given site
1246 *
1247 * N.B.: Not to be called directly but by the UpdraftCentral_Tasks_Runner instance.
1248 *
1249 * @param {object} $site_row - A jQuery object representing the currently processed site.
1250 * @param {object} $container - A jQuery object that holds the container of the mass updates area/form.
1251 * @param {object} uc_runner - An UpdraftCentral_Tasks_Runner instance primarily used as a context for the current process. It is automatically added by the UpdraftCentral_Tasks_Runner class.
1252 *
1253 * @return {object} - A jQuery promise object
1254 */
1255 function load_updates($site_row, $container, uc_runner) {
1256 var deferred = jQuery.Deferred();
1257 var progress_bar = uc_runner.progress;
1258 var update_options = {
1259 force_refresh: false
1260 }
1261
1262 if (jQuery(this).hasClass('updraftcentral_updates_force_check')) {
1263 update_options.force_refresh = true;
1264 }
1265
1266 // Set the current label of the currently processed item.
1267 // It uses the following display format: "Processing {label} ..."
1268 // If you want to use your own label, you can set the second argument "use_custom"
1269 // to true, and anything you put as a label will use instead.
1270 progress_bar.set_current_label(udclion.updates.loading_updates_from + ' "' + $site_row.data('site_description') + '"...', true);
1271
1272
1273 // Starts the progress bar routine.
1274 progress_bar.start();
1275
1276
1277 UpdraftCentral.send_site_rpc('updates.get_updates', update_options , $site_row, function(response, code, error_code) {
1278 if ('ok' == code && response && 'updates' == UpdraftCentral.get_dashboard_mode()) {
1279 var site_id = $site_row.data('site_id');
1280 var site = new UpdraftCentral_Site($site_row);
1281
1282 if (sites.exists(site_id)) {
1283 site = sites.item(site_id);
1284 }
1285
1286 var template = '',
1287 output = '',
1288 entities = {
1289 plugins: null,
1290 themes: null,
1291 core: null,
1292 translations: null
1293 };
1294
1295 if (response.data) {
1296 if (response.data.hasOwnProperty('plugins') && response.data.plugins.length) {
1297 entities.plugins = UpdraftCentral.template_replace('updates-plugin-mass-updates', { plugins: response.data.plugins, website: site.site_description, website_id: site.id, host_url: site.site_url });
1298 output += entities.plugins;
1299 }
1300
1301 if (response.data.hasOwnProperty('themes') && response.data.themes.length) {
1302 entities.themes = UpdraftCentral.template_replace('updates-theme-mass-updates', { themes: response.data.themes, website: site.site_description, website_id: site.id, host_url: site.site_url });
1303 output += entities.themes;
1304 }
1305
1306 if (response.data.hasOwnProperty('core') && response.data.core.length) {
1307 entities.core = UpdraftCentral.template_replace('updates-core-mass-updates', { core: response.data.core, website: site.site_description, website_id: site.id, host_url: site.site_url });
1308 output += entities.core;
1309 }
1310
1311 if (response.data.hasOwnProperty('translations') && response.data.translations) {
1312 var name = udclion.updates.translations+' ('+site.site_description+')';
1313 entities.translations = UpdraftCentral.template_replace('updates-translation-mass-updates', { translations: response.data.translations.items, as_json: JSON.stringify(response.data.translations.items), website_id: site.id, website: site.site_description, name: name });
1314 output += entities.translations;
1315 }
1316
1317 if (response.data.hasOwnProperty('meta')) {
1318 if (typeof response.data.meta.automatic_backups !== 'undefined') {
1319 site.automatic_backups = response.data.meta.automatic_backups;
1320 }
1321 }
1322 }
1323
1324 if (entities.plugins || entities.themes || entities.core || entities.translations) {
1325
1326 var output_container = $container.find('.updraft_updates_output');
1327 var option = jQuery('<option/>', {
1328 value: site.site_description
1329 });
1330
1331 output = UpdraftCentral_Library.sanitize_html(output);
1332 option.html(site.site_description);
1333 // output_container.find('select[name="uc_updates_filter_website"]').append(option);
1334
1335
1336 // Add Site to Sites Collection:
1337 if (!sites.exists(site_id)) sites.add(site.id, site);
1338
1339 // Preload Site Updates
1340 preload_site_updates_credentials(site, output).then(function(result) {
1341
1342 // Calling the update method of the progress bar will signal
1343 // that the current item being processed has been completed, thus,
1344 // re-evaluating and re-calculating the actual progress percentage.
1345 progress_bar.update();
1346
1347 // Add item as we load them.
1348 if (!$container.find('div.updraftcentral_table').is(':visible')) {
1349 $container.find('div.updraftcentral_table').show();
1350 }
1351 $container.find('div.updraftcentral_table > .tbody').append(output);
1352 $container.find('div.updraftcentral_table > .tbody input[name="uc_updates_check_item"]').attr('disabled', true);
1353
1354 // Show entity counts:
1355 var $mass_container = jQuery('div.updates_table_container.mass_updates');
1356 var plugin_count = $mass_container.find('.row.update-item[data-entity="plugin"]:visible').length,
1357 theme_count = $mass_container.find('.row.update-item[data-entity="theme"]:visible').length,
1358 core_count = $mass_container.find('.row.update-item[data-entity="core"]:visible').length;
1359 translation_count = $mass_container.find('.row.update-item[data-entity="translation"]:visible').length;
1360
1361 $mass_container.find('.filter-elements span.filter_plugin_count').html(plugin_count);
1362 $mass_container.find('.filter-elements span.filter_theme_count').html(theme_count);
1363 $mass_container.find('.filter-elements span.filter_core_count').html(core_count);
1364 $mass_container.find('.filter-elements span.filter_translation_count').html(translation_count);
1365
1366 if (typeof response.data !== 'undefined') {
1367 if (typeof response.data.plugins !== 'undefined' && response.data.plugins.length) {
1368 fill_wporg_metadata($site_row, response.data.plugins, 'plugin', $container.find('div.updraftcentral_table > .tbody'));
1369 }
1370 if (typeof response.data.themes !== 'undefined' && response.data.themes.length) {
1371 fill_wporg_metadata($site_row, response.data.themes, 'theme', $container.find('div.updraftcentral_table > .tbody'));
1372 }
1373 }
1374
1375 deferred.resolve({
1376 output: output,
1377 output_container: output_container,
1378 response: result,
1379 skip: false
1380 });
1381 }).fail(function(result) {
1382
1383 // Now, that we updated the UpdraftCentral.send_site_rpc() to return generic error
1384 // as fail to the callback function, therefore, we need to return this area as "resolve"
1385 // but with a "skip" response to true, so that the overall process can continue with the
1386 // rest of the websites currently being queued. Otherwise, the entire process will be aborted
1387 // when 1 website fails (D3 Queue Library feature).
1388 deferred.resolve({
1389 skip: true,
1390 response: result,
1391 site: $site_row.data('site_description')
1392 });
1393
1394 });
1395
1396
1397 } else {
1398
1399 // Don't return this as "reject" or error, if no update items are returned for plugin, theme
1400 // and core meaning that the site is up to date. Thus, we're sending it back
1401 // as "resolve" or success, to let the process continue without aborting the entire process.
1402 deferred.resolve({
1403 skip: true,
1404 response: udclion.updates.no_updates,
1405 site: $site_row.data('site_description')
1406 });
1407 }
1408
1409 } else {
1410
1411 // Same reason as above, we let the process continue with the rest of the
1412 // queued websites waiting to be processed.
1413 deferred.resolve({
1414 skip: true,
1415 response: (response && response.hasOwnProperty('error')) ? response.error : response,
1416 site: $site_row.data('site_description')
1417 });
1418
1419 // Here, we're sending a response to the callback call from UpdraftCentral.send_site_rpc()
1420 // that we will be handling the error in this call.
1421 return true;
1422 }
1423
1424 }, null, 90);
1425
1426
1427 return deferred.promise();
1428 }
1429
1430 /**
1431 * Sorts the update items by order selected
1432 *
1433 * @return {void}
1434 */
1435 function process_sort_action() {
1436 var sort_raw = jQuery('#updates_container select[name="uc_updates_sort"]').val();
1437
1438 var matches = sort_raw.match(/^(.*)_(asc|desc)$/);
1439
1440 if (null === matches) { return; }
1441
1442 var sort = matches[1];
1443 var sort_order = matches[2];
1444
1445 var $container = jQuery('#updates_container div.updraftcentral_table > .tbody');
1446 var $items = $container.find('.row.update-item');
1447
1448 $items.sort(function (a, b) {
1449 var contentA = jQuery(a).data(sort).toString().toLowerCase();
1450 var contentB = jQuery(b).data(sort).toString().toLowerCase();
1451
1452 if (sort_order === 'desc') {
1453 return (contentB < contentA) ? -1 : (contentB > contentA) ? 1 : 0;
1454 } else {
1455 return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
1456 }
1457 });
1458
1459 $container.html($items);
1460 }
1461
1462 /**
1463 * Show no patch releases available message if applicable
1464 *
1465 * @return {void}
1466 */
1467 function maybe_show_empty_release_message() {
1468 var container = $('.updraftcentral_table');
1469 var no_patch_div = container.find('.uc_no_patch_releases');
1470 var mass_opt = $('input#filter_patch_release');
1471 var site_opt = $('input#filter_site_patch_release');
1472
1473 // Hide message by default
1474 if (no_patch_div.length) no_patch_div.hide();
1475
1476 var is_mass_update_section = false;
1477 if (mass_opt.is(':visible')) is_mass_update_section = true;
1478
1479 if ((mass_opt.length && mass_opt.is(':visible') && mass_opt.is(':checked')) || (site_opt.length && site_opt.is(':visible') && site_opt.is(':checked'))) {
1480 // If we've already got the no matching items message in mass update then there's
1481 // no point in displaying the no patch release message there after.
1482 var bypass = is_mass_update_section ? container.find('div#no_matching_items').length : false;
1483
1484 if (0 === container.find('div.update-item:visible').length && !bypass) {
1485 if (no_patch_div.length) {
1486 no_patch_div.show();
1487 } else {
1488 var div = $('<div/>', {
1489 class: 'uc_no_patch_releases text-center'
1490 });
1491
1492 div.html(udclion.updates.no_patch_releases);
1493 container.append(div);
1494 }
1495 }
1496 }
1497 }
1498
1499 /**
1500 * Apply patch release filter when checkbox option is checked
1501 *
1502 * @param {array} sorted_items The items to filter
1503 * @return {void}
1504 */
1505 function maybe_apply_patch_release_filter(sorted_items) {
1506 if ($('input#filter_patch_release').is(':checked')) {
1507 $.each(sorted_items, function(index, value) {
1508 var update_item = jQuery(value);
1509 if ('undefined' !== typeof update_item && update_item) {
1510 var installed_version = update_item.data('installed_version') || '';
1511 if ('number' == typeof installed_version) installed_version = installed_version.toString();
1512
1513 var new_version = update_item.data('new_version') || '';
1514 if ('number' == typeof new_version) new_version = new_version.toString();
1515
1516 if (installed_version.length && new_version.length) {
1517 var iv = installed_version.split('.');
1518 var nv = new_version.split('.');
1519 if ('undefined' !== typeof iv[1] && 'undefined' !== typeof nv[1] && iv[0]+'.'+iv[1] == nv[0]+'.'+nv[1]) {
1520 update_item.show();
1521 } else {
1522 update_item.hide();
1523 }
1524 } else {
1525 update_item.hide();
1526 }
1527 }
1528 });
1529 }
1530 }
1531
1532 /**
1533 * Filters the updates items by filtered action selected
1534 *
1535 * @return {void}
1536 */
1537 function process_filter_action() {
1538 // var website = jQuery('#updates_container select[name="uc_updates_filter_website"] option:selected').val();
1539 var keyword = jQuery('#updates_container input[name="uc_updates_filter_keyword"]').val().toLowerCase();
1540 var $container = jQuery('#updates_container div.updraftcentral_table');
1541 var entities = [];
1542
1543 jQuery('.row.update-item').hide();
1544 jQuery('#updates_container .filter-elements input.uc_updates_filter_type:checked').each(function() {
1545 var filter = jQuery(this);
1546 entities.push(filter.val());
1547 });
1548
1549 if (entities.length) {
1550 for (var i=0; i<entities.length; i++) {
1551 var type = entities[i];
1552 if (typeof website !== 'undefined' && website.length) {
1553 jQuery('.row.update-item[data-entity="' + type + '"][data-website="' + website + '"]').show();
1554 } else {
1555 jQuery('.row.update-item[data-entity="' + type + '"]').show();
1556 }
1557 }
1558 }
1559
1560 if (keyword.length > 0) {
1561 jQuery('.row.update-item').each(function() {
1562 var update_item = jQuery(this);
1563
1564 if (typeof update_item !== 'undefined') {
1565 if (update_item.is(':visible')) {
1566 var entity = update_item.data('entity') || '';
1567 var name = update_item.data('name') || '';
1568 var website = update_item.data('website') || '';
1569 var installed_version = update_item.data('installed_version') || '';
1570 var new_version = update_item.data('new_version') || '';
1571
1572 if (entity.toLowerCase().indexOf(keyword) !== -1 || name.toLowerCase().indexOf(keyword) !== -1 || website.toLowerCase().indexOf(keyword) !== -1 || installed_version.toString().indexOf(keyword) !== -1 || new_version.toString().indexOf(keyword) !== -1) {
1573 update_item.show();
1574 } else {
1575 update_item.hide();
1576 }
1577 }
1578 }
1579 });
1580 }
1581
1582 }
1583
1584 /**
1585 * Reloads interface components/elements based on current selections/options
1586 *
1587 * A method intended to be called by "UpdraftCentral_Recorder" to update or refresh
1588 * each individual element and their underlying event handler.
1589 *
1590 * @return {void}
1591 */
1592 UpdraftCentral_Updates.reload_interface = function() {
1593 process_sort_filter_actions();
1594 }
1595
1596 /**
1597 * Paginates the update items result (to minimize vertical space)
1598 *
1599 * N.B. - Pagination is done by manipulating the resulting DOM elements
1600 * to avoid additional round trips to the server since we're loading
1601 * every update items for each sites.
1602 *
1603 * @return {void}
1604 */
1605 function paginate() {
1606 var $container = jQuery('#updates_container div.updraftcentral_table');
1607 var $item_container = jQuery('#updates_container div.updraftcentral_table > .tbody');
1608 var paginator = $container.find('span.uc_updates_paginator');
1609
1610 if (paginator.length) {
1611 var current_page = parseInt(cache.item('current_page'));
1612 var items_per_page = parseInt(cache.item('items_per_page'));
1613 var items = cache.item('sorted_items') || [];
1614 var pages = parseInt(items.length / items_per_page);
1615 var remainder = parseInt(items.length % items_per_page);
1616 var total_pages = (remainder > 0) ? pages + 1 : pages;
1617
1618 var start_index = (current_page - 1) * items_per_page;
1619 var end_index = current_page * items_per_page;
1620 var page_items = items.slice(start_index, end_index);
1621
1622
1623 if (page_items.length) {
1624 // Display filtered, sorted items from the
1625 // current page.
1626
1627 jQuery('.row.update-item').hide();
1628 for (var i=0; i<page_items.length; i++) {
1629 var item = page_items[i];
1630 var name = item.data('name');
1631 var website = item.data('website');
1632
1633 jQuery('.row.update-item[data-name="' + name + '"][data-website="' + website + '"]').show();
1634 }
1635 }
1636
1637 var previous = jQuery('<a/>', {
1638 id: 'load_previous',
1639 href: 'javascript://'
1640 }).html(udclion.updates.previous);
1641
1642 var next = jQuery('<a/>', {
1643 id: 'load_next',
1644 href: 'javascript://'
1645 }).html(udclion.updates.next);
1646
1647
1648 if ((current_page > 1) && page_items.length) {
1649 previous.removeClass('disabled');
1650 } else {
1651 previous.addClass('disabled');
1652 }
1653
1654 if ((current_page < total_pages) && page_items.length) {
1655 next.removeClass('disabled');
1656 } else {
1657 next.addClass('disabled');
1658 }
1659
1660 paginator.html(jQuery('<div/>').append(previous.clone()).html() + ' | ' + jQuery('<div/>').append(next.clone()).html());
1661 if (total_pages > 1) {
1662 paginator.show();
1663 } else {
1664 paginator.hide();
1665 }
1666
1667 if (jQuery('.row.update-item:visible').length > 0) {
1668 $item_container.find('div#no_matching_items').remove();
1669 } else {
1670 if ($container.find('div#no_matching_items').length == 0) {
1671 var no_matching_items = jQuery('<div id="no_matching_items" class="row"><div class="col-md-12">' + udclion.updates.no_matching_items + '</div></div>');
1672 $item_container.append(no_matching_items);
1673 }
1674 }
1675 }
1676
1677 }
1678
1679 /**
1680 * Moves to the next page of the update items result
1681 *
1682 * @see {UpdraftCentral_Updates.paginate}
1683 * @return {void}
1684 */
1685 function load_next() {
1686 var current_page = parseInt(cache.item('current_page'));
1687
1688 cache.update('current_page', current_page + 1);
1689 paginate();
1690 }
1691
1692 /**
1693 * Moves to the previous page of the update items result
1694 *
1695 * @see {UpdraftCentral_Updates.paginate}
1696 * @return {void}
1697 */
1698 function load_previous() {
1699 var current_page = parseInt(cache.item('current_page'));
1700
1701 cache.update('current_page', current_page - 1);
1702 paginate();
1703 }
1704
1705 /**
1706 * Fascade method for controlling and maintaining the ui elements
1707 * of the mass update interface.
1708 *
1709 * @borrows {UpdraftCentral_Updates.paginate}
1710 * @return {void}
1711 */
1712 function process_sort_filter_actions() {
1713
1714 process_filter_action();
1715 process_sort_action();
1716
1717 var $container = jQuery('#updraftcentral_dashboard_existingsites #updates_container');
1718 var $item_container = $container.find('div.updraftcentral_table > .tbody');
1719 var $mass_container = jQuery('div.updates_table_container.mass_updates');
1720 var plugin_count = 0,
1721 theme_count = 0,
1722 core_count = 0,
1723 translation_count = 0;
1724
1725 var sorted_items = [];
1726 $item_container.find('div.updates-update').each(function() {
1727 var item = jQuery(this);
1728
1729 if (item.is(':visible')) {
1730 var entity = item.data('entity');
1731 if ('plugin' === entity) plugin_count++;
1732 if ('theme' === entity) theme_count++;
1733 if ('core' === entity) core_count++;
1734 if ('translation' === entity) translation_count++;
1735
1736 sorted_items.push(item);
1737 }
1738 });
1739
1740 if (!cache.exists('sorted_items')) {
1741 cache.add('sorted_items', sorted_items);
1742 } else {
1743 cache.update('sorted_items', sorted_items);
1744 }
1745
1746 // When triggering a sort and filter action we always reset
1747 // the current_page to the first page to avoid confusion.
1748 cache.update('current_page', 1);
1749 paginate();
1750
1751 // Update entity counts:
1752 $mass_container.find('.filter-elements span.filter_plugin_count').html(plugin_count);
1753 $mass_container.find('.filter-elements span.filter_theme_count').html(theme_count);
1754 $mass_container.find('.filter-elements span.filter_core_count').html(core_count);
1755 $mass_container.find('.filter-elements span.filter_translation_count').html(translation_count);
1756
1757 // Apply patch release filter if applicable, meaning if
1758 // the patch release checkbox is checked
1759 maybe_apply_patch_release_filter(sorted_items);
1760
1761 // Show no available patch releases message depending from
1762 // the patch release filter result
1763 maybe_show_empty_release_message();
1764 }
1765
1766 /**
1767 * Resets the mass updates process (buttons, cache, runners, etc.)
1768 *
1769 * @param {object} options - An object containing options needed to reset the ui.
1770 * @param {string} abort_message - An optional message that indicates whether the reset was called due to an error or due to a normal completion.
1771 *
1772 * @return {void}
1773 */
1774 function reset_mass_loading_process(options, abort_message) {
1775 if (typeof options.site_runner !== 'undefined') {
1776 if (typeof abort_message !== 'undefined') {
1777 options.site_runner.progress.abort(abort_message);
1778 }
1779
1780 // After a successful run, we clear all tasks to give
1781 // room for a new process.
1782 options.site_runner.clear_tasks();
1783
1784 // Initialize starting cache variables:
1785 var current_page = 1;
1786
1787 if (!cache.exists('current_page')) {
1788 cache.add('current_page', current_page);
1789 } else {
1790 cache.update('current_page', current_page);
1791 }
1792
1793 if (!cache.exists('items_per_page')) {
1794 cache.add('items_per_page', default_items_per_page);
1795 } else {
1796 cache.update('items_per_page', default_items_per_page);
1797 }
1798
1799 // Set default per page value
1800 $('span.uc_updates_perpage > select').prop('disabled', false).val(default_items_per_page);
1801
1802 // Reset mass updates loading flag
1803 mass_updates_loading = false;
1804
1805 // Enable reload button
1806 options.container.find('button#btn-reload-updates').removeClass('disabled');
1807
1808
1809 if (typeof options.has_non_skipped_items !== 'undefined' && !options.has_non_skipped_items) {
1810 options.container.find('div.updraftcentral_table > .thead').hide();
1811 options.site_runner.progress.reset();
1812 } else {
1813 // Re-enable the form elements.
1814 options.form_container.find('input, select').removeAttr('disabled');
1815 options.container.find('div.updraftcentral_table > .tbody input[name="uc_updates_check_item"]').each(function() {
1816 var item = jQuery(this);
1817 var unavailable = item.data('unavailable');
1818
1819 if ('undefined' !== typeof unavailable && unavailable) {
1820 item.prop('disabled', true);
1821 } else {
1822 item.prop('disabled', false);
1823 }
1824 });
1825
1826 // Re-enable sort and filter fields:
1827 // Sort and filter with default parameters initially.
1828 process_sort_filter_actions();
1829
1830 // We're giving the user a two seconds window before completely removing
1831 // the progress var from view.
1832 setTimeout(function() {
1833 // Reset the progress bar to its original state
1834 options.site_runner.progress.reset();
1835 }, 2000);
1836 }
1837
1838
1839 }
1840 }
1841
1842 /**
1843 * Handles and controls the backup process of all the sites
1844 * that was set to run an automatic backup before update
1845 *
1846 * @params {object} button - A jQuery object representing the button that triggered the action
1847 * @param {object} progress - An UpdraftCentral_Task_Progress object from the current task runner instance
1848 *
1849 * @borrows {UpdraftCentral_Updates.process_auto_backup}
1850 *
1851 * @return {void}
1852 */
1853 function process_sites_backup(button, progress) {
1854
1855 if (sites_for_backup.count() === 0) {
1856 if (debug_level > 0) {
1857 console.log('UpdraftCentral_Updates: process_site_backup - All sites backup has been processed. Proceeding to mass updates now.');
1858 }
1859
1860 // Sets current backup status
1861 progress.set_custom_status(udclion.updates.backup_done);
1862
1863 setTimeout(function() {
1864 // Proceed with the pending mass updates process by triggering
1865 // the same button from which it was initially triggered.
1866 button.trigger('click');
1867 }, 2000);
1868
1869 } else {
1870 var items = sites_for_backup.get_items();
1871 if (items.length) {
1872 var site = items[0];
1873
1874 site.autobackup_options = {
1875 caller: process_sites_backup,
1876 mass_updates: true,
1877 args: [button, progress]
1878 };
1879 process_auto_backup(site, progress);
1880 }
1881 }
1882 }
1883
1884 /**
1885 * Removes item from queue
1886 *
1887 * @param {integer} site_id The ID of the site where the current item has been pulled from
1888 * @param {name} name The name of the the item to remove
1889 * @param {object} queue_line_item Optional. The queue item container
1890 *
1891 * @return {boolean} True on successful removal. False, otherwise.
1892 */
1893 function remove_from_queue(site_id, name, queue_line_item) {
1894 var site = sites.item(site_id);
1895 var entry_removed = false;
1896
1897 if (typeof site !== 'undefined' && site) {
1898 var key = UpdraftCentral_Library.md5(site.id+'_'+name);
1899 if (site.update_queue.exists(key)) {
1900 var queued_item = site.update_queue.item(key);
1901 if ('undefined' !== typeof queued_item && queued_item) {
1902 var removed = site_runner.remove_task(queued_item.key);
1903 if (removed) {
1904 entry_removed = true;
1905 queued_items.remove(key);
1906
1907 site.update_queue.remove(key);
1908 sites.update(site.id, site);
1909
1910 // If we no longer have any available items for update for this particular site
1911 // then we remove this site from the sites_for_backup collection.
1912 if (sites_for_backup.exists(site.id) && 0 == site.update_queue.count()) {
1913 sites_for_backup.remove(site.id);
1914 }
1915
1916 // Only gets populated when called from "View queued items" remove function
1917 if ('undefined' !== typeof queue_line_item && queue_line_item.length) {
1918 queue_line_item.remove();
1919 }
1920 }
1921 }
1922 }
1923 }
1924
1925 return entry_removed;
1926 }
1927
1928 /**
1929 * Add an item to queue
1930 *
1931 * @param {object} item The item to add
1932 *
1933 * @return {boolean} True on success. False, otherwise.
1934 */
1935 function add_item_to_queue(item) {
1936 var update_row = item.closest('.update-item');
1937 var site_id = update_row.data('website_id');
1938 var site = sites.item(site_id);
1939 var added = false;
1940
1941 if (typeof site !== 'undefined' && site && item.is(':visible')) {
1942 var entity = update_row.data('entity');
1943 var name = update_row.data('name');
1944
1945 if ('undefined' !== typeof name && name.length) {
1946 var key = UpdraftCentral_Library.md5(site.id+'_'+name);
1947 if (!site.update_queue.exists(key)) {
1948 var update_info = site.get_update_info(entity, key);
1949 if (update_info) {
1950 var task_key = site_runner.add_task(process_update_info, [update_info, site]);
1951 if (task_key) {
1952 // Get the site that this item is associated with and store
1953 // it for later reference. We only need to do backups for this sites where
1954 // items are scheduled to be upgraded.
1955 if (!selected_sites.exists(site.id)) {
1956 selected_sites.add(site.id, site);
1957 }
1958
1959 // Add newly queued item to the queued_items collection.
1960 // Primarily used by the "View queued items" feature.
1961 queued_items.add(key, {
1962 site_id: site.id,
1963 entity: entity,
1964 name: name,
1965 website: update_row.data('website')
1966 });
1967
1968 // Update site's update_queue entries:
1969 site.update_queue.add(key, {
1970 key: task_key,
1971 site_id: site.id
1972 });
1973
1974 sites.update(site.id, site);
1975 added = true;
1976 item.prop('checked', true);
1977 }
1978 }
1979 }
1980 }
1981 }
1982
1983 return added;
1984 }
1985
1986 /**
1987 * Registers all available row clickers and listeners for this tab
1988 *
1989 * @see {@link http://api.jquery.com/on}
1990 */
1991 jQuery('#updraftcentral_dashboard_existingsites').on('updraftcentral_dashboard_mode_set_updates', function() {
1992
1993 /**
1994 * Handles the patch release filter action for a single site
1995 *
1996 * @see {UpdraftCentral.register_event_handler}
1997 * @borrows {UpdraftCentral_Updates.process_sort_filter_actions}
1998 */
1999 UpdraftCentral.register_event_handler('click', 'input#filter_site_patch_release', function() {
2000 var patch_release_option = $(this);
2001
2002 $('.updraftcentral_table div.update-item').each(function() {
2003 var update_item = $(this);
2004 if (patch_release_option.is(':checked')) {
2005 if ('undefined' !== typeof update_item && update_item) {
2006 var installed_version = update_item.data('installed_version') || '';
2007
2008 // We do this to make sure that we're actually processing a numerical string value since we're going
2009 // to use the `split` and `length` functions of the string prototype. Otherwise, if we received
2010 // a real number then it would raise an error that says 'split is not a function' message.
2011 if ('number' == typeof installed_version) installed_version = installed_version.toString();
2012
2013 var new_version = update_item.data('new_version') || '';
2014 if ('number' == typeof new_version) new_version = new_version.toString(); // Same explanation as above.
2015
2016 if (installed_version.length && new_version.length) {
2017 var iv = installed_version.split('.');
2018 var nv = new_version.split('.');
2019 if ('undefined' !== typeof iv[1] && 'undefined' !== typeof nv[1] && iv[0]+'.'+iv[1] == nv[0]+'.'+nv[1]) {
2020 update_item.show();
2021 } else {
2022 update_item.hide();
2023 }
2024 } else {
2025 update_item.hide();
2026 }
2027 }
2028 } else {
2029 update_item.show();
2030 }
2031 });
2032
2033 maybe_show_empty_release_message();
2034 });
2035
2036 /**
2037 * Handles the patch release filter action
2038 *
2039 * @see {UpdraftCentral.register_event_handler}
2040 * @borrows {UpdraftCentral_Updates.process_sort_filter_actions}
2041 */
2042 UpdraftCentral.register_event_handler('click', 'input#filter_patch_release', function() {
2043 process_sort_filter_actions();
2044 });
2045
2046 /**
2047 * Add/remove single item to/from queue
2048 *
2049 * @see {UpdraftCentral.register_event_handler}
2050 */
2051 UpdraftCentral.register_event_handler('click', '#updates_container input[name="uc_updates_check_item"]', function() {
2052 var update_row = $(this).closest('.update-item');
2053 var site_id = update_row.data('website_id');
2054 var site = sites.item(site_id);
2055 var item = $(this);
2056 var result;
2057
2058 if (item.is(':checked')) {
2059 result = add_item_to_queue(item);
2060 } else {
2061 if ('undefined' !== typeof site && site) {
2062 var name = update_row.data('name');
2063 if ('undefined' !== typeof name && name.length) {
2064 result = remove_from_queue(site.id, name);
2065 }
2066 }
2067 }
2068
2069 if ('undefined' !== typeof result && result) {
2070 // Update mass update button state
2071 mass_update_button_state(site_runner);
2072
2073 if (site_runner.tasks_count()) {
2074 jQuery('#updates_container button#btn-mass-update').removeClass('disabled');
2075 } else {
2076 jQuery('#updates_container button#btn-mass-update').addClass('disabled');
2077 }
2078 }
2079 });
2080
2081 /**
2082 * Add item(s) to queue
2083 *
2084 * @see {UpdraftCentral.register_event_handler}
2085 */
2086 UpdraftCentral.register_event_handler('click', '#updates_queue_selected_items > a', function() {
2087 var entries_added = 0;
2088
2089 jQuery('#updates_container input[name="uc_updates_check_item"]:visible').each(function() {
2090 var item = $(this);
2091 if (add_item_to_queue(item)) {
2092 entries_added++;
2093 }
2094 });
2095
2096 if (0 !== entries_added) {
2097 // Update mass update button state
2098 mass_update_button_state(site_runner);
2099
2100 if (site_runner.tasks_count()) {
2101 jQuery('#updates_container button#btn-mass-update').removeClass('disabled');
2102 } else {
2103 jQuery('#updates_container button#btn-mass-update').addClass('disabled');
2104 }
2105 }
2106 });
2107
2108 /**
2109 * Removes item(s) from queue
2110 *
2111 * @see {UpdraftCentral.register_event_handler}
2112 */
2113 UpdraftCentral.register_event_handler('click', '#updates_dequeue_selected_items > a', function() {
2114 var entries_removed = 0;
2115 var removed;
2116
2117 jQuery('#updates_container input[name="uc_updates_check_item"]:checked').each(function() {
2118 var update_row = $(this).closest('.update-item');
2119 var site_id = update_row.data('website_id');
2120 var site = sites.item(site_id);
2121
2122 if (typeof site !== 'undefined' && site && $(this).is(':visible')) {
2123 var name = update_row.data('name');
2124 if ('undefined' !== typeof name && name.length) {
2125 removed = remove_from_queue(site.id, name);
2126 if (removed) {
2127 entries_removed++;
2128 $(this).prop('checked', false);
2129 }
2130 }
2131 }
2132 });
2133
2134 if (0 !== entries_removed) {
2135 // Update mass update button state
2136 mass_update_button_state(site_runner);
2137
2138 if (site_runner.tasks_count()) {
2139 jQuery('#updates_container button#btn-mass-update').removeClass('disabled');
2140 } else {
2141 jQuery('#updates_container button#btn-mass-update').addClass('disabled');
2142 }
2143 }
2144 });
2145
2146 /**
2147 * View item(s) in queue
2148 *
2149 * @see {UpdraftCentral.register_event_handler}
2150 */
2151 UpdraftCentral.register_event_handler('click', '#updates_view_queued_items > a', function() {
2152 if (queued_items.count()) {
2153 var items = UpdraftCentral_Library.sort(queued_items.get_items(), 'name');
2154 var list = '<ul>';
2155 for (var i=0; i<items.length; i++) {
2156 var queued_item = items[i];
2157 list += sprintf('<li data-site_id="%s" data-name="%s">%s (%s) - %s <button class="btn btn-primary uc_remove_queue_item">%s</button></li>', queued_item.site_id, queued_item.name, queued_item.name, queued_item.entity, queued_item.website, udclion.updates.remove);
2158 }
2159 list += '</ul>';
2160 UpdraftCentral.open_modal(udclion.updates.queued_items, '<div class="uc_queued_items">'+list+'</div>', null, false, null, true, 'modal-md');
2161 } else {
2162 UpdraftCentral_Library.dialog.alert('<h2>'+udclion.updates.queue_empty+'</h2><p>'+udclion.updates.no_items_in_queue+'</p>');
2163 }
2164 });
2165
2166 /**
2167 * Remove item from queue
2168 *
2169 * @see {UpdraftCentral.register_event_handler}
2170 */
2171 UpdraftCentral.register_event_handler('click', '#updraftcentral_modal .uc_remove_queue_item', function() {
2172 var queue_item = $(this).closest('li');
2173 var parent = queue_item.parent();
2174 var name = queue_item.data('name');
2175 var site_id = queue_item.data('site_id');
2176 var site = sites.item(site_id);
2177
2178 if (typeof site !== 'undefined' && site) {
2179 if ('undefined' !== typeof name && name.length) {
2180 var removed = remove_from_queue(site.id, name, queue_item);
2181 if (removed) {
2182 var list_item = $('.update-item[data-website_id="'+site.id+'"][data-name="'+name+'"]');
2183 if ('undefined' !== typeof list_item && list_item && list_item.length) {
2184 list_item.find('input[name="uc_updates_check_item"]').prop('checked', false);
2185 }
2186
2187 // Update mass update button state
2188 mass_update_button_state(site_runner);
2189
2190 if (site_runner.tasks_count()) {
2191 jQuery('#updates_container button#btn-mass-update').removeClass('disabled');
2192 } else {
2193 jQuery('#updates_container button#btn-mass-update').addClass('disabled');
2194 }
2195
2196 // Check if queue is already empty after latest removal, if so, then close
2197 // dialog window
2198 if (0 == parent.children().length) {
2199 UpdraftCentral.close_modal();
2200 }
2201 }
2202 }
2203 }
2204 });
2205
2206 /**
2207 * Executes the load previous feature (move to previous page)
2208 *
2209 * @see {UpdraftCentral.register_event_handler}
2210 * @borrows {UpdraftCentral_Updates.load_previous}
2211 */
2212 UpdraftCentral.register_event_handler('click', '#updates_container div.updraftcentral_table span.uc_updates_paginator a#load_previous', function() {
2213 if (!jQuery(this).hasClass('disabled')) {
2214 load_previous();
2215 }
2216 });
2217
2218 /**
2219 * Executes the load next feature (move to next page)
2220 *
2221 * @see {UpdraftCentral.register_event_handler}
2222 * @borrows {UpdraftCentral_Updates.load_next}
2223 */
2224 UpdraftCentral.register_event_handler('click', '#updates_container div.updraftcentral_table span.uc_updates_paginator a#load_next', function() {
2225 if (!jQuery(this).hasClass('disabled')) {
2226 load_next();
2227 }
2228 });
2229
2230 /**
2231 * Handles the sorting and filtering actions from the selected choices
2232 *
2233 * N.B. - Basically, this triggers the method that handles all the
2234 * the needed ui changes.
2235 *
2236 * @see {UpdraftCentral.register_event_handler}
2237 * @borrows {UpdraftCentral_Updates.process_sort_filter_actions}
2238 */
2239 UpdraftCentral.register_event_handler('change', '#updates_container select[name="uc_updates_sort"], #updates_container input.uc_updates_filter_type, #updates_container select[name="uc_updates_filter_website"]', function() {
2240 process_sort_filter_actions();
2241 });
2242
2243 /**
2244 * Handles the per page selection and re-displays the entries paginated
2245 * based from the option selected
2246 *
2247 * @see {UpdraftCentral.register_event_handler}
2248 * @borrows {UpdraftCentral_Updates.process_sort_filter_actions}
2249 */
2250 UpdraftCentral.register_event_handler('change', 'span.uc_updates_perpage > select', function() {
2251 var value = $(this).val();
2252 var items_per_page = (value.length) ? value : default_items_per_page;
2253
2254 if (!cache.exists('items_per_page')) {
2255 cache.add('items_per_page', items_per_page);
2256 } else {
2257 cache.update('items_per_page', items_per_page);
2258 }
2259
2260 process_sort_filter_actions();
2261 });
2262
2263 /**
2264 * Filter the items for update by the entered keyword
2265 *
2266 * N.B. - Basically, this triggers the method that handles all the
2267 * the needed ui changes.
2268 *
2269 * @see {UpdraftCentral.register_event_handler}
2270 * @borrows {UpdraftCentral_Updates.process_sort_filter_actions}
2271 */
2272 UpdraftCentral.register_event_handler('keyup', '#updates_container input[name="uc_updates_filter_keyword"]', function() {
2273 process_sort_filter_actions();
2274 });
2275
2276 /**
2277 * Process all available items for update
2278 *
2279 * @see {UpdraftCentral.register_event_handler}
2280 * @borrows {UpdraftCentral_Updates.mass_update_button_state}
2281 * @borrows {UpdraftCentral_Updates.process_sites_backup}
2282 * @borrows {UpdraftCentral_Updates.process_sort_filter_actions}
2283 */
2284 UpdraftCentral.register_event_handler('click', '#updates_container button#btn-mass-update', function() {
2285 if (!jQuery(this).hasClass('disabled') || (sites_for_backup.count() === 0 && site_runner.tasks_count() && !mass_updates_loading)) {
2286 var button = jQuery(this);
2287 var $container = jQuery('div.updates_table_container.mass_updates');
2288
2289 if (debug_level > 0) {
2290 console.log('Mass updates execution started...');
2291 }
2292
2293 // Setting mass updates processing flag
2294 mass_updates_processing = true;
2295
2296 // Update button with "Updates in progress" state:
2297 mass_update_button_state(site_runner, true);
2298
2299 // We're preventing user here to dequeue any tasks when the process
2300 // is already running.
2301 jQuery('div.updates_table_container.mass_updates').find('input, select, button#btn-reload-updates').attr('disabled', true);
2302
2303 var progress_bar_container = $container.find('div.progress-section div.udc-progress-bar');
2304 site_runner.progress.set_container(progress_bar_container);
2305
2306 // Get user's choice/preference for processing backups before update
2307 var backup_choice = $('.updates_table_container.mass_updates select#updates_backup_choice_dropdown').val();
2308
2309 // Clearing sites for backup. Gets populated basing on option selected.
2310 sites_for_backup.clear();
2311
2312 if (-1 != $.inArray(backup_choice, ['default', 'yes'])) {
2313 if (selected_sites.count() > 0) {
2314 var site_items = selected_sites.get_items();
2315 if (site_items.length) {
2316 for (var i=0; i<site_items.length; i++) {
2317 var site = site_items[i];
2318
2319 if (!site.autobackup_complete && site.update_queue.count() > 0) {
2320 var backup = ('yes' == backup_choice || (site.automatic_backups && 'default' == backup_choice)) ? true : false;
2321 if (backup && !sites_for_backup.exists(site.id)) {
2322 sites_for_backup.add(site.id, site);
2323 }
2324 }
2325 }
2326 }
2327 }
2328 }
2329
2330 // Backup Process: (if autobackup is set)
2331 // We make sure we only call the autobackup process once for the given session.
2332 if (sites_for_backup.count() > 0) {
2333 process_sites_backup(button, site_runner.progress);
2334 } else {
2335
2336 site_runner.process_tasks().then(function(result) {
2337 if (debug_level > 0) {
2338 console.log('Mass updates execution has been completed!');
2339 }
2340
2341 var message = '<h2>'+udclion.updates.error_items_header+' ('+udclion.updates.skipped+')</h2>';
2342 var item;
2343 var error_items = [];
2344 for (var i=0; i<result.length; i++) {
2345 item = result[i];
2346 if (typeof item.skip !== 'undefined' && item.skip) {
2347 if ('undefined' !== typeof item.item && item.item) {
2348 var info = item.response;
2349 error_items.push({
2350 label: info.label,
2351 version: info.old_version,
2352 site: item.site
2353 });
2354 } else {
2355 UpdraftCentral.add_dashboard_notice_singleton(message + item.response + ' - ' + item.site, 'error', 30000, {}, 'errors');
2356 }
2357 }
2358 }
2359
2360 if (error_items.length) {
2361 for (var i=0; i<error_items.length; i++) {
2362 var error = error_items[i];
2363 message += '<div>'+sprintf('%s (%s) - %s.', error.label, error.version, error.site)+'</div>';
2364 }
2365
2366 UpdraftCentral.add_dashboard_notice_singleton(message, 'error', 30000, {}, 'error_items');
2367 }
2368
2369 // Manually set the progress bar to complete.
2370 site_runner.progress.set_complete();
2371 site_runner.clear_tasks();
2372
2373 // Reset backup status
2374 var site_items = sites.get_items();
2375 if (site_items.length) {
2376 for (var i=0; i<site_items.length; i++) {
2377 site_items[i].autobackup_requested = false;
2378 site_items[i].autobackup_complete = false;
2379 sites.update(site_items[i].id, site_items[i]);
2380 }
2381 }
2382
2383 // Restore original (default) UI states
2384 mass_update_button_state(site_runner);
2385 $container.find('input, select, button#btn-reload-updates').each(function() {
2386 var item = jQuery(this);
2387 var unavailable = item.data('unavailable');
2388
2389 if ('undefined' !== typeof unavailable && unavailable) {
2390 item.prop('disabled', true);
2391 } else {
2392 item.prop('disabled', false);
2393 }
2394 });
2395
2396 // Unsetting mass updates processing flag
2397 mass_updates_processing = false;
2398
2399 // Clearing queued items
2400 queued_items.clear();
2401
2402 jQuery('#updates_container input[name="uc_updates_check_item"]').removeAttr('checked');
2403
2404
2405 // Check whether we still have valid update items to process.
2406 // Applicable only on successful process.
2407 setTimeout(function() {
2408 // Reset the progress bar to its original state
2409 site_runner.progress.reset();
2410
2411 // Refresh entries after successful process and removal
2412 // of update items. This is to ensure that the ui/paging won't break if some
2413 // items were skipped.
2414 process_sort_filter_actions();
2415
2416
2417 var $tbody = $container.find("div.updraftcentral_table > .tbody");
2418 if ($tbody.find('div.updates-update').length < 1) {
2419 $container.find('div.updraftcentral_table > .thead').hide();
2420 if ($tbody.find('div#no_matching_items').length > 0) {
2421 $tbody.find('div#no_matching_items').remove();
2422 }
2423 $tbody.append('<div class="row"><div class="col-md-12"><h5>' + udclion.updates.no_available_updates + '</h5></div></div>');
2424 }
2425 }, 3000);
2426
2427 }).fail(function(result) {
2428 if (debug_level > 0) {
2429 console.log('Mass updates execution has stopped due to the following error(s):');
2430 console.log(result);
2431 }
2432
2433 site_runner.progress.abort(udclion.updates.process_aborted);
2434 site_runner.clear_tasks();
2435
2436
2437 // Restore original (default) UI states
2438 mass_update_button_state(site_runner);
2439 jQuery('div.updates_table_container.mass_updates').find('input, select, button#btn-reload-updates').each(function() {
2440 var item = jQuery(this);
2441 var unavailable = item.data('unavailable');
2442
2443 if ('undefined' !== typeof unavailable && unavailable) {
2444 item.prop('disabled', true);
2445 } else {
2446 item.prop('disabled', false);
2447 }
2448 });
2449
2450 // Unsetting mass updates processing flag
2451 mass_updates_processing = false;
2452
2453 // Clearing queued items
2454 queued_items.clear();
2455
2456 jQuery('#updates_container input[name="uc_updates_check_item"]').removeAttr('checked');
2457
2458 // Re-enable sort and filter when error occurs.
2459 process_sort_filter_actions();
2460
2461
2462 // We're giving the user a two seconds window before completely removing
2463 // the progress var from view.
2464 setTimeout(function() {
2465 // Reset the progress bar to its original state
2466 site_runner.progress.reset();
2467 }, 2000);
2468
2469 });
2470 }
2471
2472 }
2473 });
2474
2475 /**
2476 * Restart the loading process
2477 *
2478 * @see {UpdraftCentral.register_event_handler}
2479 */
2480 UpdraftCentral.register_event_handler('click', '#updates_container #btn-reload-updates', function() {
2481 if (!jQuery(this).hasClass('disabled')) {
2482 jQuery(document).find('button.updraftcentral_action_show_all_updates').trigger('click');
2483 }
2484 });
2485
2486 /**
2487 * Opens a modal window showing a much more
2488 * detailed information about the item.
2489 *
2490 * @see {UpdraftCentral.register_event_handler}
2491 * @borrows {UpdraftCentral_Library.ucfirst}
2492 */
2493 UpdraftCentral.register_event_handler('click', '.updraftcentral_row_extracontents button.update-info', function() {
2494 var $container = jQuery(this).closest('.row.update-item');
2495 var entity = $container.data('entity');
2496 var info = $container.data(entity + '-info');
2497
2498 if ('undefined' === typeof info || 0 === info.length || !info) {
2499 if (debug_level > 0) {
2500 console.log(UpdraftCentral_Library.ucfirst(entity) + ' info not found');
2501 }
2502 return;
2503 }
2504
2505 if ('translation' === entity) {
2506 info = { translations: info };
2507 }
2508
2509 UpdraftCentral.open_modal(udclion.updates.update_info, UpdraftCentral.template_replace('updates-' + entity + '-update-info', info), true, false, post_modal_open);
2510 });
2511
2512 /**
2513 * Returns to the (default) updates section
2514 *
2515 * @see {UpdraftCentral.register_event_handler}
2516 */
2517 UpdraftCentral.register_event_handler('click', '#updraftcentral_updraftplus_actions > button.updraftcentral_action_choose_another_site', function() {
2518 // Restore original button label for choosing another site to manage.
2519 $(this).html($(this).html().replace(/\s\s+/, '').replace(udclion.return_to_updates, updates_button_text));
2520
2521 // Show all updates button when not in mass update area.
2522 $(this).siblings('.updraftcentral_action_show_all_updates').show();
2523 });
2524
2525 /**
2526 * Loads all updates from all the sites registered on this UpdraftCentral plugin instance
2527 *
2528 * @see {UpdraftCentral.register_event_handler}
2529 * @borrows {UpdraftCentral_Updates.reset_mass_loading_process}
2530 */
2531 UpdraftCentral.register_event_handler('click', '#updraftcentral_updraftplus_actions > button.updraftcentral_action_show_all_updates', function() {
2532
2533 // Hide show all updates button in its own area.
2534 $(this).hide();
2535
2536 // Here, we're trying to replace the "Choose another site..." label dyanmically and temporarily, only
2537 // when we're in the mass updates area.
2538 var choose = $(this).siblings('.updraftcentral_action_choose_another_site');
2539 updates_button_text = choose.text().replace(/\s\s+/, '');
2540 choose.html(choose.html().replace(/\s\s+/, '').replace(updates_button_text, udclion.return_to_updates));
2541
2542 // Mass update is non site specific. Thus, we hide all sites and only reference
2543 // it in the background to execute the needed actions.
2544 jQuery('#updraftcentral_sites_search_area, #updraftcentral_dashboard_existingsites .updraftcentral_site_row, #updraftcentral_dashboard_existingsites .updraftcentral_row_divider').hide();
2545
2546 var $existing_sites = jQuery('#updraftcentral_dashboard_existingsites');
2547
2548 // Load Initial Template:
2549 var $container = $existing_sites.find('#updates_container');
2550 if ($container.length === 0) {
2551 $container = jQuery('<div/>', {
2552 id: 'updates_container',
2553 class: 'updraftcentral_row_extracontents'
2554 });
2555
2556 $existing_sites.append($container);
2557 }
2558
2559
2560 var template = UpdraftCentral.template_replace('updates-table-mass-header', {});
2561 $container.html('<div class="updraft_updates_output updraft_module_output">' + template + '</div>');
2562
2563 // Clear selected sites and queue for new process
2564 queued_items.clear();
2565 selected_sites.clear();
2566 if (sites.count() > 0) {
2567 $.map(sites.get_items(), function(site, index) {
2568 site.update_queue.clear();
2569 });
2570 }
2571
2572 // Clear process cache
2573 cache.clear();
2574
2575
2576 // Making sure that we start with empty tasks, in case the
2577 // "Show Updates For All Sites" button is triggered more than once.
2578 site_runner.clear_tasks();
2579
2580
2581 $existing_sites.find('.updraftcentral_site_row:not(.suspended)').each(function() {
2582 var $site_row = jQuery(this);
2583 site_runner.add_task(load_updates, [$site_row, $container]);
2584 });
2585
2586
2587 // Disable reload button while the loading updates
2588 // process is in progress.
2589 $container.find('button#btn-reload-updates').addClass('disabled');
2590
2591 // Reset original website options:
2592 // $container.find('select[name="uc_updates_filter_website"] option:gt(0)').remove();
2593
2594
2595 // Disabled sort and filter fields while loading updates is on-going:
2596 var $form_container = $container.find('div.updates_table_container.mass_updates');
2597 $form_container.find('input, select').attr('disabled', true);
2598
2599
2600 // Set mass updates loading flag
2601 mass_updates_loading = true;
2602
2603
2604 // Disable per page options at first load
2605 $('span.uc_updates_perpage > select').prop('disabled', true);
2606
2607
2608 site_runner.progress.set_container($form_container.find('div.progress-section div.udc-progress-bar'));
2609 site_runner.process_tasks().then(function(result) {
2610 if (debug_level > 0) {
2611 console.log('Tasks execution has been completed!');
2612 }
2613
2614 var item,
2615 has_non_skipped_items = false;
2616
2617 for (var i=0; i<result.length; i++) {
2618 item = result[i];
2619 if (typeof item.skip !== 'undefined' && !item.skip) {
2620 has_non_skipped_items = true;
2621 } else {
2622 if (debug_level > 0) {
2623 if (typeof item.site !== 'undefined') {
2624 console.log(item.site + ' has been skipped due to the following reason or error:');
2625 console.log(item);
2626 }
2627 }
2628 }
2629 }
2630
2631 if (!has_non_skipped_items) {
2632 if (!$container.find('div.updraftcentral_table').is(':visible')) {
2633 $container.find('div.updraftcentral_table').show();
2634 }
2635 $container.find('div.updraftcentral_table > .tbody').append('<div class="row"><div class="col-md-12"><h5>' + udclion.updates.no_available_updates + '</h5></div></div>');
2636 }
2637
2638 // Set progress bar to complete.
2639 site_runner.progress.set_complete(udclion.updates.loading_process_completed);
2640
2641
2642 reset_mass_loading_process({
2643 site_runner: site_runner,
2644 container: $container,
2645 form_container: $form_container,
2646 has_non_skipped_items: has_non_skipped_items
2647 });
2648
2649 }).fail(function(result) {
2650 if (debug_level > 0) {
2651 console.log('Tasks execution has stopped due to the following error(s):');
2652 console.log(result);
2653 }
2654
2655 reset_mass_loading_process({
2656 site_runner: site_runner,
2657 container: $container,
2658 form_container: $form_container
2659 }, udclion.updates.abort);
2660
2661 });
2662
2663 });
2664
2665 // @codingStandardsIgnoreLine
2666 /**
2667 * @see {UpdraftCentral.register_modal_listener}
2668 */
2669 UpdraftCentral.register_modal_listener('ul#updates-sections-list a.updates-section-link', function(e) {
2670 e.preventDefault();
2671 var section = jQuery(this).data('section');
2672 jQuery('#updraftcentral_modal #updates-sections-list li.selected').removeClass('selected');
2673 jQuery(this).closest('li').addClass('selected');
2674 jQuery('#updraftcentral_modal .updates-section').hide();
2675 jQuery('#updraftcentral_modal #updates-section-'+section).show();
2676 });
2677
2678 // @codingStandardsIgnoreLine
2679 /**
2680 * @see {UpdraftCentral.register_modal_listener}
2681 */
2682 UpdraftCentral.register_modal_listener('.request-filesystem-credentials-dialog-content #ssh', function () {
2683 jQuery("#updraftcentral_modal .request-filesystem-credentials-dialog-content #ssh_keys").show();
2684 });
2685
2686 // @codingStandardsIgnoreLine
2687 /**
2688 * @see {UpdraftCentral.register_modal_listener}
2689 */
2690 UpdraftCentral.register_modal_listener('.request-filesystem-credentials-dialog-content #ftp, .request-filesystem-credentials-dialog-content #ftps', function () {
2691 jQuery("#updraftcentral_modal .request-filesystem-credentials-dialog-content #ssh_keys").hide();
2692 });
2693
2694 /**
2695 * Starts the update process for all available
2696 * items for update or an upgrade using the UpdraftCentral_Tasks_Runner.
2697 *
2698 * @see {UpdraftCentral.register_row_clicker}
2699 * @borrows {UpdraftCentral_Tasks_Runner.add_task}
2700 * @borrows {UpdraftCentral_Tasks_Runner.process_tasks}
2701 */
2702 UpdraftCentral.register_row_clicker('.updraftcentral_row_extracontents button#btn-update-all', function($site_row) {
2703 var site = sites.item($site_row.data('site_id'));
2704 var uc_runner = uc_runners.item(site.id);
2705
2706 if (!jQuery(this).hasClass('disabled') || (site.autobackup_complete && !site.update_processing && uc_runner.tasks_count())) {
2707 var button = jQuery(this);
2708 var $container = jQuery(this).parent();
2709
2710 if (debug_level > 0) {
2711 console.log('Task runner execution started...');
2712 }
2713
2714 // Update button with "Updates in progress" state:
2715 update_button_state(uc_runner, true);
2716
2717 // We're preventing user here to dequeue any tasks when the process
2718 // is already running.
2719 jQuery('.updraftcentral_row_extracontents button.update-go').addClass('disabled');
2720 jQuery('.updraftcentral_row_extracontents button.update-in-progress').addClass('disabled');
2721
2722 // Make sure that we set the correct message container that will be used to
2723 // display the progress (processing) messages.
2724 var $progress_bar_container = $container.find('div.progress-section div.udc-progress-bar');
2725 if (0 === $progress_bar_container.length) {
2726 $progress_bar_container = $container.append('<div class="row progress-section"><div class="col-md-12 udc-progress-bar"></div></div>').find('div.progress-section div.udc-progress-bar');
2727 }
2728 uc_runner.progress.set_container($progress_bar_container);
2729
2730 // Get user's choice/preference for processing backups before update (single site update)
2731 var backup_choice = $('.updates_table_container.per-site select#updates_backup_choice_dropdown').val();
2732
2733 // Backup Process: (if autobackup is set)
2734 // We make sure we only call the autobackup process once for the given session.
2735 if (((site.automatic_backups && 'default' == backup_choice) || 'yes' == backup_choice) && !site.autobackup_complete) {
2736 site.autobackup_options = {
2737 caller: button,
2738 bypass: false
2739 };
2740 process_auto_backup(site, uc_runner.progress);
2741 } else {
2742
2743 var timeout = 0;
2744 if (site.autobackup_complete && site.autobackup_options.hasOwnProperty('bypass') && !site.autobackup_options.bypass) {
2745 // Sets current backup status
2746 uc_runner.progress.set_custom_status(udclion.updates.backup_done);
2747
2748 // Set bypass to true to avoid showing the notice again once the backup process already completed
2749 site.autobackup_options.bypass = true;
2750
2751 // Add two seconds delay to display the message properly. Otherwise, it will
2752 // jump straight to the below code block.
2753 timeout = 2000;
2754 }
2755
2756 setTimeout(function() {
2757 // Update processing status:
2758 site.update_processing = true;
2759 sites.update(site.id, site);
2760
2761
2762 uc_runner.process_tasks().then(function(result) {
2763 if (debug_level > 0) {
2764 console.log('Tasks execution has been completed!');
2765 }
2766
2767 var message = '<h2>'+udclion.updates.error_items_header+' ('+udclion.updates.skipped+')</h2>';
2768 var item;
2769 var error_items = [];
2770 for (var i=0; i<result.length; i++) {
2771 item = result[i];
2772 if (typeof item.skip !== 'undefined' && item.skip) {
2773 if ('undefined' !== typeof item.item && item.item) {
2774 var info = item.response;
2775 error_items.push({
2776 label: info.label,
2777 version: info.old_version,
2778 site: item.site
2779 });
2780 } else {
2781 UpdraftCentral.add_dashboard_notice_singleton(message + item.response + ' - ' + item.site, 'error', 30000, {}, 'errors');
2782 }
2783 }
2784 }
2785
2786 if (error_items.length) {
2787 for (var i=0; i<error_items.length; i++) {
2788 var error = error_items[i];
2789 message += '<div>'+sprintf('%s (%s) - %s.', error.label, error.version, error.site)+'</div>';
2790 }
2791
2792 UpdraftCentral.add_dashboard_notice_singleton(message, 'error', 30000, {}, 'error_items');
2793 }
2794
2795 // Manually set the progress bar to complete.
2796 uc_runner.progress.set_complete();
2797
2798 // Reset backup status
2799 site.autobackup_requested = false;
2800 site.autobackup_complete = false;
2801 sites.update(site.id, site);
2802
2803 // Restore original (default) UI states
2804 reset_updates_process(site);
2805
2806 // Check whether we still have valid update items to process.
2807 // Applicable only on successful process.
2808 setTimeout(function() {
2809 var $tbody = $site_row.find(".updates_table_container .tbody");
2810 if ($tbody.children().length < 1) {
2811 $tbody.closest('.updates_table_container').remove();
2812 $site_row.find('.updraft_updates_output').append("<h5>" + udclion.updates.no_updates + "</h5>");
2813 }
2814 }, 3000);
2815
2816 }).fail(function(result) {
2817 if (debug_level > 0) {
2818 console.log('Tasks execution has stopped due to the following error(s):');
2819 console.log(result);
2820 }
2821
2822 // Restore original (default) UI states with abort message.
2823 reset_updates_process(site, udclion.updates.process_aborted);
2824 });
2825 }, timeout);
2826 }
2827
2828 }
2829 });
2830
2831 /**
2832 * Starts the update process for each individual
2833 * items available for update or an upgrade.
2834 *
2835 * @see {UpdraftCentral.register_row_clicker}
2836 * @borrows {UpdraftCentral_Updates.process_update}
2837 * @borrows {UpdraftCentral_Library.md5}
2838 */
2839 UpdraftCentral.register_row_clicker('.updraftcentral_row_extracontents button.update-go', function($site_row) {
2840 if (!jQuery(this).hasClass('disabled')) {
2841 var site = sites.item($site_row.data('site_id'));
2842 var uc_runner = uc_runners.item(site.id);
2843 var $update_row = jQuery(this).closest('.row.update-item');
2844 var entity = $update_row.data('entity');
2845 var name = $update_row.data('name');
2846
2847 if ('undefined' === typeof name) {
2848 switch (entity) {
2849 case 'plugin':
2850 case 'theme':
2851 var info = $update_row.data(entity + '-info');
2852 name = info.update[entity];
2853 break;
2854 case 'core':
2855 name = udclion.updates.wordpress;
2856 break;
2857 case 'translation':
2858 name = udclion.updates.translations+' ('+site.site_description+')';
2859 break;
2860 default:
2861 break;
2862 }
2863 }
2864
2865 if ('undefined' !== typeof site && 'undefined' !== typeof name && name.length) {
2866 var key = UpdraftCentral_Library.md5(site.id+'_'+name);
2867 var update_info = site.get_update_info(entity, key);
2868 if (update_info) {
2869
2870 // Check Mysql Errors:
2871 if (typeof update_info.mysql_error !== 'undefined') {
2872 UpdraftCentral_Library.dialog.alert(update_info.mysql_error, null, false);
2873 } else {
2874
2875 var task_key = uc_runner.add_task(process_update_info, [update_info, site]);
2876 if (task_key) {
2877 uc_runners.update(site.id, uc_runner);
2878
2879 if (debug_level > 0) {
2880 console.log('Item queued for processing:');
2881 console.log(update_info);
2882 }
2883
2884 // Prevent them clicking again on the same button
2885 var $button = $update_row.find('button.update-go');
2886 $button.hide();
2887 $update_row.find('.update-in-progress').css('width', 'auto').show();
2888
2889 // Saved task_key to cache in case the user wanted to de-queue
2890 // the currently queued task afterwards.
2891 cache.add(key, task_key);
2892
2893 // Update button state:
2894 update_button_state(uc_runner);
2895 }
2896
2897 }
2898 }
2899 }
2900 }
2901 });
2902
2903 /**
2904 * De-queues the previously queued task for update
2905 *
2906 * @see {UpdraftCentral.register_row_clicker}
2907 * @borrows {UpdraftCentral_Tasks_Runner.remove_task}
2908 * @borrows {UpdraftCentral_Library.md5}
2909 */
2910 UpdraftCentral.register_row_clicker('.updraftcentral_row_extracontents button.update-in-progress', function($site_row) {
2911 if (!jQuery(this).hasClass('disabled')) {
2912 var site = sites.item($site_row.data('site_id'));
2913 var uc_runner = uc_runners.item(site.id);
2914 var $update_row = jQuery(this).closest('.row.update-item');
2915 var entity = $update_row.data('entity');
2916 var name = $update_row.data('name');
2917
2918 if ('undefined' === typeof name) {
2919 switch (entity) {
2920 case 'plugin':
2921 case 'theme':
2922 var info = $update_row.data(entity + '-info');
2923 name = info.update[entity];
2924 break;
2925 case 'core':
2926 name = udclion.updates.wordpress;
2927 break;
2928 case 'translation':
2929 name = udclion.updates.translations+' ('+site.site_description+')';
2930 break;
2931 default:
2932 break;
2933 }
2934 }
2935
2936 if ('undefined' !== typeof site && 'undefined' !== typeof name && name.length) {
2937 var key = UpdraftCentral_Library.md5(site.id+'_'+name);
2938 if (cache.exists(key)) {
2939 var task_key = cache.item(key);
2940
2941 if (uc_runner.remove_task(task_key)) {
2942 // Prevent them clicking again on the same button
2943 var $button = $update_row.find('button.update-in-progress');
2944 $button.hide();
2945 $update_row.find('.update-go').css('width', 'auto').show();
2946
2947 // Remove task_key from cache
2948 cache.remove(key);
2949
2950 // Update button state:
2951 update_button_state(uc_runner);
2952 }
2953 }
2954 }
2955 }
2956 });
2957
2958 /**
2959 * Shows all available items from the server that needs
2960 * updating or an upgrade, and pre-loads site information
2961 * that is needed by the process.
2962 *
2963 * @see {UpdraftCentral.register_row_clicker}
2964 */
2965 UpdraftCentral.register_row_clicker('.updraftcentral_action_show_updates', function($site_row) {
2966 var update_options = {
2967 force_refresh: false
2968 }
2969
2970 if (jQuery(this).hasClass('updraftcentral_updates_force_check')) {
2971 update_options.force_refresh = true;
2972 }
2973
2974 UpdraftCentral.send_site_rpc('updates.get_updates', update_options , $site_row, function(response, code, error_code) {
2975 if ('ok' == code && response && 'updates' == UpdraftCentral.get_dashboard_mode()) {
2976 var site_id = $site_row.data('site_id');
2977 var site = new UpdraftCentral_Site($site_row);
2978
2979 if (sites.exists(site_id)) {
2980 site = sites.item(site_id);
2981 }
2982
2983 // We make sure that we have a task runner instance
2984 // for the given Site.
2985 if (!uc_runners.exists(site.id)) {
2986 uc_runners.add(site.id, new UpdraftCentral_Tasks_Runner({
2987 concurrency: 1
2988 }));
2989 } else {
2990 var uc_runner = uc_runners.item(site.id);
2991 uc_runner.clear_tasks();
2992
2993 // Update current runner instance
2994 uc_runners.update(site.id, uc_runner);
2995 }
2996
2997 var $row_extra_contents = $site_row.find('.updraftcentral_row_extracontents');
2998 var output = '',
2999 entities = {
3000 plugins: null,
3001 themes: null,
3002 core: null,
3003 translations: null
3004 };
3005
3006 if (response.data) {
3007 if (response.data.hasOwnProperty('plugins') && response.data.plugins.length) {
3008 entities.plugins = UpdraftCentral.template_replace('updates-plugin-updates', { plugins: response.data.plugins});
3009 }
3010
3011 if (response.data.hasOwnProperty('themes') && response.data.themes.length) {
3012 entities.themes = UpdraftCentral.template_replace('updates-theme-updates', { themes: response.data.themes});
3013 }
3014
3015 if (response.data.hasOwnProperty('core') && response.data.core.length) {
3016 entities.core = UpdraftCentral.template_replace('updates-core-updates', { core: response.data.core});
3017 }
3018
3019 if (response.data.hasOwnProperty('translations') && response.data.translations) {
3020 entities.translations = UpdraftCentral.template_replace('updates-translation-updates', { translations: response.data.translations.items, as_json: JSON.stringify(response.data.translations.items), website_id: site.id });
3021 }
3022
3023 if (response.data.hasOwnProperty('meta')) {
3024 if (typeof response.data.meta.automatic_backups !== 'undefined') {
3025 site.automatic_backups = response.data.meta.automatic_backups;
3026 }
3027 }
3028 }
3029
3030 if (entities.plugins || entities.themes || entities.core || entities.translations) {
3031 output = UpdraftCentral.template_replace('updates-table-header', entities);
3032 } else {
3033 output = "<h5>" + udclion.updates.no_updates + "</h5>";
3034 }
3035
3036 $row_extra_contents.html('<div class="updraft_updates_output updraft_module_output">' +
3037 '<button class="btn btn-refresh updraftcentral_action_show_updates updraftcentral_updates_force_check"><span class="dashicons dashicons-image-rotate"></span></button>' + UpdraftCentral_Library.sanitize_html(output) + '</div>');
3038
3039 if (response.data.plugins.length) {
3040 fill_wporg_metadata($site_row, response.data.plugins, 'plugin');
3041 }
3042 if (response.data.themes.length) {
3043 fill_wporg_metadata($site_row, response.data.themes, 'theme');
3044 }
3045
3046 // Add Site to Sites Collection:
3047 if (!sites.exists(site_id)) sites.add(site.id, site);
3048
3049 // Preload Site Updates
3050 load_site_updates(site);
3051 }
3052 });
3053 }, true);
3054
3055
3056 /**
3057 * Shows a much more detailed messages from the log when
3058 * an error has occured.
3059 *
3060 * @see {@link http://api.jquery.com/on}
3061 */
3062 jQuery('#updraftcentral_notice_container').on('click', '.updraftcentral_notice .update-failure-messages-show', function(e) {
3063 e.preventDefault();
3064 jQuery(this).hide().siblings('.update-failure-messages-text').slideDown();
3065 });
3066
3067 /**
3068 * Resets the "site.update_processing" as a means to fix an issue
3069 * that stops the process.
3070 *
3071 * @see {@link http://api.jquery.com/on}
3072 */
3073 jQuery('#updraftcentral_notice_container').on('click', '.updraftcentral_notice > .updraftcentral_notice_dismiss', function() {
3074 var $container = jQuery(this).closest('#updraftcentral_notice_container');
3075 var $error_notices = $container.find('.update-errors');
3076
3077 if (jQuery.trim($error_notices.html()).length > 1) {
3078 var site_id = $site_row.data('site_id');
3079 var site = sites.item(site_id);
3080
3081 site.update_processing = false;
3082 sites.update(site.id, site);
3083 }
3084 });
3085
3086 /**
3087 * Setting some of process flags to continue with the update process if queue
3088 * is still not empty.
3089 *
3090 * @see {@link http://api.jquery.com/on}
3091 */
3092 jQuery('#updraftcentral_dashboard_existingsites').on('updraftcentral_listener_finished_updraftplus_backup', function(event, data) {
3093 event.preventDefault();
3094
3095 var site_id = data.site_id;
3096 var site = sites.item(site_id);
3097
3098 if (typeof site !== 'undefined') {
3099 if (!site.autobackup_complete) {
3100 if (debug_level > 0) {
3101 console.log("UpdraftCentral: automatic backup is complete.");
3102 }
3103 site.autobackup_complete = true;
3104 sites.update(site.id, site);
3105
3106 // Proceed with the process after backup has finished.
3107 if (typeof site.autobackup_options.caller !== 'undefined') {
3108 var caller = site.autobackup_options.caller;
3109 if (typeof site.autobackup_options.mass_updates !== 'undefined' && site.autobackup_options.mass_updates) {
3110 if (sites_for_backup.exists(site.id)) {
3111 sites_for_backup.remove(site.id);
3112 }
3113
3114 caller.apply(null, site.autobackup_options.args);
3115 } else {
3116 caller.trigger('click');
3117 }
3118 }
3119 }
3120 }
3121 });
3122
3123 /**
3124 * Put clicked links within the settings sections into their own tab
3125 *
3126 * @see {@link http://api.jquery.com/on}
3127 */
3128 jQuery('#updraftcentral_dashboard_existingsites_container').on('click', '.updraftcentral_site_row '+settings_css_sub_prefix+' .updates-update a', function(e) {
3129 var href = jQuery(this).attr('href');
3130 if ('undefined' === typeof href) { return; }
3131 if (href.substring(0, 4) == 'http') {
3132 e.preventDefault();
3133 var win = window.open(href, '_blank');
3134 win.focus();
3135 }
3136 });
3137
3138 });
3139
3140 }
3141