PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / js / toolsMigratePlugin.js

toolsMigratePlugin.js in 404 Solution trunk, at includes/js/toolsMigratePlugin.js

146 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Tools page: "Migrate from Another Plugin" two-step preview/confirm flow.
3 *
4 * Reads ajaxUrl, nonce, and translatable messages from a JSON blob on the
5 * configuration carrier element's `data-abj404-migrate` attribute
6 * (#abj404-migrate-config). Click "Preview Import" -> AJAX preview ->
7 * show count + Confirm Import (or "Nothing to import" + Back).
8 */
9 (function () {
10 'use strict';
11
12 // Migration preview is a one-shot admin action (reads the source
13 // plugin's tables and counts rows to migrate). A generous bound avoids
14 // false-positive aborts on a slow-but-working host while still
15 // guaranteeing the spinner/disabled state cannot hang forever if
16 // admin-ajax never responds (M501).
17 var PREVIEW_TIMEOUT_MS = 30000;
18
19 // Returns the translated config-unusable message when wp.i18n's locale
20 // data is available, else the English fallback. The literal must be
21 // passed directly to wp.i18n.__() (not through a variable) so make-pot
22 // can statically extract it; mirrors the switch-dispatched literal
23 // pattern in support-request-modal-view.js's t(key) for the same
24 // situation: a user-facing string needed on a failure path where the
25 // normal PHP-supplied, pre-translated config (cfg) is unavailable.
26 function configUnusableMessage() {
27 if (window.wp && window.wp.i18n && typeof window.wp.i18n.__ === 'function') {
28 return window.wp.i18n.__('Could not load the migration tool configuration. Reload this page and try again.', '404-solution');
29 }
30 return 'Could not load the migration tool configuration. Reload this page and try again.';
31 }
32
33 function readConfig() {
34 var el = document.getElementById('abj404-migrate-config');
35 if (!el) { return null; }
36 var raw = el.getAttribute('data-abj404-migrate');
37 if (!raw) { return null; }
38 try {
39 return JSON.parse(raw);
40 } catch (e) {
41 // Malformed config JSON leaves ajaxUrl/nonce/messages entirely
42 // unavailable, so Preview Import cannot function at all. Log for
43 // diagnosis; the caller disables the control with a safe,
44 // non-cfg-dependent message instead of wiring a click handler
45 // that would silently do nothing.
46 if (window.console && window.console.error) {
47 window.console.error('404 Solution: abj404-migrate-config data-abj404-migrate attribute is not valid JSON', raw, e);
48 }
49 return null;
50 }
51 }
52
53 var cfg = null;
54
55 function showStep1() {
56 document.getElementById('abj404-migrate-step1').style.display = '';
57 document.getElementById('abj404-migrate-step2').style.display = 'none';
58 }
59
60 function showStep2(count, source, label) {
61 document.getElementById('abj404-migrate-step1').style.display = 'none';
62 document.getElementById('abj404-migrate-step2').style.display = '';
63 var msgEl = document.getElementById('abj404-migrate-preview-msg');
64 var form = document.getElementById('abj404-migrate-confirm-form');
65 var noForm = document.getElementById('abj404-migrate-back-noform');
66 if (count > 0) {
67 msgEl.textContent = cfg.msgFound.replace('%d', count).replace('%s', label);
68 document.getElementById('abj404-migrate-confirm-source').value = source;
69 form.style.display = '';
70 noForm.style.display = 'none';
71 } else {
72 msgEl.textContent = cfg.msgNone.replace('%s', label);
73 form.style.display = 'none';
74 noForm.style.display = '';
75 }
76 }
77
78 function showError() {
79 document.getElementById('abj404-migrate-step1').style.display = 'none';
80 document.getElementById('abj404-migrate-step2').style.display = '';
81 document.getElementById('abj404-migrate-preview-msg').textContent = cfg.msgError;
82 document.getElementById('abj404-migrate-confirm-form').style.display = 'none';
83 document.getElementById('abj404-migrate-back-noform').style.display = '';
84 }
85
86 document.addEventListener('DOMContentLoaded', function () {
87 var previewBtn = document.getElementById('abj404-migrate-preview-btn');
88 cfg = readConfig();
89 if (!cfg) {
90 // Config is unusable (missing feature, or the catch branch in
91 // readConfig() already logged a parse failure): the preview
92 // button has nowhere to send its AJAX request, so disable it
93 // with an explanation instead of leaving a click handler
94 // silently never wired.
95 if (previewBtn) {
96 previewBtn.disabled = true;
97 previewBtn.title = configUnusableMessage();
98 }
99 return;
100 }
101
102 var backBtn = document.getElementById('abj404-migrate-back-btn');
103 var backBtn2 = document.getElementById('abj404-migrate-back-btn2');
104 if (previewBtn) {
105 previewBtn.addEventListener('click', function () {
106 var select = document.getElementById('abj404-import-source');
107 var source = select ? select.value : '';
108 if (!source) { return; }
109 var spinner = document.getElementById('abj404-migrate-preview-spinner');
110 if (spinner) { spinner.style.display = ''; }
111 previewBtn.disabled = true;
112 var fd = new FormData();
113 fd.append('action', 'abj404_crossPluginPreview');
114 fd.append('nonce', cfg.nonce);
115 fd.append('import_source', source);
116 // A stalled admin-ajax response must not leave the button
117 // disabled and the spinner visible forever (M501); abort and
118 // let the existing generic .catch() below show msgError,
119 // exactly as it already does for any other fetch failure.
120 var controller = new AbortController();
121 var timeoutId = setTimeout(function () { controller.abort(); }, PREVIEW_TIMEOUT_MS);
122 // ajax-direct-approved: cross-plugin migration preview posts FormData and manages the two-step UI state locally.
123 fetch(cfg.ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin', signal: controller.signal }) // allow-direct-network: cross-plugin migration preview; no project-wide adapter exists for this AJAX surface
124 .then(function (r) { clearTimeout(timeoutId); return r.json(); })
125 .then(function (resp) {
126 if (spinner) { spinner.style.display = 'none'; }
127 previewBtn.disabled = false;
128 if (resp && resp.success && resp.data) {
129 showStep2(parseInt(resp.data.count, 10) || 0, resp.data.source, resp.data.label);
130 } else {
131 showError();
132 }
133 })
134 .catch(function () {
135 clearTimeout(timeoutId);
136 if (spinner) { spinner.style.display = 'none'; }
137 previewBtn.disabled = false;
138 showError();
139 });
140 });
141 }
142 if (backBtn) { backBtn.addEventListener('click', function () { showStep1(); }); }
143 if (backBtn2) { backBtn2.addEventListener('click', function () { showStep1(); }); }
144 });
145 })();
146