option value => what that option shows on this page. */ private $reveals = array(); /** @var int How many show or hide rules wrap the item being drawn. */ private $rule_depth = 0; /** @var string[] Choice question keys on the page being rendered. */ private $browser_choice_keys = array(); /** @var array Script handles to load with async and defer. */ private static $async_script_handles = array(); public function __construct( HP_Forms_Form $form ) { $this->form = $form; $this->mode = $form->style_mode(); $this->prefix = 'hpf-' . $form->id(); HP_Forms_Components::$submit_label = (string) $form->messages()['submit']; } /** @param array $query key => value, sanitised by capture_query(). */ public function set_query( array $query ) { $this->query = $query; } /* Public entry points */ public function form_html() { $form_id = $this->form->id(); if ( isset( $_GET['holmesforms_sent'], $_GET['holmesforms_form'] ) && absint( $_GET['holmesforms_form'] ) === $form_id ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read only, the sent flag is checked against the one-time code require_once HOLMESFORMS_DIR . 'includes/class-hp-forms-submission.php'; return $this->wrap_form( $this->success_markup( HP_Forms_Submission::read_sent_message() ) ); } $answers = array(); $errors = array(); $notice = ''; // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['holmesforms_error'], $_GET['holmesforms_form'] ) && absint( $_GET['holmesforms_form'] ) === $form_id && in_array( $_GET['holmesforms_error'], array( 'session', 'sent', 'nojs' ), true ) ) { $notice = sanitize_key( $_GET['holmesforms_error'] ); } if ( isset( $_GET['holmesforms_errors'], $_GET['holmesforms_form'] ) && absint( $_GET['holmesforms_form'] ) === $form_id ) { require_once HOLMESFORMS_DIR . 'includes/class-hp-forms-submission.php'; $bounce = HP_Forms_Submission::read_bounce(); if ( is_array( $bounce ) ) { $answers = isset( $bounce['answers'] ) ? $bounce['answers'] : array(); $errors = isset( $bounce['errors'] ) ? $bounce['errors'] : array(); $notice = isset( $bounce['notice'] ) ? sanitize_key( $bounce['notice'] ) : ''; if ( 'message' === $notice ) { $notice = self::NOTICE_MESSAGE_PREFIX . sanitize_text_field( isset( $bounce['message'] ) ? (string) $bounce['message'] : '' ); } } } // phpcs:enable WordPress.Security.NonceVerification.Recommended $context = HP_Forms_Logic::resolve_context( $this->form, $answers, $this->logic_environment() ); $answers = $context['fields']; $variables = $context['variables']; $pages = $this->in_scope_pages( $answers, $variables ); $draft = HP_Forms_Draft::create( $form_id ); $seed = apply_filters( 'holmesforms_seed_draft', array(), $form_id, $draft['draft_id'] ); if ( ! empty( $seed['answers'] ) && is_array( $seed['answers'] ) ) { HP_Forms_Draft::merge_answers( $draft['draft_id'], $seed['answers'] ); $loaded_draft = HP_Forms_Draft::load( $draft['token'] ); $context = HP_Forms_Logic::resolve_context( $this->form, $loaded_draft ? $loaded_draft['answers'] : $answers, $this->logic_environment() ); $answers = $context['fields']; $variables = $context['variables']; $pages = $this->in_scope_pages( $answers, $variables ); if ( ! empty( $seed['start_page'] ) ) { $start_page_id = sanitize_key( $seed['start_page'] ); if ( in_array( $start_page_id, wp_list_pluck( $pages, 'id' ), true ) ) { $this->start_page = $start_page_id; } } } $inner = ''; $inner .= $this->nojs_note_markup(); $inner .= $this->error_summary_markup( $errors, $pages, $notice, $answers ); $inner .= '
'; $inner .= '
'; $inner .= sprintf( '
', esc_url( add_query_arg( 'holmesforms_nojs', '1', home_url( '/' ) ) ), in_array( 'attachments', wp_list_pluck( $this->form->all_fields(), 'type' ), true ) ? ' enctype="multipart/form-data"' : '', count( $pages ) > 1 ? '1' : '0', $this->form->has_review_component() ? '1' : '0', (int) $this->form->count_steps( $pages ), $this->start_page ? (int) array_search( $this->start_page, wp_list_pluck( $pages, 'id' ), true ) + 1 : 1, esc_attr( implode( ',', $this->form->condition_keys() ) ), esc_attr( implode( ',', $this->keys_kept_when_hidden() ) ) ); $page_query = array_filter( $this->capture_query(), static function ( $key ) { return 0 !== strpos( (string) $key, 'holmesforms_' ); }, ARRAY_FILTER_USE_KEY ); $inner .= ''; $inner .= ''; $inner .= ''; $inner .= ''; $inner .= ''; $inner .= ''; $inner .= wp_nonce_field( 'holmesforms_submit_' . $form_id, 'holmesforms_nonce', true, false ); $inner .= $this->honeypot_markup(); $inner .= '
'; $is_first = true; foreach ( $pages as $page ) { $inner .= $this->page_section( $page, $answers, $errors, $is_first, $variables ); $is_first = false; } $inner .= '
'; $messages = $this->form->messages(); $has_author_submit = false; foreach ( $this->form->all_fields() as $author_field ) { if ( 'button' === $author_field['type'] && 'submit' === $author_field['config']['action'] ) { $has_author_submit = true; break; } } $inner .= $this->captcha_markup( count( $pages ) > 1 ); $inner .= '
'; $inner .= sprintf( ' ', esc_attr( $this->css_class( 'button_alt' ) ), esc_html( $messages['back'] ) ); $inner .= sprintf( '', esc_attr( $this->css_class( 'button' ) ), esc_attr( $messages['continue'] ), esc_attr( $messages['submit'] ), esc_html( count( $pages ) > 1 ? $messages['continue'] : $messages['submit'] ) ); $inner .= apply_filters( 'holmesforms_form_actions_html', '', $this->form, $pages ); $inner .= '
'; $inner .= '
'; $inner .= $this->debug_panel_markup(); return $this->wrap_form( $inner ); } /** * @param array $errors key => message for this page. * @return string */ public function page_html( array $page_def, array $answers, array $errors = array() ) { $context = HP_Forms_Logic::resolve_context( $this->form, $answers, $this->logic_environment() ); return $this->page_section( $page_def, $context['fields'], $errors, true, $context['variables'] ); } /** * @param array $fields key => value. * @param array $variables key => value. * @return string */ private function resolve_label( $label, array $fields, array $variables ) { return HP_Forms_Components::fill_tokens( $label, $this->form->all_fields(), $fields, $variables, false, $this->form ); } /** @param array $answers Accumulated answers. */ public function summary_html( array $answers ) { $messages = $this->form->messages(); $rows = ''; $context = HP_Forms_Logic::resolve_context( $this->form, $answers, $this->logic_environment() ); foreach ( $this->in_scope_pages( $context['fields'], $context['variables'] ) as $page ) { foreach ( $this->visible_leaf_fields( $page['fields'], $context['fields'], $context['variables'] ) as $field ) { if ( HP_Forms_Components::is_repeatable( $field ) ) { $repeat_text = HP_Forms_Components::repeat_format( $field, isset( $answers[ $field['key'] ] ) ? (array) $answers[ $field['key'] ] : array() ); if ( '' === trim( $repeat_text ) ) { continue; } $repeat_label = HP_Forms_Components::answer_label( $this->resolve_label( '' !== $field['label'] ? $field['label'] : $field['key'], $context['fields'], $context['variables'] ) ); $rows .= sprintf( '
%s
%s
' . '
', esc_html( $repeat_label ), nl2br( esc_html( $repeat_text ) ), esc_attr( $page['id'] ), esc_attr( $field['key'] ), esc_html( $messages['change'] ), esc_attr( $this->visually_hidden_class() ), esc_html( $repeat_label ) ); continue; } if ( in_array( $field['type'], array( 'hidden', 'button', 'email_action', 'submit_message', 'container', 'cya' ), true ) || HP_Forms_Components::stores_no_answer( $field['type'] ) ) { continue; } if ( 'attachments' === $field['type'] ) { $files_label = HP_Forms_Components::answer_label( $this->resolve_label( '' !== $field['label'] ? $field['label'] : $field['key'], $context['fields'], $context['variables'] ) ); $rows .= sprintf( '
%s
%s
' . '
', esc_html( $files_label ), esc_attr( $field['key'] ), esc_html__( 'Not answered', 'holmesportfolio-forms' ), esc_attr( $page['id'] ), esc_attr( $field['key'] ), esc_html( $messages['change'] ), esc_attr( $this->visually_hidden_class() ), esc_html( $files_label ) ); continue; } if ( 'alert' === $field['type'] && empty( $field['config']['confirm'] ) ) { continue; } $value = isset( $answers[ $field['key'] ] ) ? $answers[ $field['key'] ] : ''; $row_label = HP_Forms_Components::answer_name( $field ); $row_label = HP_Forms_Components::answer_label( $this->resolve_label( $row_label, $context['fields'], $context['variables'] ) ); $row_value = $this->format_shown_answer( $field, $value ); if ( '' === $row_value ) { $row_value = __( 'Not answered', 'holmesportfolio-forms' ); } $rows .= sprintf( '
%s
%s
' . '
', esc_html( $row_label ), nl2br( esc_html( $row_value ) ), esc_attr( $page['id'] ), esc_attr( $field['key'] ), esc_html( $messages['change'] ), esc_attr( $this->visually_hidden_class() ), esc_html( $row_label ) ); } } return '
' . '

' . esc_html( $messages['check_title'] ) . '

' . '
' . $rows . '
' . '
'; } /** @return string Empty when the question was not answered. */ private function format_shown_answer( array $field, $value ) { if ( 'checkbox' === $field['type'] && ! empty( $field['config']['multiple'] ) && ! array_filter( array_map( 'strval', (array) $value ), 'strlen' ) ) { return ''; } $shown = (string) HP_Forms_Components::format_answer_verbose( $field, $value ); return '' === trim( $shown ) ? '' : $shown; } /** * Answers summary: the author's HTML with [all-fields] / [all-data] and Go to buttons. * * @param array $field Normalised cya field. */ private function render_cya( array $field, array $answers, array $variables, $level = 3 ) { $pages = $this->in_scope_pages( $answers, $variables ); $is_multipage = count( $pages ) > 1; $answer_list_html = $this->cya_answer_list_html( $pages, $answers, $variables, $is_multipage ); list( $token_values, $condition_values, $plain_values ) = $this->cya_token_values( $pages, $answers, $variables ); $body = wp_kses_post( HP_Forms_Components::apply_if_blocks( (string) $field['config']['body'], $condition_values ) ); $body = $this->tidy_cya_body( $body, $pages, $answers, $variables, $is_multipage ); $body = HP_Forms_Components::fill_attribute_tokens( HP_Forms_Components::strip_tokens_in_attributes( $body ), $plain_values ); $body = str_replace( array( '[all-fields]', '[all-data]' ), "\x01hpf-list\x01", $body ); $body = preg_replace_callback( '/\[([a-z0-9_\-]+)\]/i', static function ( $matches ) use ( $token_values ) { return isset( $token_values[ $matches[1] ] ) ? str_replace( "\x01", '', $token_values[ $matches[1] ] ) : $matches[0]; }, $body ); $body = str_replace( "\x01hpf-list\x01", $answer_list_html, $body ); if ( $level < 3 ) { $body = preg_replace_callback( '#<(/?)h([3-6])\b#i', static function ( $matches ) { return '<' . $matches[1] . 'h' . ( (int) $matches[2] - 1 ); }, $body ); } return '
' . $body . '
'; } private function cya_answer_list_html( array $pages, array $answers, array $variables, $is_multipage ) { $answer_list_html = ''; $page_number = 0; foreach ( $pages as $page ) { ++$page_number; $page_title = '' !== trim( (string) $page['title'] ) ? $page['title'] /* translators: %d: page number */ : sprintf( __( 'page %d', 'holmesportfolio-forms' ), $page_number ); $rows_html = $this->cya_page_rows_html( $page, $page_title, $answers, $variables ); if ( '' === $rows_html ) { continue; } $answer_list_html .= '
' . '

' . esc_html( '' !== trim( (string) $page['title'] ) ? $page['title'] : sprintf( /* translators: %d: page number, as a heading */ __( 'Page %d', 'holmesportfolio-forms' ), $page_number ) ) . '

' . $rows_html; if ( $is_multipage ) { $answer_list_html .= sprintf( '', esc_attr( $this->css_class( 'button_alt' ) ), esc_attr( $page['id'] ), /* translators: %s: page title */ esc_html( sprintf( __( 'Go to %s', 'holmesportfolio-forms' ), $page_title ) ) ); } $answer_list_html .= '
'; } return $answer_list_html; } private function cya_page_rows_html( array $page, $page_title, array $answers, array $variables ) { $rows_html = ''; $is_after_group = false; foreach ( $this->visible_answer_groups( $page['fields'], $answers, $variables ) as $group ) { $group_rows_html = ''; foreach ( $group['items'] as $leaf ) { $group_rows_html .= $this->cya_row_html( $leaf, $answers, $variables ); } if ( '' === $group_rows_html ) { continue; } if ( '' !== $group['title'] && 0 !== strcasecmp( trim( $group['title'] ), trim( $page_title ) ) ) { $rows_html .= '

' . esc_html( $group['title'] ) . '

'; $is_after_group = true; } elseif ( $is_after_group ) { $rows_html .= '
'; $is_after_group = false; } $rows_html .= '
' . $group_rows_html . '
'; } return $rows_html; } private function cya_row_html( array $leaf, array $answers, array $variables ) { if ( HP_Forms_Components::is_repeatable( $leaf ) ) { $repeat_html = HP_Forms_Components::repeat_format( $leaf, isset( $answers[ $leaf['key'] ] ) ? (array) $answers[ $leaf['key'] ] : array(), true ); return '' !== trim( $repeat_html ) ? '
' . esc_html( $this->resolve_label( '' !== $leaf['label'] ? $leaf['label'] : $leaf['key'], $answers, $variables ) ) . '
' . $repeat_html . '
' : ''; } if ( 'attachments' === $leaf['type'] ) { return '
' . esc_html( HP_Forms_Components::answer_label( $this->resolve_label( '' !== $leaf['label'] ? $leaf['label'] : $leaf['key'], $answers, $variables ) ) ) . '
' . esc_html__( 'Not answered', 'holmesportfolio-forms' ) . '
'; } if ( in_array( $leaf['type'], array( 'hidden', 'button', 'email_action', 'submit_message', 'container', 'cya' ), true ) || HP_Forms_Components::stores_no_answer( $leaf['type'] ) ) { return ''; } if ( 'alert' === $leaf['type'] && empty( $leaf['config']['confirm'] ) ) { return ''; } $label = HP_Forms_Components::answer_label( $this->resolve_label( HP_Forms_Components::answer_name( $leaf ), $answers, $variables ) ); $shown = $this->format_shown_answer( $leaf, isset( $answers[ $leaf['key'] ] ) ? $answers[ $leaf['key'] ] : '' ); return '
' . esc_html( $label ) . '
' . ( '' === $shown ? esc_html__( 'Not answered', 'holmesportfolio-forms' ) : nl2br( esc_html( $shown ) ) ) . '
'; } /** @return array[] [token] values for the summary body, and the values its blocks test. */ private function cya_token_values( array $pages, array $answers, array $variables ) { $token_values = array(); $plain_values = array(); $condition_overrides = array(); foreach ( $pages as $page ) { foreach ( $this->visible_leaf_fields( $page['fields'], $answers, $variables ) as $leaf ) { if ( HP_Forms_Components::is_repeatable( $leaf ) ) { $token_values[ $leaf['key'] ] = HP_Forms_Components::repeat_format( $leaf, isset( $answers[ $leaf['key'] ] ) ? (array) $answers[ $leaf['key'] ] : array(), true ); $plain_values[ $leaf['key'] ] = wp_strip_all_tags( $token_values[ $leaf['key'] ] ); continue; } if ( in_array( $leaf['type'], array( 'button', 'email_action', 'submit_message', 'container', 'cya', 'attachments' ), true ) || HP_Forms_Components::stores_no_answer( $leaf['type'] ) ) { continue; } $value = isset( $answers[ $leaf['key'] ] ) ? $answers[ $leaf['key'] ] : ''; $plain_values[ $leaf['key'] ] = $this->format_shown_answer( $leaf, $value ); $token_values[ $leaf['key'] ] = nl2br( esc_html( $plain_values[ $leaf['key'] ] ) ); if ( ( 'checkbox' === $leaf['type'] && empty( $leaf['config']['multiple'] ) ) || ( 'alert' === $leaf['type'] && ! empty( $leaf['config']['confirm'] ) ) ) { $condition_overrides[ $leaf['key'] ] = ( '1' === (string) $value ) ? '1' : ''; } } } foreach ( $variables as $variable_key => $variable_value ) { $plain_values[ $variable_key ] = (string) $variable_value; $token_values[ $variable_key ] = esc_html( $plain_values[ $variable_key ] ); } $condition_values = array_merge( $token_values, $condition_overrides ); foreach ( $answers as $answer_key => $answer_value ) { if ( ! array_key_exists( $answer_key, $condition_values ) ) { $condition_values[ $answer_key ] = is_scalar( $answer_value ) ? (string) $answer_value : ''; } } return array( $token_values, $condition_values, $plain_values ); } private function tidy_cya_body( $body, array $pages, array $answers, array $variables, $is_multipage ) { if ( false === strpos( $body, 'data-hpf-' ) && false === strpos( $body, 'hpf-cya__goto' ) ) { return $body; } $in_scope = array_map( 'strval', wp_list_pluck( $pages, 'id' ) ); $leaves = array(); $groups = array(); $context = array( 'fields' => $answers, 'variables' => $variables, ); $collect_groups = static function ( array $fields ) use ( &$collect_groups, &$groups, $context ) { foreach ( $fields as $field ) { if ( 'container' === $field['type'] && HP_Forms_Logic::is_shown( $field, $context ) ) { $groups[ (string) $field['id'] ] = true; $collect_groups( isset( $field['config']['children'] ) ? (array) $field['config']['children'] : array() ); } } }; foreach ( $pages as $page ) { foreach ( $this->visible_leaf_fields( $page['fields'], $answers, $variables ) as $leaf ) { $leaves[ (string) $leaf['key'] ] = $leaf; } $collect_groups( $page['fields'] ); } $not_answered = esc_html__( 'Not answered', 'holmesportfolio-forms' ); $body = preg_replace_callback( '#]*\bdata-hpf-group="([^"]*)"[^>]*>(?:(?!).)*\s*#is', static function ( $matches ) use ( $groups ) { return isset( $groups[ $matches[1] ] ) ? $matches[0] : ''; }, $body ); $body = preg_replace_callback( '#<(p|div)\b[^>]*\bdata-hpf-field="([^"]*)"[^>]*>.*?#is', function ( $matches ) use ( $leaves, $answers, $not_answered ) { if ( ! isset( $leaves[ $matches[2] ] ) ) { return ''; } $leaf = $leaves[ $matches[2] ]; if ( 'attachments' === $leaf['type'] || HP_Forms_Components::is_repeatable( $leaf ) ) { return $matches[0]; } $shown = $this->format_shown_answer( $leaf, isset( $answers[ $matches[2] ] ) ? $answers[ $matches[2] ] : '' ); return '' === $shown ? str_replace( '[' . $matches[2] . ']', $not_answered, $matches[0] ) : $matches[0]; }, $body ); $body = preg_replace( '#]*>\s*\s*#i', '', $body ); $body = preg_replace_callback( '#]*\bdata-hpf-page="([^"]*)"[^>]*>(.*?)#is', static function ( $matches ) use ( $in_scope ) { if ( ! in_array( $matches[1], $in_scope, true ) ) { return ''; } $inner = preg_replace( '#]*\bhpf-cya__section\b[^>]*>(?:(?!).)*\s*]*\bhpf-cya__group-end\b[^>]*>#is', '', $matches[2] ); $inner = preg_replace( '#]*\bhpf-cya__section\b[^>]*>(?:(?!).)*(?=\s*(?:]*\bhpf-cya__group-end\b[^>]*>(?=\s*(?:css_class( 'button_alt' ); return preg_replace_callback( '#]*?)\bclass="([^"]*\bhpf-cya__goto\b[^"]*)"([^>]*)>(.*?)#is', static function ( $matches ) use ( $is_multipage, $in_scope, $button_class ) { $target = preg_match( '#data-hpf-change="([^"]*)"#', $matches[1] . $matches[3], $target_match ) ? $target_match[1] : ''; if ( ! $is_multipage || ( '' !== $target && ! in_array( $target, $in_scope, true ) ) ) { return ''; } $classes = implode( ' ', array_unique( array_filter( explode( ' ', $button_class . ' ' . $matches[2] ) ) ) ); return '' . $matches[4] . ''; }, $body ); } public function is_debug_visible() { return $this->form->is_debug_enabled() && current_user_can( 'manage_options' ); } private function debug_panel_markup() { if ( ! $this->is_debug_visible() ) { return ''; } $refresh_icon = ''; $refresh_label = esc_attr__( 'Refresh values', 'holmesportfolio-forms' ); return '
' . '
' . '' . esc_html__( 'Form debugger', 'holmesportfolio-forms' ) . '' . '' . '' . '' . '
' . '
' . '' . '
'; } /** * @param array $context { fields, variables }. * @param string $page_id Current page id, or '__summary' / 'submitted'. */ public function debug_snapshot( array $context, array $errors, $page_id, $reference = '' ) { $fields = isset( $context['fields'] ) ? $context['fields'] : array(); $variables = isset( $context['variables'] ) ? $context['variables'] : array(); $in_scope = wp_list_pluck( $this->in_scope_pages( $fields, $variables ), 'id' ); $skip_types = array( 'button', 'email_action', 'submit_message', 'alert', 'cya', 'container' ); $pages = array(); $logic_context = array( 'fields' => $fields, 'variables' => $variables, ); foreach ( $this->form->pages() as $page ) { $shown_keys = wp_list_pluck( $this->visible_leaf_fields( $page['fields'], $fields, $variables ), 'key' ); $rows = array(); $layouts = array(); $collect_layouts = function ( array $field_list, $is_parent_hidden ) use ( &$collect_layouts, &$layouts, $logic_context ) { foreach ( $field_list as $container ) { if ( 'container' !== $container['type'] ) { continue; } $is_shown = ! $is_parent_hidden && HP_Forms_Logic::is_shown( $container, $logic_context ); if ( ! empty( $container['visible_when'] ) ) { $layouts[] = array( 'id' => $container['id'], 'label' => '' !== $container['label'] ? $container['label'] : __( 'Untitled group', 'holmesportfolio-forms' ), 'mode' => ( isset( $container['visible_mode'] ) && 'hide' === $container['visible_mode'] ) ? 'hide' : 'show', 'shown' => (bool) $is_shown, ); } $children = ( isset( $container['config']['children'] ) && is_array( $container['config']['children'] ) ) ? $container['config']['children'] : array(); $collect_layouts( $children, ! $is_shown ); } }; $collect_layouts( $page['fields'], false ); foreach ( $this->flatten_page_fields( $page['fields'] ) as $field ) { if ( in_array( $field['type'], $skip_types, true ) || '' === $field['key'] ) { continue; } $has_value = array_key_exists( $field['key'], $fields ); $value = $has_value ? $fields[ $field['key'] ] : null; $row = array( 'key' => $field['key'], 'label' => '' !== $field['label'] ? $field['label'] : $field['key'], 'type' => $field['type'], 'stored' => 'hidden' === $field['type'], 'shown' => 'hidden' !== $field['type'] && in_array( $field['key'], $shown_keys, true ), 'value' => $has_value ? HP_Forms_Components::format_value( $field, $value ) : '', 'raw' => is_array( $value ) ? wp_json_encode( $value ) : null, ); $rows[] = $row; } $pages[] = array( 'id' => $page['id'], 'title' => '' !== $page['title'] ? $page['title'] : $page['id'], 'in_scope' => in_array( $page['id'], $in_scope, true ), 'rows' => $rows, 'layouts' => $layouts, ); } $variable_rows = array(); foreach ( $variables as $key => $variable_value ) { $variable_rows[] = array( 'key' => $key, 'value' => is_scalar( $variable_value ) ? $variable_value : wp_json_encode( $variable_value ), ); } $error_rows = array(); foreach ( $errors as $key => $message ) { $error_rows[] = array( 'key' => $key, 'message' => $message, ); } $system = array( array( 'key' => 'page-title', 'value' => $this->form->title(), ), array( 'key' => 'case-reference', 'value' => '' !== $reference ? $reference : __( '(assigned once the form is submitted)', 'holmesportfolio-forms' ), ), ); return array( 'page' => $page_id, 'pages' => $pages, 'variables' => $variable_rows, 'errors' => $error_rows, 'system' => $system, ); } public function success_markup( $sent_html = '' ) { $class = 'gds' === $this->mode ? 'govuk-panel govuk-panel--confirmation' : ( 'bootstrap' === $this->mode ? 'alert alert-success' : 'hpf-panel hpf-panel--confirmation' ); $body = '' !== $sent_html ? wp_kses_post( $sent_html ) : ''; $messages = $this->form->messages(); foreach ( '' === $body ? $this->form->submit_messages() : array() as $submit_message ) { if ( '' === trim( $submit_message['text'] ) ) { continue; } $text = HP_Forms_Components::apply_if_blocks( (string) $submit_message['text'], array() ); $text = preg_replace( '/\[[a-z0-9_\-]+\]/i', '', $text ); $body = 'html' === $submit_message['format'] ? wp_kses_post( $text ) : nl2br( esc_html( $text ) ); break; } if ( '' === $body ) { $body = esc_html( $messages['success'] ); } return '
' . '
' . $body . '
' . '
'; } /* Building blocks */ private function wrap_form( $inner ) { return sprintf( '
%s
', esc_attr( $this->mode ), $this->needs_js ? ' hpf-form--needs-js' : '', (int) $this->form->id(), (int) $this->form->id(), esc_url( rest_url( HOLMESFORMS_REST_NS . '/' ) ), esc_attr( wp_create_nonce( 'wp_rest' ) ), '' !== $this->start_page ? ' data-hpf-start-page="' . esc_attr( $this->start_page ) . '"' : '', $inner ); } /** * @param array $variables Computed variables, for visibility conditions. * @return string */ private function page_section( array $page_def, array $answers, array $errors, $is_visible, array $variables = array() ) { $heading_id = $this->prefix . '-' . $page_def['id'] . '-h'; $page_head = '' !== $page_def['title'] ? array( $page_def['title'], $heading_id ) : array(); $this->reveals = $this->build_reveal_map( $page_def['fields'], $answers, $variables ); $this->browser_choice_keys = $this->collect_browser_choice_keys( $page_def['fields'] ); $fields_html = $this->render_field_list( $page_def['fields'], $answers, $errors, $variables, $page_head ? 3 : 2, $page_head ); $this->reveals = array(); $this->browser_choice_keys = array(); $fields_html = preg_replace_callback( '/ aria-controls="([^"]*)"/', static function ( $matches ) use ( &$fields_html ) { $ids = array_filter( explode( ' ', $matches[1] ), static function ( $id ) use ( &$fields_html ) { return '' !== $id && false !== strpos( $fields_html, 'id="' . $id . '"' ); } ); return $ids ? ' aria-controls="' . implode( ' ', $ids ) . '"' : ''; }, $fields_html ); $title = '' !== $page_def['title'] ? '

