| 1 |
/** |
| 2 |
* Send an action over AJAX. A wrapper around jQuery.ajax. In future, all consumers can be reviewed to simplify some of the options, where there is historical cruft. |
| 3 |
* N.B. updraft_iframe_modal() below uses the AJAX URL for the iframe's src attribute |
| 4 |
* |
| 5 |
* @param {string} action - the action to send |
| 6 |
* @param {*} data - data to send |
| 7 |
* @param {Function} callback - will be called with the results |
| 8 |
* @param {object} options -further options. Relevant properties include: |
| 9 |
* - [json_parse=true] - whether to JSON parse the results |
| 10 |
* - [alert_on_error=true] - whether to show an alert box if there was a problem (otherwise, suppress it) |
| 11 |
* - [action='updraft_ajax'] - what to send as the action parameter on the AJAX request (N.B. action parameter to this function goes as the 'subaction' parameter on the AJAX request) |
| 12 |
* - [nonce=updraft_credentialtest_nonce] - the nonce value to send. |
| 13 |
* - [nonce_key='nonce'] - the key value for the nonce field |
| 14 |
* - [timeout=null] - set a timeout after this number of seconds (or if null, none is set) |
| 15 |
* - [async=true] - control whether the request is asynchronous (almost always wanted) or blocking (would need to have a specific reason) |
| 16 |
* - [type='POST'] - GET or POST |
| 17 |
*/ |
| 18 |
function updraftcentral_send_command(action, data, callback, options) { |
| 19 |
|
| 20 |
default_options = { |
| 21 |
json_parse: true, |
| 22 |
alert_on_error: true, |
| 23 |
action: 'updraft_central_ajax', |
| 24 |
nonce_key: 'nonce', |
| 25 |
timeout: null, |
| 26 |
async: true, |
| 27 |
type: 'POST' |
| 28 |
} |
| 29 |
|
| 30 |
if ('undefined' !== typeof uclion.updraftcentral_request_nonce && uclion.updraftcentral_request_nonce) { |
| 31 |
default_options.nonce = uclion.updraftcentral_request_nonce; |
| 32 |
} |
| 33 |
|
| 34 |
if ('undefined' === typeof options) options = {}; |
| 35 |
|
| 36 |
for (var opt in default_options) { |
| 37 |
if (!options.hasOwnProperty(opt)) { options[opt] = default_options[opt]; } |
| 38 |
} |
| 39 |
|
| 40 |
var ajax_data = { |
| 41 |
action: options.action, |
| 42 |
subaction: action, |
| 43 |
}; |
| 44 |
|
| 45 |
ajax_data[options.nonce_key] = options.nonce; |
| 46 |
ajax_data.action_data = data; |
| 47 |
|
| 48 |
var ajax_opts = { |
| 49 |
type: options.type, |
| 50 |
url: ajaxurl, |
| 51 |
data: ajax_data, |
| 52 |
success: function(response, status) { |
| 53 |
if (options.json_parse) { |
| 54 |
try { |
| 55 |
var resp = central_parse_json(response); |
| 56 |
} catch (e) { |
| 57 |
if ('function' == typeof options.error_callback) { |
| 58 |
return options.error_callback(response, e, 502, resp); |
| 59 |
} else { |
| 60 |
console.log(e); |
| 61 |
console.log(response); |
| 62 |
if (options.alert_on_error) { alert(uclion.unexpectedresponse+' '+response); } |
| 63 |
return; |
| 64 |
} |
| 65 |
} |
| 66 |
if (resp.hasOwnProperty('fatal_error')) { |
| 67 |
if ('function' == typeof options.error_callback) { |
| 68 |
// 500 is internal server error code |
| 69 |
return options.error_callback(response, status, 500, resp); |
| 70 |
} else { |
| 71 |
console.error(resp.fatal_error_message); |
| 72 |
if (options.alert_on_error) { alert(resp.fatal_error_message); } |
| 73 |
return false; |
| 74 |
} |
| 75 |
} |
| 76 |
if ('function' == typeof callback) callback(resp, status, response); |
| 77 |
} else { |
| 78 |
if ('function' == typeof callback) callback(response, status); |
| 79 |
} |
| 80 |
}, |
| 81 |
error: function(response, status, error_code) { |
| 82 |
if ('function' == typeof options.error_callback) { |
| 83 |
options.error_callback(response, status, error_code); |
| 84 |
} else { |
| 85 |
console.log("updraftcentral_send_command: error: "+status+" ("+error_code+")"); |
| 86 |
console.log(response); |
| 87 |
} |
| 88 |
}, |
| 89 |
dataType: 'text', |
| 90 |
async: options.async |
| 91 |
}; |
| 92 |
|
| 93 |
if (null != options.timeout) { ajax_opts.timeout = options.timeout; } |
| 94 |
|
| 95 |
jQuery.ajax(ajax_opts); |
| 96 |
|
| 97 |
} |
| 98 |
|
| 99 |
/** |
| 100 |
* Parse JSON string, including automatically detecting unwanted extra input and skipping it |
| 101 |
* |
| 102 |
* @param {string} json_mix_str - JSON string which need to parse and convert to object |
| 103 |
* @param {boolean} analyse - if true, then the return format will contain information on the parsing, and parsing will skip attempting to JSON.parse() the entire string (will begin with trying to locate the actual JSON) |
| 104 |
* |
| 105 |
* @throws SyntaxError|String (including passing on what JSON.parse may throw) if a parsing error occurs. |
| 106 |
* |
| 107 |
* @returns Mixed parsed JSON object. Will only return if parsing is successful (otherwise, will throw). If analyse is true, then will rather return an object with properties (mixed)parsed, (integer)json_start_pos and (integer)json_end_pos |
| 108 |
*/ |
| 109 |
function central_parse_json(json_mix_str, analyse) { |
| 110 |
|
| 111 |
analyse = ('undefined' === typeof analyse) ? false : true; |
| 112 |
|
| 113 |
// Just try it - i.e. the 'default' case where things work (which can include extra whitespace/line-feeds, and simple strings, etc.). |
| 114 |
if (!analyse) { |
| 115 |
try { |
| 116 |
var result = JSON.parse(json_mix_str); |
| 117 |
return result; |
| 118 |
} catch (e) { |
| 119 |
console.log(uclion.plugin_name+': Exception when trying to parse JSON (1) - will attempt to fix/re-parse based upon first/last curly brackets'); |
| 120 |
console.log(json_mix_str); |
| 121 |
} |
| 122 |
} |
| 123 |
|
| 124 |
var json_start_pos = json_mix_str.indexOf('{'); |
| 125 |
var json_last_pos = json_mix_str.lastIndexOf('}'); |
| 126 |
|
| 127 |
// Case where some php notice may be added after or before json string |
| 128 |
if (json_start_pos > -1 && json_last_pos > -1) { |
| 129 |
var json_str = json_mix_str.slice(json_start_pos, json_last_pos + 1); |
| 130 |
try { |
| 131 |
var parsed = JSON.parse(json_str); |
| 132 |
if (!analyse) { console.log(uclion.plugin_name+': JSON re-parse successful'); } |
| 133 |
return analyse ? { parsed: parsed, json_start_pos: json_start_pos, json_last_pos: json_last_pos + 1 } : parsed; |
| 134 |
} catch (e) { |
| 135 |
console.log(uclion.plugin_name+': Exception when trying to parse JSON (2) - will attempt to fix/re-parse based upon bracket counting'); |
| 136 |
|
| 137 |
var cursor = json_start_pos; |
| 138 |
var open_count = 0; |
| 139 |
var last_character = ''; |
| 140 |
var inside_string = false; |
| 141 |
|
| 142 |
// Don't mistake this for a real JSON parser. Its aim is to improve the odds in real-world cases seen, not to arrive at universal perfection. |
| 143 |
while ((open_count > 0 || cursor == json_start_pos) && cursor <= json_last_pos) { |
| 144 |
|
| 145 |
var current_character = json_mix_str.charAt(cursor); |
| 146 |
|
| 147 |
if (!inside_string && '{' == current_character) { |
| 148 |
open_count++; |
| 149 |
} else if (!inside_string && '}' == current_character) { |
| 150 |
open_count--; |
| 151 |
} else if ('"' == current_character && '\\' != last_character) { |
| 152 |
inside_string = inside_string ? false : true; |
| 153 |
} |
| 154 |
|
| 155 |
last_character = current_character; |
| 156 |
cursor++; |
| 157 |
} |
| 158 |
console.log("Started at cursor="+json_start_pos+", ended at cursor="+cursor+" with result following:"); |
| 159 |
console.log(json_mix_str.substring(json_start_pos, cursor)); |
| 160 |
|
| 161 |
try { |
| 162 |
var parsed = JSON.parse(json_mix_str.substring(json_start_pos, cursor)); |
| 163 |
console.log(uclion.plugin_name+': JSON re-parse successful'); |
| 164 |
return analyse ? { parsed: parsed, json_start_pos: json_start_pos, json_last_pos: cursor } : parsed; |
| 165 |
} catch (e) { |
| 166 |
// Throw it again, so that our function works just like JSON.parse() in its behaviour. |
| 167 |
throw e; |
| 168 |
} |
| 169 |
} |
| 170 |
} |
| 171 |
|
| 172 |
throw uclion.plugin_name+": could not parse the JSON"; |
| 173 |
|
| 174 |
} |
| 175 |
|
| 176 |
jQuery(function($) { |
| 177 |
$('#updraftcentral_keys').on('click', 'a.updraftcentral_keys_show', function(e) { |
| 178 |
e.preventDefault(); |
| 179 |
$(this).remove(); |
| 180 |
$('#updraftcentral_keys_table').slideDown(); |
| 181 |
}); |
| 182 |
|
| 183 |
$('#updraftcentral_keycreate_altmethod_moreinfo_get').on('click', function(e) { |
| 184 |
e.preventDefault(); |
| 185 |
$(this).remove(); |
| 186 |
$('#updraftcentral_keycreate_altmethod_moreinfo').slideDown(); |
| 187 |
}); |
| 188 |
|
| 189 |
function updraftcentral_keys_setupform(on_page_load) { |
| 190 |
var is_other = jQuery('#updraftcentral_mothership_other').is(':checked') ? true : false; |
| 191 |
if (is_other) { |
| 192 |
jQuery('#updraftcentral_keycreate_mothership').prop('disabled', false); |
| 193 |
if (on_page_load) { |
| 194 |
jQuery('#updraftcentral_keycreate_mothership_firewalled_container').show(); |
| 195 |
} else { |
| 196 |
jQuery('.updraftcentral_wizard_self_hosted_stage2').show(); |
| 197 |
jQuery('#updraftcentral_keycreate_mothership_firewalled_container').slideDown(); |
| 198 |
jQuery('#updraftcentral_keycreate_mothership').trigger('focus'); |
| 199 |
} |
| 200 |
} else { |
| 201 |
jQuery('#updraftcentral_keycreate_mothership').prop('disabled', true); |
| 202 |
if (!on_page_load) { |
| 203 |
jQuery('.updraftcentral_wizard_self_hosted_stage2').hide(); |
| 204 |
updraftcentral_stage2_go(); |
| 205 |
} |
| 206 |
} |
| 207 |
} |
| 208 |
|
| 209 |
function updraftcentral_stage2_go() { |
| 210 |
// Reset the error message before we continue |
| 211 |
jQuery('#updraftcentral_wizard_stage1_error').text(''); |
| 212 |
|
| 213 |
var host = ''; |
| 214 |
|
| 215 |
if (jQuery('#updraftcentral_mothership_updraftpluscom').is(':checked')) { |
| 216 |
jQuery('.updraftcentral_keycreate_description').hide(); |
| 217 |
host = 'updraftplus.com'; |
| 218 |
} else if (jQuery('#updraftcentral_mothership_other').is(':checked')) { |
| 219 |
jQuery('.updraftcentral_keycreate_description').show(); |
| 220 |
var mothership = jQuery('#updraftcentral_keycreate_mothership').val(); |
| 221 |
if ('' == mothership) { |
| 222 |
jQuery('#updraftcentral_wizard_stage1_error').text(uclion.updraftcentral_wizard_empty_url); |
| 223 |
return; |
| 224 |
} |
| 225 |
try { |
| 226 |
var url = new URL(mothership); |
| 227 |
host = url.hostname; |
| 228 |
} catch (e) { |
| 229 |
// Try and grab the host name a different way if it failed because of no URL object (e.g. Firefox version 25 and below). |
| 230 |
if ('undefined' === typeof URL) { |
| 231 |
host = jQuery('<a>').prop('href', mothership).prop('hostname'); |
| 232 |
} |
| 233 |
if (!host || 'undefined' !== typeof URL) { |
| 234 |
jQuery('#updraftcentral_wizard_stage1_error').text(uclion.updraftcentral_wizard_invalid_url); |
| 235 |
return; |
| 236 |
} |
| 237 |
} |
| 238 |
} |
| 239 |
|
| 240 |
jQuery('#updraftcentral_keycreate_description').val(host); |
| 241 |
|
| 242 |
jQuery('.updraftcentral_wizard_stage1').hide(); |
| 243 |
jQuery('.updraftcentral_wizard_stage2').show(); |
| 244 |
} |
| 245 |
|
| 246 |
jQuery('#updraftcentral_keys').on('click', 'input[type="radio"]', function() { |
| 247 |
updraftcentral_keys_setupform(false); |
| 248 |
}); |
| 249 |
// Initial setup (for browsers, e.g. Firefox, that remember form selection state but not DOM state, which can leave an inconsistent state) |
| 250 |
updraftcentral_keys_setupform(true); |
| 251 |
|
| 252 |
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_view_log', function(e) { |
| 253 |
e.preventDefault(); |
| 254 |
jQuery('#updraftcentral_view_log_container').block({ message: '<div style="margin: 8px; font-size:150%;"><img src="'+uclion.central_url+'/images/udlogo-rotating.gif" height="80" width="80" style="padding-bottom:10px;"><br>'+uclion.fetching+'</div>'}); |
| 255 |
try { |
| 256 |
updraftcentral_send_command('get_log', null, function(response) { |
| 257 |
jQuery('#updraftcentral_view_log_container').unblock(); |
| 258 |
if (response.hasOwnProperty('log_contents')) { |
| 259 |
jQuery('#updraftcentral_view_log_contents').html('<div style="border:1px solid;padding: 2px;max-height: 400px; overflow-y:scroll;">'+response.log_contents+'</div>'); |
| 260 |
} else { |
| 261 |
console.log(response); |
| 262 |
} |
| 263 |
}, { error_callback: function(response, status, error_code, resp) { |
| 264 |
jQuery('#updraftcentral_view_log_container').unblock(); |
| 265 |
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) { |
| 266 |
console.error(resp.fatal_error_message); |
| 267 |
alert(resp.fatal_error_message); |
| 268 |
} else { |
| 269 |
var error_message = "updraftcentral_send_command: error: "+status+" ("+error_code+")"; |
| 270 |
console.log(error_message); |
| 271 |
alert(error_message); |
| 272 |
console.log(response); |
| 273 |
} |
| 274 |
} |
| 275 |
}); |
| 276 |
} catch (err) { |
| 277 |
jQuery('#updraft_central_key').html(); |
| 278 |
console.log(err); |
| 279 |
} |
| 280 |
}); |
| 281 |
|
| 282 |
// UpdraftCentral |
| 283 |
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_wizard_go', function(e) { |
| 284 |
jQuery('#updraftcentral_wizard_go').hide(); |
| 285 |
jQuery('.updraftcentral_wizard_success').remove(); |
| 286 |
jQuery('.create_key_container').show(); |
| 287 |
}); |
| 288 |
|
| 289 |
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_stage1_go', function(e) { |
| 290 |
e.preventDefault(); |
| 291 |
jQuery('.updraftcentral_wizard_stage2').hide(); |
| 292 |
jQuery('.updraftcentral_wizard_stage1').show(); |
| 293 |
}); |
| 294 |
|
| 295 |
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_stage2_go', function(e) { |
| 296 |
e.preventDefault(); |
| 297 |
|
| 298 |
updraftcentral_stage2_go(); |
| 299 |
}); |
| 300 |
|
| 301 |
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_keycreate_go', function(e) { |
| 302 |
e.preventDefault(); |
| 303 |
|
| 304 |
var is_other = jQuery('#updraftcentral_mothership_other').is(':checked') ? true : false; |
| 305 |
|
| 306 |
var key_description = jQuery('#updraftcentral_keycreate_description').val(); |
| 307 |
var key_size = jQuery('#updraftcentral_keycreate_keysize').val(); |
| 308 |
|
| 309 |
var where_send = '__updraftpluscom'; |
| 310 |
|
| 311 |
data = { |
| 312 |
key_description: key_description, |
| 313 |
key_size: key_size, |
| 314 |
}; |
| 315 |
|
| 316 |
if (is_other) { |
| 317 |
where_send = jQuery('#updraftcentral_keycreate_mothership').val(); |
| 318 |
if (where_send.substring(0, 4) != 'http') { |
| 319 |
alert(uclion.enter_mothership_url); |
| 320 |
return; |
| 321 |
} |
| 322 |
} |
| 323 |
|
| 324 |
data.mothership_firewalled = jQuery('#updraftcentral_keycreate_mothership_firewalled').is(':checked') ? 1 : 0; |
| 325 |
data.where_send = where_send; |
| 326 |
|
| 327 |
jQuery('.create_key_container').hide(); |
| 328 |
jQuery('.updraftcentral_wizard_stage1').show(); |
| 329 |
jQuery('.updraftcentral_wizard_stage2').hide(); |
| 330 |
|
| 331 |
jQuery('#updraftcentral_keys').block({ message: '<div style="margin: 8px; font-size:150%;"><img src="'+uclion.central_url+'/images/udlogo-rotating.gif" height="80" width="80" style="padding-bottom:10px;"><br>'+uclion.creating_please_allow+'</div>'}); |
| 332 |
|
| 333 |
try { |
| 334 |
updraftcentral_send_command('create_key', data, function(resp) { |
| 335 |
jQuery('#updraftcentral_keys').unblock(); |
| 336 |
try { |
| 337 |
if (resp.hasOwnProperty('error')) { |
| 338 |
alert(resp.error); |
| 339 |
console.log(resp); |
| 340 |
return; |
| 341 |
} |
| 342 |
alert(resp.r); |
| 343 |
|
| 344 |
if (resp.hasOwnProperty('bundle') && resp.hasOwnProperty('keys_guide')) { |
| 345 |
jQuery('#updraftcentral_keys_content').html(resp.keys_guide); |
| 346 |
jQuery('#updraftcentral_keys_content').append('<div class="updraftcentral_wizard_success">'+resp.r+'<br><textarea onclick="this.select();" style="width:620px; height:165px; word-wrap:break-word; border: 1px solid #aaa; border-radius: 3px; padding:4px;">'+resp.bundle+'</textarea></div>'); |
| 347 |
} else { |
| 348 |
console.log(resp); |
| 349 |
} |
| 350 |
|
| 351 |
if (resp.hasOwnProperty('keys_table')) { |
| 352 |
jQuery('#updraftcentral_keys_content').append(resp.keys_table); |
| 353 |
} |
| 354 |
|
| 355 |
jQuery('#updraftcentral_wizard_go').show(); |
| 356 |
|
| 357 |
} catch (err) { |
| 358 |
alert(uclion.unexpectedresponse+' '+response); |
| 359 |
console.log(err); |
| 360 |
} |
| 361 |
}, { error_callback: function(response, status, error_code, resp) { |
| 362 |
jQuery('#updraftcentral_keys').unblock(); |
| 363 |
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) { |
| 364 |
console.error(resp.fatal_error_message); |
| 365 |
alert(resp.fatal_error_message); |
| 366 |
} else { |
| 367 |
var error_message = "updraftcentral_send_command: error: "+status+" ("+error_code+")"; |
| 368 |
console.log(error_message); |
| 369 |
alert(error_message); |
| 370 |
console.log(response); |
| 371 |
} |
| 372 |
} |
| 373 |
}); |
| 374 |
} catch (err) { |
| 375 |
jQuery('#updraft_central_key').html(); |
| 376 |
console.log(err); |
| 377 |
} |
| 378 |
}); |
| 379 |
|
| 380 |
jQuery('#updraftcentral_keys').on('click', '.updraftcentral_key_delete', function(e) { |
| 381 |
e.preventDefault(); |
| 382 |
var key_id = jQuery(this).data('key_id'); |
| 383 |
if ('undefined' == typeof key_id) { |
| 384 |
console.log("UpdraftPlus: .updraftcentral_key_delete clicked, but no key ID found"); |
| 385 |
return; |
| 386 |
} |
| 387 |
|
| 388 |
jQuery('#updraftcentral_keys').block({ message: '<div style="margin: 8px; font-size:150%;"><img src="'+uclion.central_url+'/images/udlogo-rotating.gif" height="80" width="80" style="padding-bottom:10px;"><br>'+uclion.deleting+'</div>'}); |
| 389 |
|
| 390 |
updraftcentral_send_command('delete_key', { key_id: key_id }, function(response) { |
| 391 |
jQuery('#updraftcentral_keys').unblock(); |
| 392 |
if (response.hasOwnProperty('keys_table')) { |
| 393 |
jQuery('#updraftcentral_keys_content').html(response.keys_table); |
| 394 |
} |
| 395 |
}, { error_callback: function(response, status, error_code, resp) { |
| 396 |
jQuery('#updraftcentral_keys').unblock(); |
| 397 |
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) { |
| 398 |
console.error(resp.fatal_error_message); |
| 399 |
alert(resp.fatal_error_message); |
| 400 |
} else { |
| 401 |
var error_message = "updraftcentral_send_command: error: "+status+" ("+error_code+")"; |
| 402 |
console.log(error_message); |
| 403 |
alert(error_message); |
| 404 |
console.log(response); |
| 405 |
} |
| 406 |
} |
| 407 |
}); |
| 408 |
}); |
| 409 |
}); |