PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / trunk
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses vtrunk
4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 4.2.1 All 138 releases
learnpress / assets / src / js / admin / webhooks.js

webhooks.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses trunk, at assets/src/js/admin/webhooks.js

548 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import SweetAlert from 'sweetalert2';
2 import * as lpUtils from '../utils.js';
3
4 ( function() {
5 'use strict';
6
7 const cfg = window.lpWebhooksSettings || {};
8 if ( ! cfg.is_webhook_section ) {
9 return;
10 }
11
12 const ajaxHandle = window.lpAJAXG;
13 if ( ! ajaxHandle || typeof ajaxHandle.fetchAJAX !== 'function' ) {
14 return;
15 }
16
17 const actions = cfg.actions || {};
18 const i18n = cfg.i18n || {};
19 const elId = document.querySelector( '#lp-webhook-id' );
20 const elName = document.querySelector( '#lp-webhook-name' );
21 const elUrl = document.querySelector( '#lp-webhook-delivery-url' );
22 const elSecret = document.querySelector( '#lp-webhook-secret' );
23 const elWebhookStatus = document.querySelector( '#lp-webhook-status' );
24 const elEvents = document.querySelector( '#lp-webhook-events' );
25 const elSubmit = document.querySelector( '#lp-webhook-submit' );
26 const elCancel = document.querySelector( '#lp-webhook-cancel' );
27 const elRegenerate = document.querySelector( '#lp-webhook-regenerate-editor' );
28 const elEditorTitle = document.querySelector( '#lp-webhook-editor-title' );
29 const elEditorHolder = document.querySelector( '#lp-webhook-editor-holder' );
30 const elEditor = document.querySelector( '#lp-webhook-editor' );
31 const elEditorFields = document.querySelector( '#lp-webhook-editor-fields' );
32 const elEditorActions = document.querySelector( '#lp-webhook-editor-actions' );
33 const elStatusMessage = document.querySelector( '#lp-webhook-status-message' );
34 const elSecretReveal = document.querySelector( '#lp-webhook-secret-reveal' );
35 const elSecretValue = document.querySelector( '#lp-webhook-secret-value' );
36 let isEditorDisabled = true;
37
38 const getEventsTomSelect = () => {
39 return elEvents?.tomselect || elEvents?.tomSelectInstance || null;
40 };
41
42 const initEventsTomSelect = () => {
43 if ( ! elEvents || getEventsTomSelect() ) {
44 return;
45 }
46
47 if ( typeof window.lpFindTomSelect === 'function' ) {
48 window.lpFindTomSelect();
49 }
50 };
51
52 const setStatus = ( message = '', isError = false ) => {
53 if ( ! elStatusMessage ) {
54 return;
55 }
56
57 elStatusMessage.textContent = message;
58 elStatusMessage.style.color = isError ? '#b32d2e' : '#1e1e1e';
59 };
60
61 const setLoading = ( isLoading ) => {
62 if ( ! elSubmit ) {
63 return;
64 }
65
66 elSubmit.disabled = !! isLoading || isEditorDisabled;
67 elSubmit.classList.toggle( 'loading', !! isLoading );
68 };
69
70 const setEditorDisabled = ( isDisabled ) => {
71 isEditorDisabled = !! isDisabled;
72
73 [
74 elName,
75 elUrl,
76 elSecret,
77 elWebhookStatus,
78 elEvents,
79 elSubmit,
80 elCancel,
81 elRegenerate,
82 ].forEach( ( el ) => {
83 if ( ! el ) {
84 return;
85 }
86
87 el.disabled = isEditorDisabled;
88 } );
89
90 const eventsTomSelect = getEventsTomSelect();
91 if ( eventsTomSelect ) {
92 if ( isEditorDisabled && typeof eventsTomSelect.disable === 'function' ) {
93 eventsTomSelect.disable();
94 } else if ( typeof eventsTomSelect.enable === 'function' ) {
95 eventsTomSelect.enable();
96 }
97 }
98
99 if ( elSubmit?.classList.contains( 'loading' ) ) {
100 elSubmit.disabled = true;
101 }
102 };
103
104 const setSecretPlaceholder = ( isEditing = false ) => {
105 if ( ! elSecret ) {
106 return;
107 }
108
109 elSecret.placeholder = isEditing
110 ? i18n.secret_edit_placeholder || 'Leave blank to keep the current secret.'
111 : i18n.secret_create_placeholder || 'Leave blank to auto-generate a secret.';
112 };
113
114 const setEditorFormVisible = ( isVisible = true ) => {
115 if ( elEditorFields ) {
116 elEditorFields.style.display = isVisible ? '' : 'none';
117 }
118
119 if ( elEditorActions ) {
120 elEditorActions.style.display = '';
121 }
122
123 if ( ! isVisible ) {
124 [ elSubmit, elCancel, elRegenerate ].forEach( ( el ) => {
125 if ( el ) {
126 el.style.display = 'none';
127 }
128 } );
129 }
130 };
131
132 const hideSecret = () => {
133 if ( elSecretReveal ) {
134 elSecretReveal.style.display = 'none';
135 }
136 if ( elSecretValue ) {
137 elSecretValue.value = '';
138 }
139 };
140
141 const revealSecret = ( secret ) => {
142 if ( ! secret || ! elSecretReveal || ! elSecretValue ) {
143 return;
144 }
145
146 elSecretValue.value = secret;
147 elSecretReveal.style.display = 'block';
148 };
149
150 const openEditorPopup = () => {
151 if ( ! elEditor || ! elEditorHolder ) {
152 return;
153 }
154
155 SweetAlert.fire( {
156 html: '<div id="lp-webhook-editor-popup"></div>',
157 width: 860,
158 showConfirmButton: false,
159 showCloseButton: true,
160 showCancelButton: false,
161 focusConfirm: false,
162 customClass: {
163 popup: 'lp-webhook-editor-popup',
164 },
165 didOpen: () => {
166 const popup =
167 typeof SweetAlert.getPopup === 'function' ? SweetAlert.getPopup() : null;
168 const mount = popup?.querySelector( '#lp-webhook-editor-popup' );
169 if ( ! mount ) {
170 return;
171 }
172
173 elEditor.style.display = 'block';
174 mount.appendChild( elEditor );
175 initEventsTomSelect();
176 setEditorDisabled( false );
177 elName?.focus();
178 },
179 willClose: () => {
180 setEditorDisabled( true );
181 elEditor.style.display = 'none';
182 elEditorHolder.appendChild( elEditor );
183 },
184 } );
185 };
186
187 const closeEditorPopup = () => {
188 if ( typeof SweetAlert.close === 'function' ) {
189 SweetAlert.close();
190 }
191 };
192
193 const setSelectedEvents = ( eventKeys = [] ) => {
194 if ( ! elEvents ) {
195 return;
196 }
197
198 const eventsTomSelect = getEventsTomSelect();
199 if ( eventsTomSelect ) {
200 eventsTomSelect.setValue( eventKeys, true );
201 return;
202 }
203
204 elEvents.querySelectorAll( 'option' ).forEach( ( option ) => {
205 option.selected = eventKeys.includes( option.value );
206 } );
207 };
208
209 const getSelectedEvents = () => {
210 if ( ! elEvents ) {
211 return [];
212 }
213
214 const eventsTomSelect = getEventsTomSelect();
215 if ( eventsTomSelect ) {
216 const value = eventsTomSelect.getValue();
217
218 return Array.isArray( value ) ? value : [ value ].filter( Boolean );
219 }
220
221 return Array.from(
222 elEvents.querySelectorAll( 'option:checked' )
223 ).map( ( option ) => option.value );
224 };
225
226 const resetEditor = () => {
227 if ( elId ) {
228 elId.value = '0';
229 }
230 if ( elName ) {
231 elName.value = '';
232 }
233 if ( elUrl ) {
234 elUrl.value = '';
235 }
236 if ( elSecret ) {
237 elSecret.value = '';
238 }
239 setSecretPlaceholder();
240 if ( elWebhookStatus ) {
241 elWebhookStatus.value = 'active';
242 }
243 if ( elEditorTitle ) {
244 elEditorTitle.textContent = i18n.create_title || 'Create Webhook';
245 }
246 if ( elSubmit ) {
247 elSubmit.textContent = i18n.create_button || 'Create Webhook';
248 elSubmit.style.display = '';
249 }
250 if ( elCancel ) {
251 elCancel.style.display = 'none';
252 }
253 if ( elRegenerate ) {
254 elRegenerate.style.display = 'none';
255 }
256
257 setEditorFormVisible();
258 setSelectedEvents();
259 hideSecret();
260 setStatus();
261 };
262
263 const populateEditor = ( webhook ) => {
264 if ( ! webhook ) {
265 return;
266 }
267
268 if ( elId ) {
269 elId.value = webhook.webhook_id || 0;
270 }
271 if ( elName ) {
272 elName.value = webhook.name || '';
273 }
274 if ( elUrl ) {
275 elUrl.value = webhook.delivery_url || '';
276 }
277 if ( elSecret ) {
278 elSecret.value = '';
279 }
280 setSecretPlaceholder( true );
281 if ( elWebhookStatus ) {
282 elWebhookStatus.value = webhook.status || 'active';
283 }
284 if ( elEditorTitle ) {
285 elEditorTitle.textContent = i18n.edit_title || 'Edit Webhook';
286 }
287 if ( elSubmit ) {
288 elSubmit.textContent = i18n.update_button || 'Update Webhook';
289 elSubmit.style.display = '';
290 }
291 if ( elCancel ) {
292 elCancel.style.display = '';
293 }
294 if ( elRegenerate ) {
295 elRegenerate.style.display = '';
296 }
297
298 setEditorFormVisible();
299 setSelectedEvents( Array.isArray( webhook.events ) ? webhook.events : [] );
300 hideSecret();
301 setStatus();
302 openEditorPopup();
303 };
304
305 const runRequest = ( dataSend, callbacks = {} ) => {
306 ajaxHandle.fetchAJAX( dataSend, {
307 success: ( response ) => {
308 if ( typeof callbacks.success === 'function' ) {
309 callbacks.success( response );
310 }
311 },
312 error: ( error ) => {
313 if ( typeof callbacks.error === 'function' ) {
314 callbacks.error( error );
315 }
316 },
317 completed: () => {
318 if ( typeof callbacks.completed === 'function' ) {
319 callbacks.completed();
320 }
321 },
322 } );
323 };
324
325 const refreshList = async () => {
326 const currentList = document.querySelector( '.lp-webhook-list' );
327 if ( ! currentList ) {
328 return;
329 }
330
331 try {
332 const response = await fetch( window.location.href, {
333 method: 'GET',
334 credentials: 'same-origin',
335 cache: 'no-store',
336 } );
337 if ( ! response.ok ) {
338 return;
339 }
340
341 const html = await response.text();
342 const doc = new DOMParser().parseFromString( html, 'text/html' );
343 const newList = doc.querySelector( '.lp-webhook-list' );
344 if ( newList ) {
345 currentList.replaceWith( newList );
346 }
347 } catch {
348 // Keep the current table when refresh fails.
349 }
350 };
351
352 const onSubmit = () => {
353 if ( ! elSubmit || ! elName || ! elUrl ) {
354 return;
355 }
356
357 if ( ! elName.reportValidity() || ! elUrl.reportValidity() ) {
358 return;
359 }
360
361 const webhookId = elId ? parseInt( elId.value, 10 ) || 0 : 0;
362 const isUpdate = webhookId > 0;
363 const dataSend = {
364 action: isUpdate ? actions.update || 'update_webhook' : actions.create || 'create_webhook',
365 webhook_id: webhookId,
366 name: elName.value,
367 delivery_url: elUrl.value,
368 secret: elSecret ? elSecret.value : '',
369 status: elWebhookStatus ? elWebhookStatus.value : 'active',
370 events: getSelectedEvents(),
371 };
372
373 setLoading( true );
374 setStatus( i18n.processing || 'Processing...' );
375
376 runRequest( dataSend, {
377 success: ( response ) => {
378 const message = response?.message || i18n.request_failed || 'Request failed.';
379 if ( response?.status !== 'success' ) {
380 setStatus( message, true );
381 return;
382 }
383
384 refreshList();
385
386 if ( isUpdate ) {
387 closeEditorPopup();
388 resetEditor();
389 return;
390 }
391
392 const createdSecret = response?.data?.webhook?.secret || '';
393 resetEditor();
394 if ( elEditorTitle ) {
395 elEditorTitle.textContent = i18n.created_title || 'Webhook Created';
396 }
397 setEditorFormVisible( false );
398 revealSecret( createdSecret );
399 setStatus( message );
400 setEditorDisabled( true );
401 },
402 error: () => setStatus( i18n.request_failed || 'Request failed.', true ),
403 completed: () => setLoading( false ),
404 } );
405 };
406
407 const onDelete = ( webhookId ) => {
408 if ( ! webhookId || ! window.confirm( i18n.confirm_delete || 'Delete this webhook?' ) ) {
409 return;
410 }
411
412 setStatus( i18n.processing || 'Processing...' );
413 runRequest(
414 {
415 action: actions.delete || 'delete_webhook',
416 webhook_id: webhookId,
417 },
418 {
419 success: ( response ) => {
420 const message = response?.message || i18n.request_failed || 'Request failed.';
421 setStatus( message, response?.status !== 'success' );
422 if ( response?.status === 'success' ) {
423 if ( elId && parseInt( elId.value, 10 ) === webhookId ) {
424 resetEditor();
425 }
426 refreshList();
427 }
428 },
429 error: () => setStatus( i18n.request_failed || 'Request failed.', true ),
430 }
431 );
432 };
433
434 const onRegenerate = ( webhookId ) => {
435 if ( ! webhookId || ! window.confirm( i18n.confirm_regenerate || 'Regenerate this webhook secret?' ) ) {
436 return;
437 }
438
439 setStatus( i18n.processing || 'Processing...' );
440 runRequest(
441 {
442 action: actions.regenerate || 'regenerate_webhook_secret',
443 webhook_id: webhookId,
444 },
445 {
446 success: ( response ) => {
447 const message = response?.message || i18n.request_failed || 'Request failed.';
448 setStatus( message, response?.status !== 'success' );
449 if ( response?.status === 'success' ) {
450 revealSecret( response?.data?.secret || '' );
451 refreshList();
452 }
453 },
454 error: () => setStatus( i18n.request_failed || 'Request failed.', true ),
455 }
456 );
457 };
458
459 const onCopySecret = async () => {
460 if ( ! elSecretValue ) {
461 return;
462 }
463
464 try {
465 if ( navigator.clipboard?.writeText ) {
466 await navigator.clipboard.writeText( elSecretValue.value );
467 } else {
468 elSecretValue.select();
469 document.execCommand( 'copy' );
470 }
471 setStatus( i18n.copy_success || 'Copied.' );
472 } catch {
473 setStatus( i18n.copy_fallback || 'Copy this value manually.' );
474 }
475 };
476
477 lpUtils.eventHandlers( 'click', [
478 {
479 selector: '#lp-webhook-open-create',
480 callBack: ( args ) => {
481 const { e } = args;
482 e.preventDefault();
483 resetEditor();
484 openEditorPopup();
485 },
486 },
487 {
488 selector: '.lp-webhook-edit',
489 callBack: ( args ) => {
490 const { e, target } = args;
491 e.preventDefault();
492 const edit = target.closest( '.lp-webhook-edit' );
493 try {
494 populateEditor( JSON.parse( edit.dataset.webhook || '{}' ) );
495 } catch {
496 setStatus( i18n.request_failed || 'Request failed.', true );
497 }
498 },
499 },
500 {
501 selector: '.lp-webhook-delete',
502 callBack: ( args ) => {
503 const { e, target } = args;
504 e.preventDefault();
505 const deleteLink = target.closest( '.lp-webhook-delete' );
506 onDelete( parseInt( deleteLink.dataset.webhookId, 10 ) || 0 );
507 },
508 },
509 {
510 selector: '.lp-webhook-regenerate',
511 callBack: ( args ) => {
512 const { e, target } = args;
513 e.preventDefault();
514 const regenerateLink = target.closest( '.lp-webhook-regenerate' );
515 onRegenerate( parseInt( regenerateLink.dataset.webhookId, 10 ) || 0 );
516 },
517 },
518 {
519 selector: '#lp-webhook-submit',
520 callBack: () => {
521 onSubmit();
522 },
523 },
524 {
525 selector: '#lp-webhook-cancel',
526 callBack: () => {
527 resetEditor();
528 closeEditorPopup();
529 },
530 },
531 {
532 selector: '#lp-webhook-regenerate-editor',
533 callBack: () => {
534 onRegenerate( elId ? parseInt( elId.value, 10 ) || 0 : 0 );
535 },
536 },
537 {
538 selector: '#lp-webhook-copy-secret',
539 callBack: () => {
540 onCopySecret();
541 },
542 },
543 ] );
544
545 setSecretPlaceholder();
546 setEditorDisabled( true );
547 } )();
548