' . esc_html( $page_def['title'] ) . '

' : ''; $is_cya_only = false; foreach ( $this->flatten_page_fields( $page_def['fields'] ) as $field ) { if ( 'cya' === $field['type'] ) { $is_cya_only = true; } elseif ( ! in_array( $field['type'], array( 'button', 'email_action', 'submit_message', 'container', 'hidden' ), true ) ) { $is_cya_only = false; break; } } return sprintf( '
%s%s
', esc_attr( $page_def['id'] ), $is_cya_only ? ' data-hpf-cya-page' : '', '' !== $title ? 'aria-labelledby="' . esc_attr( $heading_id ) . '"' : 'aria-label="' . esc_attr( wp_strip_all_tags( $this->form->title() ) ) . '"', $is_visible ? '' : ' hidden', $title, $fields_html ); } /** @return array Choice key => lower-case option value => list of name, kind, anchor. */ private function build_reveal_map( array $fields, array $answers, array $variables ) { $context = array( 'fields' => $answers, 'variables' => $variables, ); $reveals = array(); $collect_reveals = function ( array $field_list, $is_hidden ) use ( &$collect_reveals, &$reveals, $context ) { foreach ( $field_list as $field ) { $is_shown = ! $is_hidden && HP_Forms_Logic::is_shown( $field, $context ); $rules = ( empty( $field['visible_when'] ) || ( isset( $field['visible_mode'] ) && 'hide' === $field['visible_mode'] ) ) ? array() : (array) HP_Forms_Logic::normalise_ruleset( $field['visible_when'] ); foreach ( $rules as $rule ) { if ( 'field' !== $rule['type'] || ! in_array( $rule['op'], array( 'eq', 'contains' ), true ) || '' === trim( (string) $rule['value'] ) ) { continue; } $kind = 'alert' === $field['type'] ? 'note' : 'question'; $name = 'note' === $kind ? '' : trim( wp_strip_all_tags( (string) $field['label'] ) ); if ( 'alert' === $field['type'] ) { $anchor = $this->prefix . '-alert-' . $field['id']; } else { $anchor = $this->prefix . '-' . ( '' !== (string) $field['key'] ? $field['key'] : $field['id'] ) . '-anchor'; } $reveals[ $rule['key'] ][ strtolower( trim( (string) $rule['value'] ) ) ][] = array( 'name' => $name, 'kind' => $kind, 'anchor' => $is_shown ? $anchor : '', ); } if ( 'container' === $field['type'] && ! HP_Forms_Components::is_repeatable( $field ) ) { $collect_reveals( isset( $field['config']['children'] ) ? (array) $field['config']['children'] : array(), ! $is_shown ); } } }; $collect_reveals( $fields, false ); return $reveals; } /** * @param array $fields Normalised field list. * @param array $page_head Page title and heading id. */ private function render_field_list( array $fields, array $answers, array $errors, array $variables, $level = 3, array $page_head = array() ) { $context = array( 'fields' => $answers, 'variables' => $variables, ); $html = ''; $heading_level = max( self::MIN_HEADING_LEVEL, min( self::MAX_HEADING_LEVEL, (int) $level ) ); foreach ( $fields as $field ) { $is_shown = HP_Forms_Logic::is_shown( $field, $context ); $browser_rules = $this->browser_rules( $field ); if ( ! $is_shown && null === $browser_rules ) { continue; } $has_rule = ! empty( $field['visible_when'] ); if ( $has_rule ) { ++$this->rule_depth; } $item_html = $this->render_field_item( $field, $answers, $errors, $variables, $heading_level, $level, $page_head ); if ( $has_rule ) { --$this->rule_depth; } if ( null !== $browser_rules && '' !== $item_html ) { $item_html = '
' . $item_html . '
'; } $html .= $item_html; } return $html; } private function render_field_item( array $field, array $answers, array $errors, array $variables, $heading_level, $level, array $page_head ) { if ( 'container' === $field['type'] ) { return $this->render_container( $field, $answers, $errors, $variables, $heading_level, $page_head ); } if ( 'cya' === $field['type'] ) { return $this->render_cya( $field, $answers, $variables, $level ); } $value = isset( $answers[ $field['key'] ] ) ? $answers[ $field['key'] ] : $this->default_value( $field ); $error = isset( $errors[ $field['key'] ] ) ? $errors[ $field['key'] ] : ''; return HP_Forms_Components::render( $field, $value, $error, $this->mode, array( 'prefix' => $this->prefix, 'form' => $this->form, 'query' => $this->capture_query(), 'answers' => $answers, 'variables' => $variables, 'reveals' => isset( $this->reveals[ $field['key'] ] ) ? $this->reveals[ $field['key'] ] : array(), 'has_rule' => $this->rule_depth > 0, 'heading_level' => $heading_level, ) ); } private function render_container( array $field, array $answers, array $errors, array $variables, $heading_level, array $page_head ) { $title = trim( (string) $field['label'] ); if ( HP_Forms_Components::is_repeatable( $field ) ) { $repeat_rows = isset( $answers[ $field['key'] ] ) && is_array( $answers[ $field['key'] ] ) ? $answers[ $field['key'] ] : array(); $repeat_context = array( 'prefix' => $this->prefix, 'form' => $this->form, 'query' => $this->capture_query(), 'errors' => $errors, ); $body = HP_Forms_Components::repeat_render( $field, $repeat_rows, $this->mode, $repeat_context, isset( $errors[ $field['key'] ] ) ? $errors[ $field['key'] ] : '' ); $head = '' !== $title ? $this->group_heading( $title, $heading_level ) : ''; return '
' . $head . $body . '
'; } $is_page_head = '' !== $title && $page_head && 0 === strcasecmp( $title, trim( (string) $page_head[0] ) ); $inner = $this->render_field_list( isset( $field['config']['children'] ) ? $field['config']['children'] : array(), $answers, $errors, $variables, ( '' !== $title && ! $is_page_head ) ? $heading_level + 1 : $heading_level ); if ( '' === $inner ) { return ''; } $id_attribute = ' id="' . esc_attr( $this->prefix . '-' . ( '' !== (string) $field['key'] ? $field['key'] : $field['id'] ) . '-anchor' ) . '"'; if ( $is_page_head ) { return '' . $inner . ''; } if ( '' !== $title ) { return '' . '' . $this->group_heading( $title, $heading_level ) . '' . $inner . ''; } return '' . $inner . ''; } /** @return string[] Keys of the page's choice questions, outside repeatable groups. */ private function collect_browser_choice_keys( array $fields ) { $keys = array(); foreach ( $fields as $field ) { if ( 'container' === $field['type'] ) { if ( ! HP_Forms_Components::is_repeatable( $field ) ) { $keys = array_merge( $keys, $this->collect_browser_choice_keys( isset( $field['config']['children'] ) ? (array) $field['config']['children'] : array() ) ); } } elseif ( in_array( $field['type'], self::BROWSER_CHOICE_TYPES, true ) && '' !== (string) $field['key'] ) { $keys[] = (string) $field['key']; } } return $keys; } /** @return array|null Rule the browser can apply, or null to leave it to the server. */ private function browser_rules( array $field ) { if ( empty( $field['visible_when'] ) || ! $this->browser_choice_keys || ! $this->is_browser_safe_item( $field ) ) { return null; } $rows = array(); foreach ( (array) HP_Forms_Logic::normalise_ruleset( $field['visible_when'] ) as $row ) { $value = (string) $row['value']; if ( 'field' !== $row['type'] || ! in_array( $row['key'], $this->browser_choice_keys, true ) || ! in_array( $row['op'], self::BROWSER_RULE_OPERATORS, true ) || HP_Forms_Logic::is_plain_date( $value ) || false !== strpos( $value, '{' ) ) { return null; } $rows[] = array( 'key' => $row['key'], 'op' => $row['op'], 'value' => $value, 'join' => $row['join'], ); } if ( ! $rows ) { return null; } return array( 'mode' => ( isset( $field['visible_mode'] ) && 'hide' === $field['visible_mode'] ) ? 'hide' : 'show', 'rows' => $rows, ); } private function is_browser_safe_item( array $field ) { if ( ! in_array( $field['type'], self::BROWSER_SAFE_TYPES, true ) ) { return false; } if ( 'alert' === $field['type'] ) { $alert_text = isset( $field['config']['text'] ) ? (string) $field['config']['text'] : ''; return false === strpos( $alert_text, '[' ) && false === stripos( $alert_text, 'is_browser_safe_item( $child ) ) { return false; } } return true; } /** @return array[] */ public function visible_leaf_fields( array $fields, array $answers, array $variables ) { $context = array( 'fields' => $answers, 'variables' => $variables, ); $leaves = array(); foreach ( $fields as $field ) { if ( ! HP_Forms_Logic::is_shown( $field, $context ) || ! HP_Forms_Components::is_known_type( $field['type'] ) ) { continue; } if ( 'container' === $field['type'] ) { if ( HP_Forms_Components::is_repeatable( $field ) ) { $leaves[] = $field; continue; } foreach ( $this->visible_leaf_fields( isset( $field['config']['children'] ) ? $field['config']['children'] : array(), $answers, $variables ) as $child ) { $leaves[] = $child; } continue; } $leaves[] = $field; } return $leaves; } /** @return array[] Each: { title: string, items: array[] }. */ public function visible_answer_groups( array $fields, array $answers, array $variables ) { $context = array( 'fields' => $answers, 'variables' => $variables, ); $groups = array(); $loose_fields = array(); $flush_loose_fields = function () use ( &$groups, &$loose_fields ) { if ( $loose_fields ) { $groups[] = array( 'title' => '', 'items' => $loose_fields, ); $loose_fields = array(); } }; foreach ( $fields as $field ) { if ( ! HP_Forms_Logic::is_shown( $field, $context ) ) { continue; } if ( 'container' === $field['type'] && ! HP_Forms_Components::is_repeatable( $field ) ) { $children = isset( $field['config']['children'] ) ? $field['config']['children'] : array(); $items = $this->visible_leaf_fields( $children, $answers, $variables ); $title = trim( (string) $field['label'] ); if ( '' !== $title ) { $flush_loose_fields(); if ( $items ) { $groups[] = array( 'title' => $title, 'items' => $items, ); } } else { foreach ( $items as $child ) { $loose_fields[] = $child; } } continue; } $loose_fields[] = $field; } $flush_loose_fields(); return $groups; } /** @return array[] */ private function flatten_page_fields( array $fields ) { $flat_fields = array(); foreach ( $fields as $field ) { $flat_fields[] = $field; if ( 'container' === $field['type'] && ! empty( $field['config']['children'] ) ) { foreach ( $this->flatten_page_fields( $field['config']['children'] ) as $child ) { $flat_fields[] = $child; } } } return $flat_fields; } /** * @param array $pages In-scope pages, to map a field to its page. * @param array $answers The answers sent, so a group's error links to the box with the problem. * @return string */ private function error_summary_markup( array $errors, array $pages, $notice = '', array $answers = array() ) { $messages = $this->form->messages(); $has_errors = ! empty( $errors ) || '' !== $notice; $items = ''; if ( 'session' === $notice ) { $items .= '
  • ' . esc_html__( 'Your session timed out, so your answers were not sent. Fill in the form again.', 'holmesportfolio-forms' ) . '
  • '; } elseif ( 'sent' === $notice ) { require_once HOLMESFORMS_DIR . 'includes/class-hp-forms-submission.php'; $items .= '
  • ' . esc_html( HP_Forms_Submission::already_sent_message() ) . '
  • '; } elseif ( 'nojs' === $notice ) { $items .= '
  • ' . esc_html__( 'This form needs JavaScript. Turn on JavaScript in your browser and reload the page, or contact us another way.', 'holmesportfolio-forms' ) . '
  • '; } elseif ( 'rate' === $notice ) { $items .= '
  • ' . esc_html__( 'Too many attempts from your connection. Wait 10 minutes, then send again.', 'holmesportfolio-forms' ) . '
  • '; } elseif ( 0 === strpos( $notice, self::NOTICE_MESSAGE_PREFIX ) && '' !== substr( $notice, strlen( self::NOTICE_MESSAGE_PREFIX ) ) ) { $items .= '
  • ' . esc_html( substr( $notice, strlen( self::NOTICE_MESSAGE_PREFIX ) ) ) . '
  • '; } if ( ! empty( $errors ) ) { $page_by_field = array(); $field_by_key = array(); foreach ( $pages as $page ) { foreach ( $this->flatten_page_fields( $page['fields'] ) as $field ) { $page_by_field[ $field['key'] ] = $page['id']; $field_by_key[ $field['key'] ] = $field; } } foreach ( $errors as $key => $message ) { $target = $this->prefix . '-' . HP_Forms_Components::error_link_key( $key, $field_by_key[ $key ] ?? null, $answers ) . '-anchor'; $question = HP_Forms_Components::error_field_key( $key ); $items .= sprintf( '
  • %s
  • ', esc_attr( $target ), esc_attr( isset( $page_by_field[ $question ] ) ? $page_by_field[ $question ] : '' ), esc_html( HP_Forms_Components::error_summary_text( $key, $message, $field_by_key ) ) ); } } $class = 'gds' === $this->mode ? 'govuk-error-summary' : ( 'bootstrap' === $this->mode ? 'alert alert-danger' : 'hpf-error-summary' ); $is_gds = 'gds' === $this->mode; return sprintf( '', esc_attr( $class ), $has_errors ? '' : ' hidden', $is_gds ? 'govuk-error-summary__title hpf-error-summary__title' : 'hpf-error-summary__title', esc_html( $messages['error_summary'] ), $is_gds ? 'govuk-error-summary__body' : 'hpf-error-summary__body', $is_gds ? 'govuk-list govuk-error-summary__list hpf-error-summary__list' : 'hpf-error-summary__list', $items ); } private function honeypot_markup() { return sprintf( '', esc_attr( $this->prefix ), esc_html__( 'Leave this field blank', 'holmesportfolio-forms' ), esc_attr( HP_Forms_Spam::HONEYPOT ) ); } private function captcha_markup( $wait_for_last_step = false ) { $spam_config = $this->form->spam_config(); $provider = isset( $spam_config['provider'] ) ? $spam_config['provider'] : 'none'; if ( 'turnstile' === $provider ) { $site_key = trim( (string) $spam_config['turnstile_site'] ); $script_url = 'https://challenges.cloudflare.com/turnstile/v0/api.js'; // phpcs:ignore PluginCheck.CodeAnalysis.Offloading.OffloadedContent -- the provider's script, only when that provider is switched on $widget = 'cf-turnstile'; } elseif ( 'recaptcha' === $provider ) { $site_key = trim( (string) $spam_config['recaptcha_site'] ); $script_url = 'https://www.google.com/recaptcha/api.js'; // phpcs:ignore PluginCheck.CodeAnalysis.Offloading.OffloadedContent -- the provider's script, only when that provider is switched on $widget = 'g-recaptcha'; } else { return ''; } if ( '' === $site_key ) { return ''; } $handle = 'hpf-captcha-' . $provider; if ( $wait_for_last_step ) { return sprintf( '', esc_url( $script_url ), esc_attr( $widget ), esc_attr( $site_key ) ); } wp_enqueue_script( $handle, $script_url, array(), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion, WordPress.WP.EnqueuedResourceParameters.NoExplicitVersion -- the provider's script has no version of ours self::$async_script_handles[ $handle ] = true; add_filter( 'script_loader_tag', array( __CLASS__, 'add_async_to_captcha_script' ), 10, 2 ); return sprintf( '
    ' . '
    ', esc_attr( $widget ), esc_attr( $site_key ) ); } public static function add_async_to_captcha_script( $tag, $script_handle ) { return isset( self::$async_script_handles[ $script_handle ] ) ? str_replace( ' src=', ' async defer src=', $tag ) : $tag; } private function nojs_note_markup() { $spam_config = $this->form->spam_config(); $provider = isset( $spam_config['provider'] ) ? $spam_config['provider'] : 'none'; $site_key = 'turnstile' === $provider ? $spam_config['turnstile_site'] : ( 'recaptcha' === $provider ? $spam_config['recaptcha_site'] : '' ); if ( '' !== trim( (string) $site_key ) ) { $text = __( 'This form needs JavaScript for its spam check, so it cannot be sent with JavaScript turned off. Turn on JavaScript in your browser and reload the page, or contact us another way.', 'holmesportfolio-forms' ); } elseif ( self::is_nojs_blocked() ) { $text = __( 'This form needs JavaScript. Turn on JavaScript in your browser and reload the page, or contact us another way.', 'holmesportfolio-forms' ); } else { return ''; } $class = 'gds' === $this->mode ? 'govuk-inset-text' : ( 'bootstrap' === $this->mode ? 'alert alert-warning' : 'hpf-alert hpf-alert--warning' ); $this->needs_js = true; return sprintf( '', esc_attr( $class ), esc_html( $text ) ); } private function keys_kept_when_hidden() { $keys = array(); $collect_keys = static function ( array $fields ) use ( &$collect_keys, &$keys ) { foreach ( $fields as $field ) { if ( 'container' === $field['type'] ) { if ( empty( $field['visible_when'] ) && ! HP_Forms_Components::is_repeatable( $field ) ) { $collect_keys( (array) ( $field['config']['children'] ?? array() ) ); } } elseif ( ! empty( $field['visible_when'] ) && empty( $field['clear_on_hide'] ) && '' !== (string) $field['key'] ) { $keys[] = $field['key']; } } }; foreach ( $this->form->pages() as $page ) { $collect_keys( $page['fields'] ); } return $keys; } public static function is_nojs_blocked() { $settings = get_option( 'holmesforms_settings', array() ); return ! ( is_array( $settings ) && isset( $settings['nojs'] ) && 'allow' === $settings['nojs'] ); } /** @return array What stops working without JavaScript. */ public static function list_nojs_conflicts( HP_Forms_Form $form ) { $conflicts = array(); foreach ( $form->pages() as $page ) { if ( ! empty( $page['visible_when'] ) ) { $conflicts['page_rules'] = __( 'Pages with a show or hide rule are left out, so their questions are never asked.', 'holmesportfolio-forms' ); break; } } $types = array(); foreach ( $form->all_fields() as $field ) { $types[ $field['type'] ] = true; if ( ! empty( $field['visible_when'] ) ) { $conflicts['rules'] = __( 'Questions and groups with a show or hide rule don\'t change after an answer, so hidden ones never appear.', 'holmesportfolio-forms' ); } if ( ! empty( $field['config']['filter_by'] ) && in_array( $field['type'], array( 'select', 'radio', 'checkbox' ), true ) ) { $conflicts['filter'] = __( 'A list that changes with an earlier answer shows every choice. If someone picks one that doesn\'t match, they\'re asked to pick another.', 'holmesportfolio-forms' ); } } if ( isset( $types['address'] ) && '1' === (string) get_option( HP_Forms_Lookup::OPTION_ENABLED, '' ) ) { $conflicts['lookup'] = __( 'The "Find address" button does nothing, so people type their address into the boxes.', 'holmesportfolio-forms' ); } $spam_config = $form->spam_config(); $site_key = 'turnstile' === $spam_config['provider'] ? $spam_config['turnstile_site'] : ( 'recaptcha' === $spam_config['provider'] ? $spam_config['recaptcha_site'] : '' ); if ( '' !== trim( (string) $site_key ) ) { $conflicts['challenge'] = __( 'The spam check (Turnstile or reCAPTCHA) can\'t run, so the form is hidden from them anyway.', 'holmesportfolio-forms' ); } return (array) apply_filters( 'holmesforms_nojs_conflicts', $conflicts, $form, $types ); } /** Environment for server-side hidden-field resolution. */ private function logic_environment() { return array( 'query' => $this->capture_query(), 'user' => wp_get_current_user(), ); } /* Helpers */ /** @return array[] */ public function in_scope_pages( array $answers, array $variables ) { $context = array( 'fields' => $answers, 'variables' => $variables, ); $pages = array(); foreach ( $this->form->pages() as $page ) { if ( HP_Forms_Logic::is_shown( $page, $context ) && self::has_something_to_show( (array) $page['fields'], $context ) ) { $pages[] = $page; } } if ( ! $pages ) { $all_pages = $this->form->pages(); $pages[] = $all_pages[0]; } return $pages; } private static function has_something_to_show( array $fields, array $context ) { foreach ( $fields as $field ) { if ( in_array( (string) ( $field['type'] ?? '' ), array( 'hidden', 'button', 'email_action', 'submit_message' ), true ) || ! HP_Forms_Logic::is_shown( $field, $context ) ) { continue; } if ( 'container' !== $field['type'] || HP_Forms_Components::is_repeatable( $field ) || self::has_something_to_show( (array) ( $field['config']['children'] ?? array() ), $context ) ) { return true; } } return false; } private function default_value( array $field ) { if ( 'slider' === $field['type'] ) { return (string) $field['config']['min']; } if ( 'checkbox' === $field['type'] && $field['config']['multiple'] ) { return array(); } return ''; } /** @param int $level 2..6. */ private function group_heading( $text, $level ) { $level = max( self::MIN_HEADING_LEVEL, min( self::MAX_HEADING_LEVEL, (int) $level ) ); return sprintf( '%3$s', $level, esc_attr( $this->css_class( 'group_legend' ) ), esc_html( $text ) ); } private function css_class( $role ) { $classes_by_mode = array( 'gds' => array( 'button' => 'govuk-button', 'button_alt' => 'govuk-button govuk-button--secondary', 'legend' => 'govuk-fieldset__legend govuk-fieldset__legend--l', 'group_legend' => 'govuk-fieldset__legend govuk-fieldset__legend--m', ), 'bootstrap' => array( 'button' => 'btn btn-primary', 'button_alt' => 'btn btn-secondary', 'legend' => 'h4', 'group_legend' => 'h5', ), 'none' => array( 'button' => 'wp-element-button button hpf-button', 'button_alt' => 'wp-element-button button hpf-button hpf-button--secondary', 'legend' => 'hpf-legend', 'group_legend' => 'hpf-legend hpf-legend--group', ), 'plain' => array( 'button' => 'hpf-button', 'button_alt' => 'hpf-button hpf-button--secondary', 'legend' => 'hpf-legend', 'group_legend' => 'hpf-legend hpf-legend--group', ), ); $classes = isset( $classes_by_mode[ $this->mode ] ) ? $classes_by_mode[ $this->mode ] : $classes_by_mode['none']; return isset( $classes[ $role ] ) ? $classes[ $role ] : ''; } private function visually_hidden_class() { if ( 'gds' === $this->mode ) { return 'govuk-visually-hidden'; } if ( 'bootstrap' === $this->mode ) { return 'visually-hidden'; } return 'hpf-visually-hidden'; } private function current_url() { global $wp; if ( is_singular() ) { $link = get_permalink( get_queried_object_id() ); if ( $link ) { return $link; } } return home_url( add_query_arg( array(), isset( $wp->request ) ? $wp->request : '' ) ); } private function capture_query() { if ( is_array( $this->query ) ) { return $this->query; } $query = array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read only, carries the page's own query into the form foreach ( (array) $_GET as $key => $value ) { if ( ! is_scalar( $value ) ) { continue; } $query[ sanitize_key( $key ) ] = sanitize_text_field( wp_unslash( $value ) ); if ( count( $query ) >= self::MAX_QUERY_PARAMS ) { break; } } return $query; } }