PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / js / profile / wp2fa-profile-backup-codes.js
wp-2fa / js / profile Last commit date
wp2fa-profile-backup-codes.js 3 days ago wp2fa-profile.js 3 days ago
wp2fa-profile-backup-codes.js
297 lines
1 /**
2 * WP 2FA - Profile Backup Codes
3 *
4 * Handles the "Generate list of backup codes" button on the profile page.
5 * Generates codes via AJAX, then displays them in a dialog with
6 * Copy, Print, Send via email actions.
7 *
8 * @package wp2fa
9 * @since 4.0.0
10 */
11 ( function () {
12 'use strict';
13
14 var DIALOG_ID = 'wp2fa-profile-backup-codes-dialog';
15
16 function getAjaxUrl() {
17 if ( typeof wp2faProfileData !== 'undefined' && wp2faProfileData.ajaxUrl ) {
18 return wp2faProfileData.ajaxUrl;
19 }
20 return '/wp-admin/admin-ajax.php';
21 }
22
23 function getI18n() {
24 if ( typeof wp2faProfileData !== 'undefined' && wp2faProfileData.i18n ) {
25 return wp2faProfileData.i18n;
26 }
27 return {};
28 }
29
30 /* -------------------------------------------------------
31 * Dialog show / hide
32 * ------------------------------------------------------- */
33 function showDialog() {
34 var dialog = document.getElementById( DIALOG_ID );
35 if ( dialog ) {
36 dialog.style.display = '';
37 dialog.classList.add( 'wp2fa-profile__confirm--open' );
38 document.addEventListener( 'keydown', onEscClose );
39 }
40 }
41
42 function hideDialog() {
43 var dialog = document.getElementById( DIALOG_ID );
44 if ( dialog ) {
45 dialog.style.display = 'none';
46 dialog.classList.remove( 'wp2fa-profile__confirm--open' );
47 document.removeEventListener( 'keydown', onEscClose );
48 }
49 }
50
51 function onEscClose( e ) {
52 if ( e.key === 'Escape' ) {
53 hideDialog();
54 window.location.reload();
55 }
56 }
57
58 function showStatus( message, isError ) {
59 var el = document.getElementById( 'wp2fa-profile-backup-codes-status' );
60 if ( el ) {
61 el.textContent = message;
62 el.style.display = '';
63 el.style.color = isError ? '#d63638' : '#00a32a';
64 el.style.marginTop = '10px';
65 }
66 }
67
68 /* -------------------------------------------------------
69 * Generate codes via AJAX and populate dialog
70 * ------------------------------------------------------- */
71 function generateAndShowCodes( btn ) {
72 var nonce = btn.getAttribute( 'data-nonce' );
73 var ajaxUrl = getAjaxUrl();
74
75 btn.disabled = true;
76 btn.textContent = btn.textContent.trim() + '';
77
78 var formData = new FormData();
79 formData.append( 'action', 'wp2fa_run_ajax_generate_json' );
80 formData.append( 'nonce', nonce );
81
82 fetch( ajaxUrl, {
83 method: 'POST',
84 credentials: 'same-origin',
85 body: formData
86 } )
87 .then( function ( res ) { return res.json(); } )
88 .then( function ( data ) {
89 btn.disabled = false;
90 btn.textContent = btn.textContent.replace( /$/, '' );
91
92 if ( data.success && data.data && data.data.codes ) {
93 populateDialog( data.data.codes );
94 showDialog();
95 } else {
96 var errMsg = ( data.data && typeof data.data === 'string' )
97 ? data.data
98 : 'Failed to generate backup codes.';
99 alert( errMsg ); // eslint-disable-line no-alert
100 }
101 } )
102 .catch( function () {
103 btn.disabled = false;
104 btn.textContent = btn.textContent.replace( /$/, '' );
105 alert( 'Network error. Please try again.' ); // eslint-disable-line no-alert
106 } );
107 }
108
109 /**
110 * Populate the dialog textarea with formatted codes.
111 *
112 * @param {Array} codes Array of code strings.
113 */
114 function populateDialog( codes ) {
115 var textarea = document.getElementById( 'wp2fa-profile-backup-codes-textarea' );
116 if ( ! textarea ) {
117 return;
118 }
119 var lines = [];
120 for ( var i = 0; i < codes.length; i++ ) {
121 lines.push( ( i + 1 ) + ': ' + codes[ i ] );
122 }
123 textarea.value = lines.join( '\n' );
124 textarea.rows = Math.max( codes.length, 4 );
125
126 // Store codes for email sending.
127 textarea.setAttribute( 'data-raw-codes', JSON.stringify( codes ) );
128
129 // Reset status.
130 var status = document.getElementById( 'wp2fa-profile-backup-codes-status' );
131 if ( status ) {
132 status.style.display = 'none';
133 status.textContent = '';
134 }
135 }
136
137 /* -------------------------------------------------------
138 * Copy codes to clipboard
139 * ------------------------------------------------------- */
140 function copyCodes() {
141 var textarea = document.getElementById( 'wp2fa-profile-backup-codes-textarea' );
142 if ( ! textarea ) {
143 return;
144 }
145 var text = textarea.value;
146 var i18n = getI18n();
147
148 if ( navigator.clipboard && window.isSecureContext ) {
149 navigator.clipboard.writeText( text ).then( function () {
150 showCopyFeedback();
151 } );
152 } else {
153 textarea.select();
154 document.execCommand( 'copy' );
155 showCopyFeedback();
156 }
157
158 function showCopyFeedback() {
159 var copyBtn = document.getElementById( 'wp2fa-profile-backup-codes-copy' );
160 if ( copyBtn ) {
161 var original = copyBtn.textContent;
162 copyBtn.textContent = i18n.copied || 'Copied!';
163 setTimeout( function () {
164 copyBtn.textContent = original;
165 }, 2000 );
166 }
167 }
168 }
169
170 /* -------------------------------------------------------
171 * Print codes
172 * ------------------------------------------------------- */
173 function printCodes() {
174 var textarea = document.getElementById( 'wp2fa-profile-backup-codes-textarea' );
175 if ( ! textarea ) {
176 return;
177 }
178 var printWindow = window.open( '', '_blank' );
179 if ( printWindow ) {
180 var safeText = textarea.value.replace( /&/g, '&amp;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' ).replace( /"/g, '&quot;' );
181 printWindow.document.write( '<html><head><title>Backup Codes</title></head><body>' );
182 printWindow.document.write( '<h1>Backup Codes</h1>' );
183 printWindow.document.write( '<pre>' + safeText + '</pre>' );
184 printWindow.document.write( '</body></html>' );
185 printWindow.document.close();
186 printWindow.print();
187 }
188 }
189
190 /* -------------------------------------------------------
191 * Send codes via email
192 * ------------------------------------------------------- */
193 function sendCodesViaEmail( emailBtn ) {
194 var textarea = document.getElementById( 'wp2fa-profile-backup-codes-textarea' );
195 if ( ! textarea ) {
196 return;
197 }
198 var nonce = emailBtn.getAttribute( 'data-nonce' );
199 var codesText = textarea.value;
200 var ajaxUrl = getAjaxUrl();
201
202 emailBtn.disabled = true;
203
204 var formData = new FormData();
205 formData.append( 'action', 'send_backup_codes_email' );
206 formData.append( '_wpnonce', nonce );
207 formData.append( 'codes', JSON.stringify( codesText ) );
208
209 fetch( ajaxUrl, {
210 method: 'POST',
211 credentials: 'same-origin',
212 body: formData
213 } )
214 .then( function ( res ) { return res.json(); } )
215 .then( function ( data ) {
216 emailBtn.disabled = false;
217 if ( data.success ) {
218 showStatus( getI18n().backupCodesEmailSent || 'Codes sent to your email.', false );
219 } else {
220 showStatus( getI18n().backupCodesEmailError || 'Failed to send email.', true );
221 }
222 } )
223 .catch( function () {
224 emailBtn.disabled = false;
225 showStatus( getI18n().networkError || 'Network error. Please try again.', true );
226 } );
227 }
228
229 /* -------------------------------------------------------
230 * Init
231 * ------------------------------------------------------- */
232 function init() {
233 var container = document.getElementById( 'wp2fa-profile-section' );
234 if ( ! container ) {
235 return;
236 }
237
238 // Intercept the "Generate list of backup codes" button.
239 container.addEventListener( 'click', function ( e ) {
240 var btn = e.target.closest( '[data-wp2fa-generate-backup-codes]' );
241 if ( ! btn ) {
242 return;
243 }
244 e.preventDefault();
245 e.stopImmediatePropagation();
246 generateAndShowCodes( btn );
247 } );
248
249 // Dialog: Copy.
250 var copyBtn = document.getElementById( 'wp2fa-profile-backup-codes-copy' );
251 if ( copyBtn ) {
252 copyBtn.addEventListener( 'click', copyCodes );
253 }
254
255 // Dialog: Print.
256 var printBtn = document.getElementById( 'wp2fa-profile-backup-codes-print' );
257 if ( printBtn ) {
258 printBtn.addEventListener( 'click', printCodes );
259 }
260
261 // Dialog: Send via email.
262 var emailBtn = document.getElementById( 'wp2fa-profile-backup-codes-email' );
263 if ( emailBtn ) {
264 emailBtn.addEventListener( 'click', function () {
265 sendCodesViaEmail( emailBtn );
266 } );
267 }
268
269 // Dialog: Close.
270 var closeLink = document.getElementById( 'wp2fa-profile-backup-codes-close' );
271 if ( closeLink ) {
272 closeLink.addEventListener( 'click', function ( e ) {
273 e.preventDefault();
274 hideDialog();
275 window.location.reload();
276 } );
277 }
278
279 // Click overlay backdrop to close.
280 var dialog = document.getElementById( DIALOG_ID );
281 if ( dialog ) {
282 dialog.addEventListener( 'click', function ( e ) {
283 if ( e.target === dialog ) {
284 hideDialog();
285 window.location.reload();
286 }
287 } );
288 }
289 }
290
291 if ( document.readyState === 'loading' ) {
292 document.addEventListener( 'DOMContentLoaded', init );
293 } else {
294 init();
295 }
296 }() );
297