File: /var/www/html/wp-content/mu-plugins/ethnic-shared-contact-form.php
<?php
/**
* Shared Contact Form 7 enhancements.
*
* @package EthnicInfotech
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! defined( 'ETHNIC_CONTACT_SUBMISSION_POST_TYPE' ) ) {
define( 'ETHNIC_CONTACT_SUBMISSION_POST_TYPE', 'ethnic_contact_lead' );
}
/**
* Return the shared Contact Form 7 post ID.
*
* @return int
*/
function ethnic_shared_contact_form_target_id() {
return 26259;
}
/**
* Return the shared Contact Form 7 sender markup for this site host.
*
* @return string
*/
function ethnic_shared_contact_form_sender_markup() {
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$host = $host ? strtolower( $host ) : 'localhost';
return sprintf( '[_site_title] <wordpress@%s>', $host );
}
/**
* Return the shared Contact Form 7 primary mail subject.
*
* @return string
*/
function ethnic_shared_contact_form_primary_subject() {
return '[_site_title] New contact form submission from [your-name]';
}
/**
* Return the shared Contact Form 7 autoresponder mail subject.
*
* @return string
*/
function ethnic_shared_contact_form_secondary_subject() {
return '[_site_title] We received your enquiry';
}
/**
* Check whether the current Contact Form 7 instance is the shared site form.
*
* @param object|null $contact_form Contact Form 7 object.
* @return bool
*/
function ethnic_shared_contact_form_matches( $contact_form = null ) {
if ( null === $contact_form && function_exists( 'wpcf7_get_current_contact_form' ) ) {
$contact_form = wpcf7_get_current_contact_form();
}
if ( ! is_object( $contact_form ) || ! method_exists( $contact_form, 'id' ) ) {
$posted_form_id = isset( $_POST['_wpcf7'] ) ? absint( wp_unslash( $_POST['_wpcf7'] ) ) : 0;
return $posted_form_id > 0 && ethnic_shared_contact_form_target_id() === $posted_form_id;
}
return ethnic_shared_contact_form_target_id() === (int) $contact_form->id();
}
/**
* Return the original form markup block before the budget field.
*
* @return string
*/
function ethnic_shared_contact_form_old_markup() {
return <<<'HTML'
<div class="contact-form">
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="form-line">[text* your-name placeholder akismet:author "Name *"]</div>
</div>
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="form-line">[email* your-email placeholder akismet:author_email "Email *"]</div>
</div>
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="form-line">[text* your-website placeholder "Website *"]</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line">[textarea* your-message 30x4 placeholder "Message *"]</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line">[acceptance acceptance-293 optional] Save my name, email, and website in this browser for the next time I comment. [/acceptance]</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line">[submit class:gem-button class:gem-button-text-weight-bold "Send message"]</div>
</div>
</div>
HTML;
}
/**
* Return the updated shared form markup with the budget slider.
*
* @return string
*/
function ethnic_shared_contact_form_new_markup() {
return <<<'HTML'
<div class="contact-form">
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="form-line">[text* your-name placeholder akismet:author "Name *"]</div>
</div>
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="form-line">[email* your-email placeholder akismet:author_email "Email *"]</div>
</div>
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="form-line">[text* your-website placeholder "Website *"]</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line ethnic-budget-line">
<label class="ethnic-budget-field">
<span class="ethnic-budget-field__meta">
<span class="ethnic-budget-field__label">Budget</span>
<span class="ethnic-budget-field__value" data-budget-display>$5,000</span>
</span>
[range project-budget min:500 max:10000 step:500 class:ethnic-budget-field__range "5000"]
<span class="ethnic-budget-field__scale" aria-hidden="true">
<span>$500</span>
<span>$10,000+</span>
</span>
</label>
</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line">[textarea* your-message 30x4 placeholder "Message *"]</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line">[acceptance acceptance-293 optional] Save my name, email, and website in this browser for the next time I comment. [/acceptance]</div>
</div>
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="form-line">[submit class:gem-button class:gem-button-text-weight-bold "Send message"]</div>
</div>
</div>
HTML;
}
/**
* Keep the shared Contact Form 7 properties aligned with the site field set.
*
* @return void
*/
function ethnic_shared_contact_form_sync_template() {
$budget_mail_line = "Budget: [project-budget]\nMessage Body:\n[your-message]";
$existing_mail_line = "Message Body:\n[your-message]";
$form_updated = false;
$mail_updated = false;
$mail_2_updated = false;
$requires_save = false;
if ( ! class_exists( 'WPCF7_ContactForm' ) ) {
return;
}
$contact_form = WPCF7_ContactForm::get_instance( ethnic_shared_contact_form_target_id() );
if ( ! $contact_form ) {
return;
}
$properties = $contact_form->get_properties();
if ( empty( $properties['form'] ) || ! is_string( $properties['form'] ) ) {
return;
}
if ( false === strpos( $properties['form'], 'project-budget' ) ) {
$properties['form'] = str_replace(
ethnic_shared_contact_form_old_markup(),
ethnic_shared_contact_form_new_markup(),
$properties['form'],
$form_replacements
);
$form_updated = (bool) $form_replacements;
$requires_save = $requires_save || $form_updated;
}
if ( ! empty( $properties['mail']['body'] ) && is_string( $properties['mail']['body'] ) && false === strpos( $properties['mail']['body'], 'Budget: [project-budget]' ) ) {
$properties['mail']['body'] = str_replace(
$existing_mail_line,
$budget_mail_line,
$properties['mail']['body'],
$mail_replacements
);
$mail_updated = (bool) $mail_replacements;
$requires_save = $requires_save || $mail_updated;
}
if ( ! empty( $properties['mail_2']['body'] ) && is_string( $properties['mail_2']['body'] ) && false === strpos( $properties['mail_2']['body'], 'Budget: [project-budget]' ) ) {
$properties['mail_2']['body'] = str_replace(
$existing_mail_line,
$budget_mail_line,
$properties['mail_2']['body'],
$mail_2_replacements
);
$mail_2_updated = (bool) $mail_2_replacements;
$requires_save = $requires_save || $mail_2_updated;
}
if ( ! empty( $properties['mail'] ) && is_array( $properties['mail'] ) ) {
$expected_subject = ethnic_shared_contact_form_primary_subject();
$expected_sender = ethnic_shared_contact_form_sender_markup();
if ( empty( $properties['mail']['subject'] ) || false !== strpos( $properties['mail']['subject'], '[your-subject]' ) ) {
$properties['mail']['subject'] = $expected_subject;
$requires_save = true;
}
if ( empty( $properties['mail']['sender'] ) || false !== strpos( $properties['mail']['sender'], 'development.dummy-domain.tld' ) ) {
$properties['mail']['sender'] = $expected_sender;
$requires_save = true;
}
}
if ( ! empty( $properties['mail_2'] ) && is_array( $properties['mail_2'] ) ) {
$expected_subject = ethnic_shared_contact_form_secondary_subject();
$expected_sender = ethnic_shared_contact_form_sender_markup();
if ( empty( $properties['mail_2']['subject'] ) || false !== strpos( $properties['mail_2']['subject'], '[your-subject]' ) ) {
$properties['mail_2']['subject'] = $expected_subject;
$requires_save = true;
}
if ( empty( $properties['mail_2']['sender'] ) || false !== strpos( $properties['mail_2']['sender'], 'development.dummy-domain.tld' ) ) {
$properties['mail_2']['sender'] = $expected_sender;
$requires_save = true;
}
}
if ( ! $requires_save ) {
return;
}
if ( false === strpos( $properties['form'], 'project-budget' ) ) {
return;
}
if ( ! empty( $properties['mail']['body'] ) && false === strpos( $properties['mail']['body'], 'Budget: [project-budget]' ) ) {
return;
}
if ( ! empty( $properties['mail_2']['body'] ) && false === strpos( $properties['mail_2']['body'], 'Budget: [project-budget]' ) ) {
return;
}
$contact_form->set_properties( $properties );
$contact_form->save();
}
add_action( 'init', 'ethnic_shared_contact_form_sync_template', 20 );
/**
* Add a shared class to the common Contact Form 7 instance.
*
* @param string $class Existing Contact Form 7 classes.
* @return string
*/
function ethnic_shared_contact_form_add_class( $class ) {
if ( ! ethnic_shared_contact_form_matches() ) {
return $class;
}
if ( false === strpos( $class, 'ethnic-shared-contact-form' ) ) {
$class .= ' ethnic-shared-contact-form';
}
return trim( $class );
}
add_filter( 'wpcf7_form_class_attr', 'ethnic_shared_contact_form_add_class', 20 );
/**
* Reject blocked email domains on the shared Contact Form 7 instance.
*
* @param object $result Contact Form 7 validation result.
* @param object $tag Contact Form 7 form tag.
* @return object
*/
function ethnic_shared_contact_form_validate_email_domain( $result, $tag ) {
if ( ! ethnic_shared_contact_form_matches() ) {
return $result;
}
$tag_name = '';
if ( is_object( $tag ) ) {
if ( method_exists( $tag, 'name' ) ) {
$tag_name = (string) $tag->name();
} elseif ( isset( $tag->name ) ) {
$tag_name = (string) $tag->name;
}
}
if ( 'your-email' !== $tag_name ) {
return $result;
}
$submission = class_exists( 'WPCF7_Submission' ) ? WPCF7_Submission::get_instance() : null;
$posted_data = $submission ? $submission->get_posted_data() : array();
$email = sanitize_email( isset( $posted_data['your-email'] ) ? (string) $posted_data['your-email'] : '' );
if ( $email && function_exists( 'ethnic_email_domain_is_allowed' ) && ! ethnic_email_domain_is_allowed( $email ) ) {
$result->invalidate( $tag, function_exists( 'ethnic_email_domain_block_message' ) ? ethnic_email_domain_block_message() : 'Please use a different business email address.' );
}
return $result;
}
add_filter( 'wpcf7_validate_email', 'ethnic_shared_contact_form_validate_email_domain', 20, 2 );
add_filter( 'wpcf7_validate_email*', 'ethnic_shared_contact_form_validate_email_domain', 20, 2 );
/**
* Enqueue shared slider styles and behavior.
*
* @return void
*/
function ethnic_shared_contact_form_enqueue_assets() {
if ( is_admin() ) {
return;
}
wp_register_style( 'ethnic-shared-contact-form', false, array(), null );
wp_enqueue_style( 'ethnic-shared-contact-form' );
wp_add_inline_style(
'ethnic-shared-contact-form',
<<<'CSS'
.ethnic-shared-contact-form .ethnic-budget-field {
display: block;
width: 100%;
}
.ethnic-shared-contact-form .ethnic-budget-field__meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}
.ethnic-shared-contact-form .ethnic-budget-field__label {
display: inline-block;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.14em;
line-height: 1.4;
text-transform: uppercase;
}
.ethnic-shared-contact-form .ethnic-budget-field__value {
display: inline-block;
font-size: 16px;
font-weight: 700;
line-height: 1;
}
.ethnic-shared-contact-form .ethnic-budget-line .wpcf7-form-control-wrap {
display: block;
margin-bottom: 0;
}
.ethnic-shared-contact-form .ethnic-budget-line .wpcf7-not-valid-tip {
display: block;
margin-top: 12px;
}
.ethnic-shared-contact-form input.ethnic-budget-field__range.wpcf7-form-control {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 18px;
padding: 0 !important;
margin: 0 !important;
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
box-shadow: none !important;
cursor: pointer;
accent-color: #e8ff57;
}
.ethnic-shared-contact-form input.ethnic-budget-field__range.wpcf7-form-control::-webkit-slider-runnable-track {
height: 6px;
border-radius: 999px;
background: linear-gradient(
90deg,
#e8ff57 0,
#e8ff57 var(--ethnic-budget-progress, 47.37%),
rgba(255, 255, 255, 0.18) var(--ethnic-budget-progress, 47.37%),
rgba(255, 255, 255, 0.18) 100%
);
}
.ethnic-shared-contact-form input.ethnic-budget-field__range.wpcf7-form-control::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
margin-top: -6px;
border: 3px solid #e8ff57;
border-radius: 50%;
background: #ffffff;
box-shadow: 0 0 0 4px rgba(232, 255, 87, 0.18);
}
.ethnic-shared-contact-form input.ethnic-budget-field__range.wpcf7-form-control::-moz-range-track {
height: 6px;
border: 0;
border-radius: 999px;
background: rgba(255, 255, 255, 0.18);
}
.ethnic-shared-contact-form input.ethnic-budget-field__range.wpcf7-form-control::-moz-range-progress {
height: 6px;
border-radius: 999px;
background: #e8ff57;
}
.ethnic-shared-contact-form input.ethnic-budget-field__range.wpcf7-form-control::-moz-range-thumb {
width: 18px;
height: 18px;
border: 3px solid #e8ff57;
border-radius: 50%;
background: #ffffff;
box-shadow: 0 0 0 4px rgba(232, 255, 87, 0.18);
}
.ethnic-shared-contact-form .ethnic-budget-field__scale {
display: flex;
justify-content: space-between;
gap: 24px;
margin-top: 14px;
font-size: 12px;
line-height: 1.4;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__feedback {
display: none;
margin: 18px 0 20px;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__feedback.is-visible {
display: block;
}
.ethnic-shared-contact-form {
position: relative;
}
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitting .contact-form,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitting .ethnic-shared-contact-form__recaptcha,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitting .wpcf7-response-output,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitting .wpcf7-spinner,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitted .contact-form,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitted .ethnic-shared-contact-form__recaptcha,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitted .wpcf7-response-output,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitted .wpcf7-spinner {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay {
position: absolute;
inset: 0;
z-index: 30;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
background: rgba(18, 20, 17, 0.92);
backdrop-filter: blur(8px);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitting .ethnic-shared-contact-form__overlay,
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitted .ethnic-shared-contact-form__overlay {
opacity: 1 !important;
visibility: visible !important;
pointer-events: auto !important;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay-panel {
width: min(420px, 100%);
text-align: center;
color: #ffffff;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay-spinner {
width: 54px;
height: 54px;
margin: 0 auto 20px;
border: 4px solid rgba(232, 255, 87, 0.18);
border-top-color: #e8ff57;
border-radius: 50%;
animation: ethnic-shared-contact-form-spin 0.8s linear infinite;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay-icon {
display: none !important;
width: 58px;
height: 58px;
margin: 0 auto 20px;
border: 2px solid rgba(232, 255, 87, 0.4);
border-radius: 50%;
color: #e8ff57;
font-size: 28px;
font-weight: 700;
line-height: 54px;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay.is-success .ethnic-shared-contact-form__overlay-spinner {
display: none;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay.is-success .ethnic-shared-contact-form__overlay-icon {
display: block;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay-title {
margin: 0;
font-size: 28px;
font-weight: 700;
line-height: 1.2;
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay-message {
margin: 14px 0 0;
font-size: 16px;
line-height: 1.6;
color: rgba(255, 255, 255, 0.84);
}
.ethnic-shared-contact-form .ethnic-shared-contact-form__overlay-countdown {
display: none;
margin: 18px 0 0;
font-size: 34px;
font-weight: 700;
line-height: 1;
color: rgba(232, 255, 87, 0.92);
}
.ethnic-shared-contact-form.ethnic-shared-contact-form--submitted .ethnic-shared-contact-form__overlay-countdown {
display: block;
}
@keyframes ethnic-shared-contact-form-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
CSS
);
wp_register_script( 'ethnic-shared-contact-form', false, array(), null, true );
wp_enqueue_script( 'ethnic-shared-contact-form' );
wp_add_inline_script(
'ethnic-shared-contact-form',
<<<'JS'
(function () {
function formatBudget(value, max) {
var numericValue = Number(value || 0);
var maximumValue = Number(max || 0);
if (!Number.isFinite(numericValue)) {
return '';
}
if (numericValue >= maximumValue) {
return '$' + maximumValue.toLocaleString('en-US') + '+';
}
return '$' + numericValue.toLocaleString('en-US');
}
function syncBudgetField(field) {
var input = field.querySelector('.ethnic-budget-field__range');
var display = field.querySelector('[data-budget-display]');
var minimumValue;
var maximumValue;
var currentValue;
var denominator;
var progress;
if (!input || !display) {
return;
}
minimumValue = Number(input.min || 0);
maximumValue = Number(input.max || 100);
currentValue = Number(input.value || minimumValue);
denominator = maximumValue - minimumValue || 1;
progress = ((currentValue - minimumValue) / denominator) * 100;
progress = Math.max(0, Math.min(100, progress));
input.style.setProperty('--ethnic-budget-progress', progress + '%');
display.textContent = formatBudget(currentValue, maximumValue);
}
function bindBudgetField(field) {
var input = field.querySelector('.ethnic-budget-field__range');
if (!input || input.dataset.ethnicBudgetBound === 'true') {
syncBudgetField(field);
return;
}
input.dataset.ethnicBudgetBound = 'true';
input.addEventListener('input', function () {
syncBudgetField(field);
});
input.addEventListener('change', function () {
syncBudgetField(field);
});
syncBudgetField(field);
}
function initBudgetFields(root) {
var scope = root && root.querySelectorAll ? root : document;
scope.querySelectorAll('.ethnic-shared-contact-form .ethnic-budget-field').forEach(bindBudgetField);
}
function isSharedContactForm(form) {
return !!(form && form.classList && form.classList.contains('ethnic-shared-contact-form'));
}
function responseOutputNode(form) {
return form ? form.querySelector('.wpcf7-response-output') : null;
}
function feedbackContainer(form) {
var container;
var recaptcha;
var content;
if (!isSharedContactForm(form)) {
return null;
}
container = form.querySelector('.ethnic-shared-contact-form__feedback');
if (container) {
return container;
}
container = document.createElement('div');
container.className = 'ethnic-shared-contact-form__feedback';
container.hidden = true;
recaptcha = form.querySelector('.ethnic-shared-contact-form__recaptcha');
content = form.querySelector('.contact-form');
if (recaptcha && recaptcha.parentNode === form) {
form.insertBefore(container, recaptcha);
return container;
}
if (content && content.parentNode === form) {
content.insertAdjacentElement('afterend', container);
return container;
}
form.appendChild(container);
return container;
}
function syncResponseOutputPlacement(form) {
var output;
var container;
var text;
if (!isSharedContactForm(form)) {
return;
}
output = responseOutputNode(form);
container = feedbackContainer(form);
if (!output || !container) {
return;
}
if (output.parentNode !== container) {
container.appendChild(output);
}
text = String(output.textContent || '').trim();
container.hidden = !text;
container.classList.toggle('is-visible', !!text);
}
function overlayContainer(form) {
var container;
if (!isSharedContactForm(form)) {
return null;
}
container = form.querySelector('.ethnic-shared-contact-form__overlay');
if (container) {
return container;
}
container = document.createElement('div');
container.className = 'ethnic-shared-contact-form__overlay';
container.hidden = true;
container.setAttribute('aria-hidden', 'true');
container.innerHTML =
'<div class="ethnic-shared-contact-form__overlay-panel">' +
'<div class="ethnic-shared-contact-form__overlay-spinner" aria-hidden="true"></div>' +
'<div class="ethnic-shared-contact-form__overlay-icon" aria-hidden="true">✓</div>' +
'<h3 class="ethnic-shared-contact-form__overlay-title"></h3>' +
'<p class="ethnic-shared-contact-form__overlay-message"></p>' +
'<p class="ethnic-shared-contact-form__overlay-countdown"></p>' +
'</div>';
form.appendChild(container);
return container;
}
function overlayParts(container) {
return {
title: container ? container.querySelector('.ethnic-shared-contact-form__overlay-title') : null,
message: container ? container.querySelector('.ethnic-shared-contact-form__overlay-message') : null,
countdown: container ? container.querySelector('.ethnic-shared-contact-form__overlay-countdown') : null
};
}
function setSuccessStateLock(form, state) {
if (!form) {
return;
}
form.ethnicSuccessStateLock = state ? String(state) : '';
}
function setFormState(form, state) {
if (!isSharedContactForm(form)) {
return;
}
form.classList.remove('ethnic-shared-contact-form--submitting', 'ethnic-shared-contact-form--submitted');
if ('submitting' === state) {
form.classList.add('ethnic-shared-contact-form--submitting');
}
if ('submitted' === state) {
form.classList.add('ethnic-shared-contact-form--submitted');
}
}
function clearReloadTimer(form) {
if (!form) {
return;
}
if (form.ethnicReloadTimer) {
window.clearInterval(form.ethnicReloadTimer);
form.ethnicReloadTimer = 0;
}
}
function hideOverlay(form) {
var container = overlayContainer(form);
var parts = overlayParts(container);
clearReloadTimer(form);
setSuccessStateLock(form, '');
setFormState(form, '');
if (!container) {
return;
}
container.hidden = true;
container.setAttribute('aria-hidden', 'true');
container.classList.remove('is-loading', 'is-success');
if (parts.title) {
parts.title.textContent = '';
}
if (parts.message) {
parts.message.textContent = '';
}
if (parts.countdown) {
parts.countdown.textContent = '';
}
}
function showLoadingOverlay(form) {
var container = overlayContainer(form);
var parts = overlayParts(container);
if (!container) {
return;
}
clearReloadTimer(form);
setSuccessStateLock(form, '');
setFormState(form, 'submitting');
container.hidden = false;
container.setAttribute('aria-hidden', 'false');
container.classList.add('is-loading');
container.classList.remove('is-success');
if (parts.title) {
parts.title.textContent = 'Submitting your form';
}
if (parts.message) {
parts.message.textContent = 'Please wait while we send your message.';
}
if (parts.countdown) {
parts.countdown.textContent = '';
}
}
function startReloadCountdown(form, container) {
var seconds = 5;
var parts = overlayParts(container);
function syncCountdown() {
if (!parts.countdown) {
return;
}
parts.countdown.textContent = String(seconds);
}
clearReloadTimer(form);
syncCountdown();
form.ethnicReloadTimer = window.setInterval(function () {
if (seconds <= 1) {
clearReloadTimer(form);
window.location.reload();
return;
}
seconds -= 1;
syncCountdown();
}, 1000);
}
function showSuccessOverlay(form) {
var container = overlayContainer(form);
var parts = overlayParts(container);
if (!container) {
return;
}
clearReloadTimer(form);
setSuccessStateLock(form, 'submitted');
setFormState(form, 'submitted');
container.hidden = false;
container.setAttribute('aria-hidden', 'false');
container.classList.add('is-success');
container.classList.remove('is-loading');
if (parts.title) {
parts.title.textContent = 'Form submitted successfully';
}
if (parts.message) {
parts.message.textContent = 'This page will refresh automatically in';
}
startReloadCountdown(form, container);
}
function scheduleSuccessOverlay(form) {
window.requestAnimationFrame(function () {
window.setTimeout(function () {
showSuccessOverlay(form);
}, 80);
});
}
document.addEventListener('DOMContentLoaded', function () {
initBudgetFields(document);
document.querySelectorAll('form.ethnic-shared-contact-form').forEach(syncResponseOutputPlacement);
});
document.addEventListener('submit', function (event) {
var form = event.target;
if (!isSharedContactForm(form) || event.defaultPrevented) {
return;
}
showLoadingOverlay(form);
});
document.addEventListener('wpcf7beforesubmit', function (event) {
if (!isSharedContactForm(event.target)) {
return;
}
showLoadingOverlay(event.target);
});
document.addEventListener('wpcf7reset', function (event) {
var form = event.target;
window.requestAnimationFrame(function () {
initBudgetFields(form || document);
syncResponseOutputPlacement(form);
});
if (form && form.ethnicSuccessStateLock) {
return;
}
hideOverlay(form);
});
document.addEventListener('wpcf7submit', function (event) {
var form = event.target;
var status = event && event.detail && event.detail.status ? String(event.detail.status) : '';
if (!isSharedContactForm(form)) {
return;
}
if ('mail_sent' === status) {
setSuccessStateLock(form, 'pending');
scheduleSuccessOverlay(form);
return;
}
window.requestAnimationFrame(function () {
window.setTimeout(function () {
syncResponseOutputPlacement(form);
}, 40);
});
hideOverlay(form);
});
document.addEventListener('wpcf7mailsent', function (event) {
if (!isSharedContactForm(event.target)) {
return;
}
setSuccessStateLock(event.target, 'pending');
scheduleSuccessOverlay(event.target);
});
})();
JS
);
}
add_action( 'wp_enqueue_scripts', 'ethnic_shared_contact_form_enqueue_assets', 20 );
/**
* Safely read a Contact Form 7 posted field as plain text.
*
* @param array $posted_data Submitted Contact Form 7 data.
* @param string $key Array key.
* @return string
*/
function ethnic_shared_contact_form_posted_value( $posted_data, $key ) {
if ( ! is_array( $posted_data ) || ! isset( $posted_data[ $key ] ) ) {
return '';
}
$value = $posted_data[ $key ];
if ( is_array( $value ) ) {
$value = implode( ', ', array_map( 'strval', $value ) );
}
return trim( sanitize_text_field( (string) $value ) );
}
/**
* Return the local contact submission meta key.
*
* @return string
*/
function ethnic_shared_contact_form_submission_meta_key() {
return '_ethnic_contact_submission_data';
}
/**
* Register the admin post type used to store shared contact enquiries.
*
* @return void
*/
function ethnic_shared_contact_form_register_submission_post_type() {
register_post_type(
ETHNIC_CONTACT_SUBMISSION_POST_TYPE,
array(
'labels' => array(
'name' => 'Contact Submissions',
'singular_name' => 'Contact Submission',
'menu_name' => 'Contact Submissions',
'name_admin_bar' => 'Contact Submission',
'all_items' => 'All Contact Submissions',
'view_item' => 'View Contact Submission',
'edit_item' => 'View Submission',
'search_items' => 'Search Contact Submissions',
'not_found' => 'No contact submissions found.',
'not_found_in_trash' => 'No contact submissions found in Trash.',
),
'public' => false,
'publicly_queryable' => false,
'show_ui' => true,
'show_in_menu' => false,
'show_in_rest' => false,
'rewrite' => false,
'query_var' => false,
'capability_type' => 'page',
'map_meta_cap' => true,
'capabilities' => array(
'create_posts' => 'do_not_allow',
),
'menu_icon' => 'dashicons-email-alt',
'supports' => array( 'title' ),
)
);
}
add_action( 'init', 'ethnic_shared_contact_form_register_submission_post_type', 22 );
/**
* Add the Contact Submissions screen under Pages.
*
* @return void
*/
function ethnic_shared_contact_form_add_submission_submenu() {
add_submenu_page(
'edit.php?post_type=page',
'Contact Submissions',
'Contact Submissions',
'edit_pages',
'edit.php?post_type=' . ETHNIC_CONTACT_SUBMISSION_POST_TYPE
);
}
add_action( 'admin_menu', 'ethnic_shared_contact_form_add_submission_submenu', 40 );
/**
* Return stored contact submission data.
*
* @param int $post_id Submission post ID.
* @return array
*/
function ethnic_shared_contact_form_submission_data( $post_id ) {
$data = get_post_meta( (int) $post_id, ethnic_shared_contact_form_submission_meta_key(), true );
return is_array( $data ) ? $data : array();
}
/**
* Update stored contact submission data.
*
* @param int $post_id Submission post ID.
* @param array $changes Data to merge.
* @return array
*/
function ethnic_shared_contact_form_update_submission_data( $post_id, $changes ) {
$post_id = absint( $post_id );
$data = ethnic_shared_contact_form_submission_data( $post_id );
$data = array_merge( $data, (array) $changes );
update_post_meta( $post_id, ethnic_shared_contact_form_submission_meta_key(), $data );
return $data;
}
/**
* Build the manual Odoo sync URL for a saved contact submission.
*
* @param int $post_id Submission post ID.
* @param string $redirect_to Optional redirect URL.
* @return string
*/
function ethnic_shared_contact_form_submission_sync_url( $post_id, $redirect_to = '' ) {
$post_id = absint( $post_id );
$redirect_to = $redirect_to ? $redirect_to : admin_url( 'post.php?post=' . $post_id . '&action=edit' );
return wp_nonce_url(
add_query_arg(
array(
'action' => 'ethnic_contact_sync_submission',
'submission' => $post_id,
'redirect_to' => rawurlencode( $redirect_to ),
),
admin_url( 'admin-post.php' )
),
'ethnic_contact_sync_submission_' . $post_id
);
}
/**
* Build the Odoo CRM description for a saved contact submission.
*
* @param array $submission_data Saved contact submission data.
* @param array $settings Shared Odoo CRM channel settings.
* @return string
*/
function ethnic_shared_contact_form_submission_odoo_description( $submission_data, $settings ) {
$lines = array(
'Lead source: ' . ( ! empty( $settings['source_label'] ) ? $settings['source_label'] : 'Website Contact Form' ),
'Name: ' . ( ! empty( $submission_data['name'] ) ? $submission_data['name'] : '' ),
'Email: ' . ( ! empty( $submission_data['email'] ) ? $submission_data['email'] : '' ),
'Website: ' . ( ! empty( $submission_data['website'] ) ? $submission_data['website'] : '' ),
'Budget: ' . ( ! empty( $submission_data['budget'] ) ? $submission_data['budget'] : '' ),
'Message: ' . ( ! empty( $submission_data['message'] ) ? $submission_data['message'] : '' ),
'Submitted at: ' . ( ! empty( $submission_data['submitted_at'] ) ? $submission_data['submitted_at'] : '' ),
'Page URL: ' . ( ! empty( $submission_data['page_url'] ) ? $submission_data['page_url'] : '' ),
'Referrer: ' . ( ! empty( $submission_data['referrer'] ) ? $submission_data['referrer'] : '' ),
'IP address: ' . ( ! empty( $submission_data['ip'] ) ? $submission_data['ip'] : '' ),
'User agent: ' . ( ! empty( $submission_data['user_agent'] ) ? $submission_data['user_agent'] : '' ),
);
$lines = array_filter(
array_map(
'trim',
$lines
)
);
return implode( "\n", $lines );
}
/**
* Build the shared contact form Odoo CRM lead payload from a saved submission.
*
* @param array $submission_data Saved contact submission data.
* @param array $settings Shared Odoo CRM channel settings.
* @return array
*/
function ethnic_shared_contact_form_submission_odoo_payload( $submission_data, $settings ) {
$name = ! empty( $submission_data['name'] ) ? (string) $submission_data['name'] : '';
$email = ! empty( $submission_data['email'] ) ? sanitize_email( (string) $submission_data['email'] ) : '';
$website = ! empty( $submission_data['website'] ) ? esc_url_raw( (string) $submission_data['website'] ) : '';
$lead_prefix = ! empty( $settings['lead_prefix'] ) ? trim( (string) $settings['lead_prefix'] ) : 'Contact Inquiry';
$lead_name = $lead_prefix ? $lead_prefix . ': ' . ( $name ? $name : ( $email ? $email : 'Website Contact' ) ) : ( $name ? $name : 'Website Contact' );
$payload = array(
'name' => $lead_name,
'type' => ! empty( $settings['lead_type'] ) ? (string) $settings['lead_type'] : 'lead',
'contact_name' => $name,
'email_from' => $email,
'website' => $website,
'description' => ethnic_shared_contact_form_submission_odoo_description( $submission_data, $settings ),
);
if ( ! empty( $settings['team_id'] ) ) {
$payload['team_id'] = absint( $settings['team_id'] );
}
if ( ! empty( $settings['user_id'] ) ) {
$payload['user_id'] = absint( $settings['user_id'] );
}
return $payload;
}
/**
* Sync a saved contact submission to Odoo CRM.
*
* @param int $post_id Submission post ID.
* @param bool $force Whether to force re-syncing an already synced record.
* @return int|WP_Error
*/
function ethnic_shared_contact_form_sync_submission_to_odoo( $post_id, $force = false ) {
$post_id = absint( $post_id );
$data = ethnic_shared_contact_form_submission_data( $post_id );
if ( $post_id <= 0 || empty( $data ) ) {
return new WP_Error( 'ethnic_contact_submission_missing', 'The contact submission could not be found.' );
}
if ( ! function_exists( 'ethnic_odoo_crm_channel_settings' ) || ! function_exists( 'ethnic_odoo_crm_create_lead' ) ) {
return new WP_Error( 'ethnic_contact_odoo_unavailable', 'The shared Odoo CRM module is not available.' );
}
$settings = ethnic_odoo_crm_channel_settings( 'contact' );
$status = ! empty( $data['odoo_sync_status'] ) ? sanitize_key( (string) $data['odoo_sync_status'] ) : 'pending';
if ( ! empty( $data['odoo_lead_id'] ) && 'synced' === $status && ! $force ) {
return absint( $data['odoo_lead_id'] );
}
if ( empty( $settings['enabled'] ) ) {
ethnic_shared_contact_form_update_submission_data(
$post_id,
array(
'odoo_sync_status' => 'disabled',
'odoo_sync_message' => 'Odoo CRM sync is currently disabled in settings.',
'odoo_last_attempt' => current_time( 'mysql' ),
)
);
return new WP_Error( 'ethnic_contact_odoo_disabled', 'Odoo CRM sync is currently disabled in settings.' );
}
if ( ! function_exists( 'ethnic_odoo_crm_credentials_present' ) || ! ethnic_odoo_crm_credentials_present( $settings ) ) {
ethnic_shared_contact_form_update_submission_data(
$post_id,
array(
'odoo_sync_status' => 'not-configured',
'odoo_sync_message' => 'Complete the Odoo CRM settings before syncing contact submissions.',
'odoo_last_attempt' => current_time( 'mysql' ),
)
);
return new WP_Error( 'ethnic_contact_odoo_not_configured', 'Complete the Odoo CRM settings before syncing contact submissions.' );
}
ethnic_shared_contact_form_update_submission_data(
$post_id,
array(
'odoo_sync_status' => 'syncing',
'odoo_sync_message' => 'Syncing contact submission to Odoo CRM.',
'odoo_last_attempt' => current_time( 'mysql' ),
)
);
$payload = ethnic_shared_contact_form_submission_odoo_payload( $data, $settings );
$result = ethnic_odoo_crm_create_lead( $payload, $settings );
if ( is_wp_error( $result ) ) {
ethnic_shared_contact_form_update_submission_data(
$post_id,
array(
'odoo_sync_status' => 'failed',
'odoo_sync_message' => $result->get_error_message(),
'odoo_last_attempt' => current_time( 'mysql' ),
)
);
do_action( 'ethnic_odoo_crm_contact_sync_failed', $result, $payload, $data, $settings, $post_id );
return $result;
}
$lead_id = absint( $result );
ethnic_shared_contact_form_update_submission_data(
$post_id,
array(
'odoo_sync_status' => 'synced',
'odoo_sync_message' => 'Contact submission synced to Odoo CRM successfully.',
'odoo_lead_id' => $lead_id,
'odoo_synced_at' => current_time( 'mysql' ),
'odoo_last_attempt' => current_time( 'mysql' ),
)
);
return $lead_id;
}
/**
* Maybe sync a saved contact submission to Odoo CRM.
*
* @param int $post_id Submission post ID.
* @return int
*/
function ethnic_shared_contact_form_maybe_sync_submission_to_odoo( $post_id ) {
$result = ethnic_shared_contact_form_sync_submission_to_odoo( $post_id );
return is_wp_error( $result ) ? 0 : absint( $result );
}
/**
* Sync existing contact submissions to Odoo CRM.
*
* @return array|WP_Error
*/
function ethnic_shared_contact_form_sync_existing_submissions_to_odoo() {
if ( ! function_exists( 'ethnic_odoo_crm_channel_settings' ) ) {
return new WP_Error( 'ethnic_contact_odoo_unavailable', 'The shared Odoo CRM module is not available.' );
}
$settings = ethnic_odoo_crm_channel_settings( 'contact' );
if ( empty( $settings['enabled'] ) ) {
return new WP_Error( 'ethnic_contact_odoo_disabled', 'Enable Contact Form sync before syncing existing contact submissions.' );
}
if ( ! function_exists( 'ethnic_odoo_crm_credentials_present' ) || ! ethnic_odoo_crm_credentials_present( $settings ) ) {
return new WP_Error( 'ethnic_contact_odoo_not_configured', 'Complete the Odoo CRM settings before syncing existing contact submissions.' );
}
$ids = get_posts(
array(
'post_type' => ETHNIC_CONTACT_SUBMISSION_POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'date',
'order' => 'ASC',
)
);
$totals = array(
'processed' => 0,
'synced' => 0,
'failed' => 0,
'skipped' => 0,
);
foreach ( $ids as $post_id ) {
$data = ethnic_shared_contact_form_submission_data( $post_id );
$status = ! empty( $data['odoo_sync_status'] ) ? sanitize_key( (string) $data['odoo_sync_status'] ) : 'pending';
if ( ! empty( $data['odoo_lead_id'] ) && 'synced' === $status ) {
++$totals['skipped'];
continue;
}
++$totals['processed'];
$result = ethnic_shared_contact_form_sync_submission_to_odoo( $post_id, false );
if ( is_wp_error( $result ) ) {
++$totals['failed'];
} else {
++$totals['synced'];
}
}
return $totals;
}
/**
* Save a shared contact form submission locally and sync it to Odoo if enabled.
*
* @param object $contact_form Contact Form 7 form instance.
* @return int
*/
function ethnic_shared_contact_form_store_submission( $contact_form ) {
if ( ! ethnic_shared_contact_form_matches( $contact_form ) ) {
return 0;
}
if ( ! class_exists( 'WPCF7_Submission' ) ) {
return 0;
}
$submission = WPCF7_Submission::get_instance();
if ( ! $submission ) {
return 0;
}
$posted_data = $submission->get_posted_data();
$name = ethnic_shared_contact_form_posted_value( $posted_data, 'your-name' );
$email = sanitize_email( isset( $posted_data['your-email'] ) ? (string) $posted_data['your-email'] : '' );
$website = ethnic_shared_contact_form_posted_value( $posted_data, 'your-website' );
$budget = ethnic_shared_contact_form_posted_value( $posted_data, 'project-budget' );
$message = isset( $posted_data['your-message'] ) ? sanitize_textarea_field( (string) $posted_data['your-message'] ) : '';
if ( $email && function_exists( 'ethnic_email_domain_is_allowed' ) && ! ethnic_email_domain_is_allowed( $email ) ) {
return 0;
}
if ( '' === $name && '' === $email ) {
return 0;
}
$post_title = $name ? $name : ( $email ? $email : 'Contact Submission' );
$post_id = wp_insert_post(
array(
'post_type' => ETHNIC_CONTACT_SUBMISSION_POST_TYPE,
'post_status' => 'publish',
'post_title' => $post_title,
'post_content' => '',
'comment_status' => 'closed',
'ping_status' => 'closed',
)
);
if ( ! $post_id || is_wp_error( $post_id ) ) {
return 0;
}
update_post_meta(
$post_id,
ethnic_shared_contact_form_submission_meta_key(),
array(
'name' => $name,
'email' => $email,
'website' => $website,
'budget' => $budget,
'message' => $message,
'contact_form_id' => method_exists( $contact_form, 'id' ) ? (int) $contact_form->id() : ethnic_shared_contact_form_target_id(),
'submitted_at' => current_time( 'mysql' ),
'ip' => isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '',
'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '',
'referrer' => isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '',
'page_url' => isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '',
'odoo_sync_status' => 'pending',
'odoo_sync_message'=> '',
'odoo_lead_id' => 0,
'odoo_synced_at' => '',
'odoo_last_attempt'=> '',
)
);
ethnic_shared_contact_form_maybe_sync_submission_to_odoo( $post_id );
return (int) $post_id;
}
/**
* Save shared contact enquiries after successful CF7 delivery.
*
* @param object $contact_form Contact Form 7 form instance.
* @return void
*/
function ethnic_shared_contact_form_maybe_sync_to_odoo( $contact_form ) {
ethnic_shared_contact_form_store_submission( $contact_form );
}
add_action( 'wpcf7_mail_sent', 'ethnic_shared_contact_form_maybe_sync_to_odoo', 20 );
/**
* Customize the Contact Submissions admin columns.
*
* @param array $columns Existing columns.
* @return array
*/
function ethnic_shared_contact_form_submission_columns( $columns ) {
$ordered = array();
foreach ( $columns as $key => $label ) {
if ( 'title' === $key ) {
$ordered['title'] = 'Name';
$ordered['ethnic_contact_email'] = 'Email';
$ordered['ethnic_contact_website'] = 'Website';
$ordered['ethnic_contact_budget'] = 'Budget';
$ordered['ethnic_contact_message'] = 'Message';
$ordered['ethnic_contact_crm'] = 'Odoo CRM';
continue;
}
if ( 'date' === $key ) {
$ordered['date'] = 'Submitted';
continue;
}
$ordered[ $key ] = $label;
}
return $ordered;
}
add_filter( 'manage_' . ETHNIC_CONTACT_SUBMISSION_POST_TYPE . '_posts_columns', 'ethnic_shared_contact_form_submission_columns' );
/**
* Render the Contact Submissions admin columns.
*
* @param string $column Column key.
* @param int $post_id Submission post ID.
* @return void
*/
function ethnic_shared_contact_form_render_submission_column( $column, $post_id ) {
$data = ethnic_shared_contact_form_submission_data( $post_id );
switch ( $column ) {
case 'ethnic_contact_email':
if ( ! empty( $data['email'] ) ) {
printf(
'<a href="mailto:%1$s">%2$s</a>',
esc_attr( $data['email'] ),
esc_html( $data['email'] )
);
} else {
echo '<span style="color:#6a7691;">—</span>';
}
break;
case 'ethnic_contact_website':
if ( ! empty( $data['website'] ) ) {
$website = esc_url( (string) $data['website'] );
if ( $website ) {
printf(
'<a href="%1$s" target="_blank" rel="noopener">%2$s</a>',
$website,
esc_html( $data['website'] )
);
} else {
echo esc_html( $data['website'] );
}
} else {
echo '<span style="color:#6a7691;">—</span>';
}
break;
case 'ethnic_contact_budget':
echo ! empty( $data['budget'] ) ? esc_html( $data['budget'] ) : '<span style="color:#6a7691;">—</span>';
break;
case 'ethnic_contact_message':
if ( ! empty( $data['message'] ) ) {
echo esc_html( wp_trim_words( (string) $data['message'], 16, '…' ) );
} else {
echo '<span style="color:#6a7691;">—</span>';
}
break;
case 'ethnic_contact_crm':
$status = ! empty( $data['odoo_sync_status'] ) ? sanitize_key( (string) $data['odoo_sync_status'] ) : 'pending';
$settings = function_exists( 'ethnic_odoo_crm_channel_settings' ) ? ethnic_odoo_crm_channel_settings( 'contact' ) : array();
$can_manual_sync = ! empty( $settings['enabled'] ) && function_exists( 'ethnic_odoo_crm_credentials_present' ) && ethnic_odoo_crm_credentials_present( $settings );
$lead_url = ! empty( $data['odoo_lead_id'] ) && function_exists( 'ethnic_odoo_crm_lead_admin_url' ) ? ethnic_odoo_crm_lead_admin_url( (int) $data['odoo_lead_id'], $settings ) : '';
if ( function_exists( 'ethnic_odoo_crm_sync_badge_html' ) ) {
echo wp_kses_post( ethnic_odoo_crm_sync_badge_html( $status ) );
} else {
echo esc_html( ucfirst( $status ) );
}
if ( ! empty( $data['odoo_lead_id'] ) ) {
echo '<br /><span style="color:#6a7691;font-size:12px;">Lead #' . esc_html( (string) $data['odoo_lead_id'] ) . '</span>';
}
if ( $lead_url ) {
echo '<br /><a href="' . esc_url( $lead_url ) . '" target="_blank" rel="noopener">Open lead</a>';
} elseif ( 'synced' !== $status && $can_manual_sync ) {
echo '<br /><a href="' . esc_url( ethnic_shared_contact_form_submission_sync_url( $post_id, admin_url( 'edit.php?post_type=' . ETHNIC_CONTACT_SUBMISSION_POST_TYPE ) ) ) . '">Sync now</a>';
}
break;
}
}
add_action( 'manage_' . ETHNIC_CONTACT_SUBMISSION_POST_TYPE . '_posts_custom_column', 'ethnic_shared_contact_form_render_submission_column', 10, 2 );
/**
* Add the Contact Submission details meta box.
*
* @return void
*/
function ethnic_shared_contact_form_add_submission_meta_boxes() {
add_meta_box(
'ethnic-contact-submission-details',
'Contact Submission Details',
'ethnic_shared_contact_form_render_submission_meta_box',
ETHNIC_CONTACT_SUBMISSION_POST_TYPE,
'normal',
'high'
);
}
add_action( 'add_meta_boxes_' . ETHNIC_CONTACT_SUBMISSION_POST_TYPE, 'ethnic_shared_contact_form_add_submission_meta_boxes' );
/**
* Render the Contact Submission details meta box.
*
* @param WP_Post $post Submission post.
* @return void
*/
function ethnic_shared_contact_form_render_submission_meta_box( $post ) {
$data = ethnic_shared_contact_form_submission_data( $post->ID );
$odoo_settings = function_exists( 'ethnic_odoo_crm_channel_settings' ) ? ethnic_odoo_crm_channel_settings( 'contact' ) : array();
$can_manual_sync = ! empty( $odoo_settings['enabled'] ) && function_exists( 'ethnic_odoo_crm_credentials_present' ) && ethnic_odoo_crm_credentials_present( $odoo_settings );
$odoo_lead_admin = ! empty( $data['odoo_lead_id'] ) && function_exists( 'ethnic_odoo_crm_lead_admin_url' ) ? ethnic_odoo_crm_lead_admin_url( (int) $data['odoo_lead_id'], $odoo_settings ) : '';
$rows = array(
'Full Name' => isset( $data['name'] ) ? $data['name'] : '',
'Email' => isset( $data['email'] ) ? $data['email'] : '',
'Website' => isset( $data['website'] ) ? $data['website'] : '',
'Budget' => isset( $data['budget'] ) ? $data['budget'] : '',
'Message' => isset( $data['message'] ) ? $data['message'] : '',
'Form ID' => isset( $data['contact_form_id'] ) ? $data['contact_form_id'] : '',
'Odoo Sync' => isset( $data['odoo_sync_status'] ) ? $data['odoo_sync_status'] : 'pending',
'Odoo Lead ID' => isset( $data['odoo_lead_id'] ) ? $data['odoo_lead_id'] : '',
'Odoo Synced' => isset( $data['odoo_synced_at'] ) ? $data['odoo_synced_at'] : '',
'Last Attempt' => isset( $data['odoo_last_attempt'] ) ? $data['odoo_last_attempt'] : '',
'Odoo Error' => isset( $data['odoo_sync_message'] ) && 'failed' === ( isset( $data['odoo_sync_status'] ) ? $data['odoo_sync_status'] : '' ) ? $data['odoo_sync_message'] : '',
'Submitted At' => isset( $data['submitted_at'] ) ? $data['submitted_at'] : '',
'IP Address' => isset( $data['ip'] ) ? $data['ip'] : '',
'User Agent' => isset( $data['user_agent'] ) ? $data['user_agent'] : '',
'Referrer' => isset( $data['referrer'] ) ? $data['referrer'] : '',
);
?>
<style>
.ethnic-contact-submission__grid {
display: grid;
grid-template-columns: 180px minmax(0, 1fr);
gap: 12px 18px;
align-items: start;
}
.ethnic-contact-submission__label {
font-weight: 600;
color: #22314f;
}
.ethnic-contact-submission__value {
min-width: 0;
word-break: break-word;
white-space: pre-wrap;
}
.ethnic-contact-submission__actions {
margin-top: 18px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
</style>
<div class="ethnic-contact-submission__grid">
<?php foreach ( $rows as $label => $value ) : ?>
<div class="ethnic-contact-submission__label"><?php echo esc_html( $label ); ?></div>
<div class="ethnic-contact-submission__value">
<?php if ( 'Email' === $label && $value ) : ?>
<a href="mailto:<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $value ); ?></a>
<?php elseif ( 'Website' === $label && $value && esc_url( (string) $value ) ) : ?>
<a href="<?php echo esc_url( $value ); ?>" target="_blank" rel="noopener"><?php echo esc_html( $value ); ?></a>
<?php elseif ( 'Odoo Sync' === $label && function_exists( 'ethnic_odoo_crm_sync_badge_html' ) ) : ?>
<?php echo wp_kses_post( ethnic_odoo_crm_sync_badge_html( (string) $value ) ); ?>
<?php elseif ( 'Referrer' === $label && $value ) : ?>
<a href="<?php echo esc_url( $value ); ?>" target="_blank" rel="noopener"><?php echo esc_html( $value ); ?></a>
<?php else : ?>
<?php echo '' !== (string) $value ? esc_html( $value ) : '—'; ?>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div class="ethnic-contact-submission__actions">
<?php if ( ! empty( $data['website'] ) && esc_url( (string) $data['website'] ) ) : ?>
<a class="button button-secondary" href="<?php echo esc_url( $data['website'] ); ?>" target="_blank" rel="noopener">Open Website</a>
<?php endif; ?>
<?php if ( $odoo_lead_admin ) : ?>
<a class="button button-secondary" href="<?php echo esc_url( $odoo_lead_admin ); ?>" target="_blank" rel="noopener">Open Odoo Lead</a>
<?php elseif ( $can_manual_sync ) : ?>
<a class="button button-primary" href="<?php echo esc_url( ethnic_shared_contact_form_submission_sync_url( $post->ID ) ); ?>">Send to Odoo Now</a>
<?php else : ?>
<span style="color:#6a7691;">Contact form sync follows the shared Odoo CRM settings.</span>
<?php endif; ?>
</div>
<?php
}
/**
* Handle manual Odoo sync requests for saved contact submissions.
*
* @return void
*/
function ethnic_shared_contact_form_handle_manual_submission_sync() {
if ( ! current_user_can( 'edit_pages' ) ) {
wp_die( 'You do not have permission to sync contact submissions.', 403 );
}
$post_id = isset( $_GET['submission'] ) ? absint( wp_unslash( $_GET['submission'] ) ) : 0;
check_admin_referer( 'ethnic_contact_sync_submission_' . $post_id );
$redirect_to = isset( $_GET['redirect_to'] ) ? rawurldecode( (string) wp_unslash( $_GET['redirect_to'] ) ) : '';
$redirect_to = wp_validate_redirect( $redirect_to, admin_url( 'post.php?post=' . $post_id . '&action=edit' ) );
$result = ethnic_shared_contact_form_sync_submission_to_odoo( $post_id, false );
if ( function_exists( 'ethnic_odoo_crm_set_admin_notice' ) ) {
if ( is_wp_error( $result ) ) {
ethnic_odoo_crm_set_admin_notice( 'error', 'Odoo CRM sync failed: ' . $result->get_error_message() );
} else {
ethnic_odoo_crm_set_admin_notice( 'success', 'Contact submission synced to Odoo CRM successfully.' );
}
}
wp_safe_redirect( $redirect_to );
exit;
}
add_action( 'admin_post_ethnic_contact_sync_submission', 'ethnic_shared_contact_form_handle_manual_submission_sync' );