Files
SIGE-WEB-snapshot/wp-content/plugins/wp-security-audit-log/classes/FeedbackForm/feedback-form.js

306 lines
7.5 KiB
JavaScript

/**
* JavaScript for feedback form in wp-admin/plugins
*
* @since 5.6.2
*/
(function () {
const { __ } = wp.i18n;
const deactivationPopover = document.getElementById(
'wp-security-audit-log-popover'
);
if (!deactivationPopover) {
return;
}
let deactivateButton = null;
const feedbackFieldSelector = 'textarea, input[type="text"]';
const reasonValidationMessage = deactivationPopover.querySelector(
'.wsal-reason-validation'
);
const feedbackValidationMessage = deactivationPopover.querySelector(
'.wsal-feedback-validation'
);
/**
* Normalize free-text feedback before validation or submission.
*
* @param {string} feedbackValue The feedback field value.
* @param {boolean} trimValue Whether to trim whitespace.
*
* @since 5.6.3
*/
const sanitizeFeedbackValue = (feedbackValue, trimValue = true) => {
const sanitizedFeedbackValue = String(feedbackValue || '')
.replace(/&(?:lt|gt|#60|#62|#x3c|#x3e);/gi, '')
.replace(/[<>]/g, '');
return trimValue ? sanitizedFeedbackValue.trim() : sanitizedFeedbackValue;
};
/**
* Show a validation message, or hide all validation messages.
*
* @param {HTMLElement|null} validationElement The message element to show.
*
* @since 5.6.3
*/
const toggleValidationMessage = (validationElement = null) => {
[reasonValidationMessage, feedbackValidationMessage].forEach(
(messageElement) => {
if (messageElement) {
messageElement.hidden = messageElement !== validationElement;
}
}
);
};
/**
* Toggle the selected feedback field and reset the others.
*
* @param {HTMLElement|null} reasonWrapper The selected reason wrapper.
*
* @since 5.6.3
*/
const toggleFeedbackFields = (reasonWrapper = null) => {
deactivationPopover
.querySelectorAll('.wsal-feedback-wrapper')
.forEach((feedbackWrapper) => {
feedbackWrapper.style.display = 'none';
const feedbackField = feedbackWrapper.querySelector(
feedbackFieldSelector
);
if (feedbackField) {
feedbackField.value = '';
}
});
if (!reasonWrapper) {
return;
}
const feedbackWrapper = reasonWrapper.querySelector(
'.wsal-feedback-wrapper'
);
if (feedbackWrapper) {
feedbackWrapper.style.display = 'block';
}
};
/**
* Get the selected reason data from the popover.
*
* @return {Object|null} The selected reason data or null.
*
* @since 5.6.3
*/
const getSelectedReasonData = () => {
const checkedRadio = deactivationPopover.querySelector(
'input[name="reason"]:checked'
);
if (!checkedRadio) {
return null;
}
const reasonWrapper = checkedRadio.closest('.wsal-reason-wrapper');
const feedbackField = reasonWrapper
? reasonWrapper.querySelector(feedbackFieldSelector)
: null;
const feedbackValue = feedbackField
? sanitizeFeedbackValue(feedbackField.value)
: '';
if (feedbackField) {
feedbackField.value = feedbackValue;
}
return {
feedbackField: feedbackField,
feedbackValue: feedbackValue,
reason: checkedRadio.value
};
};
/**
* Send a POST request to the remote server.
*
* @since 5.6.2
*/
const sendRequest = async (url, data) => {
const formData = new FormData();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
const response = await fetch(url, { method: 'POST', body: formData });
try {
return await response.json();
} catch (error) {
return {};
}
};
// Intercept at document level in capture phase runs before any other handler.
document.addEventListener(
'click',
(clickEvent) => {
const target = clickEvent.target.closest('span.deactivate a');
if (!target) {
return;
}
// Check if this is the WSAL deactivate link, if not return early.
if (!target.href || target.href.indexOf('wp-security-audit-log') === -1) {
return;
}
deactivateButton = target;
clickEvent.preventDefault();
clickEvent.stopPropagation();
clickEvent.stopImmediatePropagation();
toggleValidationMessage();
deactivationPopover.showPopover();
},
true
);
// Show/hide the feedback fields based on the selected reason.
deactivationPopover.addEventListener('click', (clickEvent) => {
const reasonWrapper = clickEvent.target.closest('.wsal-reason-wrapper');
if (!reasonWrapper || !deactivationPopover.contains(reasonWrapper)) {
return;
}
const reasonRadio = reasonWrapper.querySelector('input[type="radio"]');
toggleValidationMessage();
if (!reasonRadio || reasonRadio.checked) {
return;
}
reasonRadio.checked = true;
toggleFeedbackFields(reasonWrapper);
});
deactivationPopover.addEventListener('change', (changeEvent) => {
if (!changeEvent.target.matches('input[name="reason"]')) {
return;
}
toggleValidationMessage();
toggleFeedbackFields(changeEvent.target.closest('.wsal-reason-wrapper'));
});
deactivationPopover.addEventListener('input', (inputEvent) => {
if (!inputEvent.target.matches(feedbackFieldSelector)) {
return;
}
inputEvent.target.value = sanitizeFeedbackValue(
inputEvent.target.value,
false
);
toggleValidationMessage();
});
// Handle clicking on the dismiss button skip feedback, deactivate directly.
deactivationPopover
.querySelector('button.wsal-dismiss')
.addEventListener('click', (dismissEvent) => {
dismissEvent.preventDefault();
if (deactivateButton) {
window.location.href = deactivateButton.href;
}
});
// Handle clicking on the close button to dismiss the popover.
deactivationPopover
.querySelector('button.wsal-close-button')
.addEventListener('click', () => {
toggleValidationMessage();
deactivationPopover.hidePopover();
});
// Handle clicking on the submit button to validate, send feedback, then deactivate.
deactivationPopover
.querySelector('button.wsal-submit')
.addEventListener('click', async (submitEvent) => {
submitEvent.preventDefault();
const selectedReasonData = getSelectedReasonData();
if (!selectedReasonData) {
toggleValidationMessage(reasonValidationMessage);
return;
}
const { feedbackField, feedbackValue, reason } = selectedReasonData;
if (feedbackField && !feedbackValue) {
toggleValidationMessage(feedbackValidationMessage);
feedbackField.focus();
return;
}
const requestData = {
action: 'plugin_deactivation',
plugin: wsalFeedbackForm.plugin,
site: wsalFeedbackForm.siteUrl,
reason: reason,
feedback: feedbackValue || 'no-feedback-detail-available'
};
// Hide the popover and show deactivating feedback after collecting form data.
deactivationPopover.hidePopover();
// Show deactivating feedback on the button.
if (deactivateButton) {
const isNetwork = window.location.pathname.indexOf('/network/') !== -1;
deactivateButton.textContent = isNetwork
? __('Network Deactivating...', 'wp-security-audit-log')
: __('Deactivating...', 'wp-security-audit-log');
}
try {
// Request 1: Get nonce from remote server.
const nonceResponse = await sendRequest(
wsalFeedbackForm.remoteUrl +
'/?rest_route=/deactivation-feedback-server/v1/get-nonce',
requestData
);
// Request 2: Submit feedback to remote server.
if (nonceResponse.nonce) {
requestData.nonce = nonceResponse.nonce;
await sendRequest(
wsalFeedbackForm.remoteUrl +
'/?rest_route=/deactivation-feedback-server/v1/submit-feedback',
requestData
);
}
} catch (error) {
// Silent fail in case this fetch fails, feedback lost, deactivation continues.
}
if (deactivateButton) {
window.location.href = deactivateButton.href;
}
});
})();