';
// Choice groups use a non-interactive legend so each option keeps its own
';
}
/**
* Ask page caches not to store a page that contains a form: its nonce would go stale and be shared
* between visitors. DONOTCACHEPAGE is honoured by the major caching plugins; nocache_headers() covers
* browsers, proxies and CDNs.
*/
function pubforms_prevent_page_cache() {
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- standard constant read by caching plugins.
}
if ( ! headers_sent() ) {
nocache_headers();
}
}
// Send the no-cache headers before output starts, on any singular page whose content embeds a form.
add_action( 'template_redirect', function () {
if ( ! is_singular() ) {
return;
}
$post = get_queried_object();
if ( $post instanceof WP_Post && ( has_shortcode( $post->post_content, 'pubforms' ) || has_block( 'pubforms/form', $post ) ) ) {
pubforms_prevent_page_cache();
}
} );
/**
* The form's HTML. It carries a nonce, so pages containing a form must not be cached
* (see pubforms_prevent_page_cache()).
*/
function pubforms_render_form( $form_id, $style = array() ) {
$form = pubforms_get_form( $form_id );
if ( ! $form ) {
return '';
}
if ( 'publish' !== $form['status'] ) {
return current_user_can( 'edit_post', $form['id'] )
? '
' . esc_html__( 'This form is not published yet, so visitors cannot see it.', 'pubforms' ) . '
' . wp_kses_post( sprintf( __( 'You are editing a saved draft. You can return to it anytime with this link: resume this form.', 'pubforms' ), esc_url( $url ) ) ) . '
';
}
$has_files = false;
$max_page = 0;
foreach ( $form['fields'] as $field ) {
if ( 'file' === $field['type'] ) {
$has_files = true;
}
$max_page = max( $max_page, (int) $field['page'] );
}
$multi = $max_page > 0;
// Always on: also handles conditional logic and abandonment capture on plain forms.
wp_enqueue_script( 'pubforms-ui', PUBFORMS_URL . 'assets/form-ui.js', array(), pubforms_asset_version( 'form-ui.js' ), true );
$spam = pubforms_spam_provider( $form );
if ( $spam ) {
$spam['keys'] = pubforms_spam_keys( $spam['id'] );
}
$save_abandoned = ! empty( $form['settings']['save_abandoned'] );
$out .= '';
}
/**
* Which spam provider a form uses, if it is configured site-wide. Returns null when none applies.
*/
function pubforms_spam_provider( $form ) {
$id = pubforms_effective_spam_method( $form );
if ( '' === $id ) {
return null;
}
return array( 'id' => $id, 'keys' => 'question' === $id ? array() : pubforms_spam_keys( $id ) );
}
/**
* Site + secret keys for a spam provider, from the Settings screen.
*/
function pubforms_spam_keys( $provider ) {
return array(
'site' => trim( (string) get_option( 'pubforms_' . $provider . '_site_key', '' ) ),
'secret' => trim( (string) get_option( 'pubforms_' . $provider . '_secret_key', '' ) ),
);
}
/**
* The CAPTCHA widget for a form, or '' when none is configured. This deliberately loads the
* provider's own script so the widget works on cached pages without a per-visitor token.
*/
function pubforms_spam_widget_html( $spam, $form_id = 0 ) {
if ( $spam && 'question' === $spam['id'] ) {
return pubforms_spam_challenge_html( $form_id );
}
if ( ! $spam || empty( $spam['keys']['site'] ) ) {
return '';
}
if ( 'turnstile' === $spam['id'] ) {
wp_enqueue_script( 'pubforms-turnstile', 'https://challenges.cloudflare.com/turnstile/v0/api.js', array(), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters, PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent -- provider-hosted CAPTCHA script, loaded only for forms that opt in to Turnstile.
return '
';
}
wp_enqueue_script( 'pubforms-hcaptcha', 'https://hcaptcha.com/1/api.js', array(), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters, PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent -- provider-hosted CAPTCHA script, loaded only for forms that opt in to hCaptcha.
return '