plugin name. * * @return string[] */ public static function names() { return array( 'ninja' => 'Ninja Forms', 'gravity' => 'Gravity Forms', 'formidable' => 'Formidable Forms', 'fluent' => 'Fluent Forms', ); } /** * All forms of all table-based sources. * * @return array[] */ public static function find() { return array_merge( self::ninja(), self::gravity(), self::formidable(), self::fluent() ); } /** * Is there at least one form in one of the sources? * * @return bool */ public static function has_sources() { global $wpdb; $checks = array( 'nf3_forms' => 'SELECT id FROM %i LIMIT 1', 'gf_form' => 'SELECT id FROM %i WHERE is_trash = 0 LIMIT 1', 'frm_forms' => "SELECT id FROM %i WHERE is_template = 0 AND status <> 'trash' AND ( parent_form_id IS NULL OR parent_form_id = 0 ) LIMIT 1", 'fluentform_forms' => 'SELECT id FROM %i LIMIT 1', ); foreach ( $checks as $table => $sql ) { $name = $wpdb->prefix . $table; if ( self::table_exists( $name ) && $wpdb->get_var( $wpdb->prepare( $sql, $name ) ) ) { // phpcs:ignore WordPress.DB return true; } } return false; } /* ------------------------------------------------------------------ * Ninja Forms 3 * ---------------------------------------------------------------- */ /** * Forms from Ninja Forms (tables nf3_forms, nf3_fields, nf3_field_meta, nf3_actions, nf3_action_meta). * * @return array[] */ public static function ninja() { global $wpdb; $forms_t = $wpdb->prefix . 'nf3_forms'; $field_t = $wpdb->prefix . 'nf3_fields'; if ( ! self::table_exists( $forms_t ) || ! self::table_exists( $field_t ) ) { return array(); } $out = array(); $forms = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i ORDER BY id ASC', $forms_t ), ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( (array) $forms as $form ) { $id = (int) $form['id']; $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE parent_id = %d', $field_t, $id ), ARRAY_A ); // phpcs:ignore WordPress.DB $raw = array(); foreach ( (array) $rows as $row ) { $raw[] = array_merge( $row, self::ninja_meta( $wpdb->prefix . 'nf3_field_meta', (int) $row['id'] ) ); } usort( $raw, static function ( $a, $b ) { return (int) ( isset( $a['order'] ) ? $a['order'] : 0 ) - (int) ( isset( $b['order'] ) ? $b['order'] : 0 ); } ); $map = array( 'textbox' => 'text', 'firstname' => 'text', 'lastname' => 'text', 'address' => 'text', 'city' => 'text', 'zip' => 'text', 'email' => 'email', 'phone' => 'tel', 'textarea' => 'textarea', 'listselect' => 'select', 'listcountry' => 'select', 'liststate' => 'select', 'listradio' => 'radio', 'listcheckbox' => 'checkbox', 'listmultiselect' => 'checkbox', 'date' => 'date', 'number' => 'number', 'file_upload' => 'file', ); $ignore = array( 'hr', 'html', 'hidden', 'spam', 'recaptcha', 'recaptcha_v3', 'turnstile', 'note', 'confirm' ); $fields = array(); $skipped = array(); $ids = array(); $submit = ''; foreach ( $raw as $f ) { $type = isset( $f['type'] ) ? (string) $f['type'] : ''; $label = self::text( isset( $f['label'] ) ? $f['label'] : '' ); if ( 'submit' === $type ) { $submit = $label; continue; } if ( 'checkbox' === $type ) { // A single checkbox: usually "I agree …" – take it over as consent if it is required. if ( ! empty( $f['required'] ) ) { $fields[] = self::field( 'consent', self::consent_label( $label ), true, $ids ); } else { $fields[] = self::field( 'checkbox', $label, false, $ids, array( 'options' => array( '' !== $label ? $label : __( 'Yes', 'alpeg-forms' ) ) ) ); } continue; } if ( ! isset( $map[ $type ] ) ) { if ( ! in_array( $type, $ignore, true ) ) { $skipped[] = $type . ( $label ? ' (' . $label . ')' : '' ); } continue; } $extra = array( 'placeholder' => self::text( isset( $f['placeholder'] ) ? $f['placeholder'] : '' ), 'help' => self::text( isset( $f['desc_text'] ) ? $f['desc_text'] : '' ), ); $new = $map[ $type ]; if ( Fields::has_options( $new ) ) { $extra['options'] = self::option_labels( self::decode( isset( $f['options'] ) ? $f['options'] : array() ), array( 'label', 'value' ) ); if ( ! $extra['options'] ) { $skipped[] = $type . ( $label ? ' (' . $label . ')' : '' ); continue; } } if ( 'file' === $new ) { $extra['accept'] = 'both'; $types = self::text( isset( $f['upload_types'] ) ? $f['upload_types'] : '' ); if ( '' !== $types ) { $extra['accept'] = 'custom'; $extra['extensions'] = str_replace( array( '.', ',' ), array( '', ', ' ), $types ); } $extra['max_size'] = isset( $f['max_file_size'] ) && (int) $f['max_file_size'] > 0 ? (int) $f['max_file_size'] : 5; } $fields[] = self::field( $new, $label, ! empty( $f['required'] ), $ids, $extra ); } // Email and success message from the actions. $notify = ''; $subject = ''; $success = ''; $act_t = $wpdb->prefix . 'nf3_actions'; if ( self::table_exists( $act_t ) ) { $actions = $wpdb->get_results( $wpdb->prepare( 'SELECT id, type FROM %i WHERE parent_id = %d ORDER BY id ASC', $act_t, $id ), ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( (array) $actions as $a ) { $meta = self::ninja_meta( $wpdb->prefix . 'nf3_action_meta', (int) $a['id'] ); if ( 'email' === $a['type'] && '' === $notify ) { $to = isset( $meta['to'] ) ? (string) $meta['to'] : ''; $notify = self::plain_address( $to ); $subject = self::no_tags( isset( $meta['email_subject'] ) ? $meta['email_subject'] : '' ); } elseif ( 'successmessage' === $a['type'] && '' === $success ) { $success = self::no_tags( isset( $meta['success_msg'] ) ? $meta['success_msg'] : '' ); } } } $title = isset( $form['title'] ) && '' !== $form['title'] ? $form['title'] : ( isset( $form['form_title'] ) ? $form['form_title'] : '' ); $out[] = Importer::result( 'ninja', 'Ninja Forms', $id, self::text( $title ), $fields, $skipped, array( 'submitLabel' => $submit, 'notifyEmail' => $notify, 'notifySubject' => $subject, 'successMessage' => $success, ) ); } return $out; } /** * Ninja Forms meta rows (key/value or meta_key/meta_value depending on version). * * @param string $table Meta table. * @param int $parent_id Parent ID. * @return array */ private static function ninja_meta( $table, $parent_id ) { global $wpdb; if ( ! self::table_exists( $table ) ) { return array(); } $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE parent_id = %d', $table, $parent_id ), ARRAY_A ); // phpcs:ignore WordPress.DB $out = array(); foreach ( (array) $rows as $r ) { $k = isset( $r['meta_key'] ) && '' !== (string) $r['meta_key'] ? $r['meta_key'] : ( isset( $r['key'] ) ? $r['key'] : '' ); $v = isset( $r['meta_key'] ) && '' !== (string) $r['meta_key'] ? ( isset( $r['meta_value'] ) ? $r['meta_value'] : '' ) : ( isset( $r['value'] ) ? $r['value'] : '' ); if ( '' !== (string) $k ) { $out[ $k ] = $v; } } return $out; } /* ------------------------------------------------------------------ * Gravity Forms * ---------------------------------------------------------------- */ /** * Forms from Gravity Forms (tables gf_form and gf_form_meta). * * @return array[] */ public static function gravity() { global $wpdb; $forms_t = $wpdb->prefix . 'gf_form'; $meta_t = $wpdb->prefix . 'gf_form_meta'; if ( ! self::table_exists( $forms_t ) || ! self::table_exists( $meta_t ) ) { return array(); } $out = array(); $forms = $wpdb->get_results( $wpdb->prepare( 'SELECT id, title FROM %i WHERE is_trash = 0 ORDER BY id ASC', $forms_t ), ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( (array) $forms as $form ) { $id = (int) $form['id']; $meta = $wpdb->get_row( $wpdb->prepare( 'SELECT display_meta, confirmations, notifications FROM %i WHERE form_id = %d', $meta_t, $id ), ARRAY_A ); // phpcs:ignore WordPress.DB $disp = $meta ? json_decode( (string) $meta['display_meta'], true ) : null; if ( ! is_array( $disp ) ) { continue; } $map = array( 'text' => 'text', 'name' => 'text', 'address' => 'text', 'email' => 'email', 'phone' => 'tel', 'textarea' => 'textarea', 'select' => 'select', 'multiselect' => 'checkbox', 'radio' => 'radio', 'checkbox' => 'checkbox', 'number' => 'number', 'date' => 'date', 'website' => 'url', 'fileupload' => 'file', 'consent' => 'consent', ); $ignore = array( 'section', 'html', 'hidden', 'captcha', 'honeypot' ); $pages = isset( $disp['pagination']['pages'] ) && is_array( $disp['pagination']['pages'] ) ? array_values( $disp['pagination']['pages'] ) : array(); $page = 1; $fields = array(); $skipped = array(); $ids = array(); foreach ( (array) ( isset( $disp['fields'] ) ? $disp['fields'] : array() ) as $f ) { $type = isset( $f['type'] ) ? (string) $f['type'] : ''; $label = self::text( isset( $f['label'] ) ? $f['label'] : '' ); if ( 'page' === $type ) { $title = isset( $pages[ $page ] ) && '' !== trim( (string) $pages[ $page ] ) ? self::text( $pages[ $page ] ) : __( 'Next step', 'alpeg-forms' ); $fields[] = self::field( 'step', $title, false, $ids ); ++$page; continue; } if ( ! isset( $map[ $type ] ) ) { if ( ! in_array( $type, $ignore, true ) ) { $skipped[] = $type . ( $label ? ' (' . $label . ')' : '' ); } continue; } $new = $map[ $type ]; $extra = array( 'placeholder' => self::text( isset( $f['placeholder'] ) ? $f['placeholder'] : '' ), 'help' => self::no_tags( isset( $f['description'] ) ? $f['description'] : '' ), ); if ( Fields::has_options( $new ) ) { $extra['options'] = self::option_labels( isset( $f['choices'] ) ? $f['choices'] : array(), array( 'text', 'value' ) ); if ( ! $extra['options'] ) { $skipped[] = $type . ( $label ? ' (' . $label . ')' : '' ); continue; } } if ( 'consent' === $new ) { $text = isset( $f['checkboxLabel'] ) && '' !== trim( (string) $f['checkboxLabel'] ) ? $f['checkboxLabel'] : $label; $fields[] = self::field( 'consent', self::consent_label( $text ), true, $ids ); continue; } if ( 'file' === $new ) { $exts = self::text( isset( $f['allowedExtensions'] ) ? $f['allowedExtensions'] : '' ); $extra['accept'] = '' !== $exts ? 'custom' : 'both'; $extra['extensions'] = str_replace( array( '.', ',' ), array( '', ', ' ), $exts ); $extra['max_size'] = isset( $f['maxFileSize'] ) && (int) $f['maxFileSize'] > 0 ? (int) $f['maxFileSize'] : 5; $extra['max_files'] = ! empty( $f['multipleFiles'] ) ? max( 1, min( Files::MAX_FILES, (int) ( ! empty( $f['maxFiles'] ) ? $f['maxFiles'] : Files::MAX_FILES ) ) ) : 1; } $fields[] = self::field( $new, $label, ! empty( $f['isRequired'] ), $ids, $extra ); } $notify = ''; $subject = ''; $notes = $meta ? json_decode( (string) $meta['notifications'], true ) : null; if ( is_array( $notes ) && $notes ) { $n = reset( $notes ); $notify = self::plain_address( isset( $n['to'] ) ? $n['to'] : '' ); $subject = self::no_tags( isset( $n['subject'] ) ? $n['subject'] : '' ); } $success = ''; $confs = $meta ? json_decode( (string) $meta['confirmations'], true ) : null; if ( is_array( $confs ) ) { foreach ( $confs as $c ) { if ( isset( $c['type'] ) && 'message' === $c['type'] && ! empty( $c['message'] ) ) { $success = self::no_tags( $c['message'] ); if ( ! empty( $c['isDefault'] ) ) { break; } } } } $out[] = Importer::result( 'gravity', 'Gravity Forms', $id, self::text( isset( $disp['title'] ) ? $disp['title'] : $form['title'] ), $fields, $skipped, array( 'submitLabel' => self::text( isset( $disp['button']['text'] ) ? $disp['button']['text'] : '' ), 'notifyEmail' => $notify, 'notifySubject' => $subject, 'successMessage' => $success, 'firstStepTitle' => isset( $pages[0] ) ? self::text( $pages[0] ) : '', ) ); } return $out; } /* ------------------------------------------------------------------ * Formidable Forms * ---------------------------------------------------------------- */ /** * Forms from Formidable Forms (tables frm_forms, frm_fields; email actions as posts). * * @return array[] */ public static function formidable() { global $wpdb; $forms_t = $wpdb->prefix . 'frm_forms'; $field_t = $wpdb->prefix . 'frm_fields'; if ( ! self::table_exists( $forms_t ) || ! self::table_exists( $field_t ) ) { return array(); } $out = array(); $forms = $wpdb->get_results( $wpdb->prepare( "SELECT id, name, options FROM %i WHERE is_template = 0 AND status <> 'trash' AND ( parent_form_id IS NULL OR parent_form_id = 0 ) ORDER BY id ASC", $forms_t ), ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( (array) $forms as $form ) { $id = (int) $form['id']; $opts = self::decode( $form['options'] ); $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT name, description, type, options, required, field_options FROM %i WHERE form_id = %d ORDER BY field_order ASC, id ASC', $field_t, $id ), ARRAY_A ); // phpcs:ignore WordPress.DB $map = array( 'text' => 'text', 'name' => 'text', 'address' => 'text', 'email' => 'email', 'phone' => 'tel', 'textarea' => 'textarea', 'rte' => 'textarea', 'select' => 'select', 'radio' => 'radio', 'checkbox' => 'checkbox', 'number' => 'number', 'date' => 'date', 'url' => 'url', 'file' => 'file', ); $ignore = array( 'html', 'hidden', 'captcha', 'divider', 'end_divider', 'user_id', 'submit', 'summary' ); $fields = array(); $skipped = array(); $ids = array(); foreach ( (array) $rows as $f ) { $type = (string) $f['type']; $label = self::text( $f['name'] ); $fopts = self::decode( $f['field_options'] ); if ( 'break' === $type ) { $fields[] = self::field( 'step', '' !== $label && 'Page Break' !== $label ? $label : __( 'Next step', 'alpeg-forms' ), false, $ids ); continue; } if ( 'gdpr' === $type ) { $text = isset( $fopts['gdpr_text'] ) && '' !== $fopts['gdpr_text'] ? $fopts['gdpr_text'] : $label; $fields[] = self::field( 'consent', self::consent_label( $text ), true, $ids ); continue; } if ( ! isset( $map[ $type ] ) ) { if ( ! in_array( $type, $ignore, true ) ) { $skipped[] = $type . ( $label ? ' (' . $label . ')' : '' ); } continue; } $new = $map[ $type ]; $extra = array( 'placeholder' => self::text( isset( $fopts['placeholder'] ) ? $fopts['placeholder'] : '' ), 'help' => self::no_tags( $f['description'] ), ); if ( Fields::has_options( $new ) ) { $extra['options'] = self::option_labels( self::decode( $f['options'] ), array( 'label', 'value' ) ); if ( ! $extra['options'] ) { $skipped[] = $type . ( $label ? ' (' . $label . ')' : '' ); continue; } } if ( 'file' === $new ) { $extra['accept'] = 'both'; $extra['max_size'] = isset( $fopts['size'] ) && (int) $fopts['size'] > 0 ? (int) $fopts['size'] : 5; $extra['max_files'] = ! empty( $fopts['multiple'] ) ? max( 1, min( Files::MAX_FILES, (int) ( ! empty( $fopts['max'] ) ? $fopts['max'] : Files::MAX_FILES ) ) ) : 1; } $fields[] = self::field( $new, $label, ! empty( $f['required'] ), $ids, $extra ); } // Email action: post type frm_form_actions, post_excerpt "email", menu_order = form ID, JSON content. $notify = ''; $subject = ''; $action = $wpdb->get_var( $wpdb->prepare( "SELECT post_content FROM {$wpdb->posts} WHERE post_type = 'frm_form_actions' AND post_excerpt = 'email' AND menu_order = %d AND post_status = 'publish' ORDER BY ID ASC LIMIT 1", $id ) ); // phpcs:ignore WordPress.DB if ( $action ) { $a = json_decode( (string) $action, true ); $notify = self::plain_address( isset( $a['email_to'] ) ? $a['email_to'] : '' ); $subject = self::no_tags( isset( $a['email_subject'] ) ? $a['email_subject'] : '' ); } $out[] = Importer::result( 'formidable', 'Formidable Forms', $id, self::text( $form['name'] ), $fields, $skipped, array( 'submitLabel' => self::text( isset( $opts['submit_value'] ) ? $opts['submit_value'] : '' ), 'notifyEmail' => $notify, 'notifySubject' => $subject, 'successMessage' => self::no_tags( isset( $opts['success_msg'] ) ? $opts['success_msg'] : '' ), ) ); } return $out; } /* ------------------------------------------------------------------ * Fluent Forms * ---------------------------------------------------------------- */ /** * Forms from Fluent Forms (tables fluentform_forms and fluentform_form_meta). * * @return array[] */ public static function fluent() { global $wpdb; $forms_t = $wpdb->prefix . 'fluentform_forms'; $meta_t = $wpdb->prefix . 'fluentform_form_meta'; if ( ! self::table_exists( $forms_t ) ) { return array(); } $out = array(); $forms = $wpdb->get_results( $wpdb->prepare( 'SELECT id, title, form_fields FROM %i ORDER BY id ASC', $forms_t ), ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( (array) $forms as $form ) { $id = (int) $form['id']; $data = json_decode( (string) $form['form_fields'], true ); if ( ! is_array( $data ) ) { continue; } $state = array( 'fields' => array(), 'skipped' => array(), 'ids' => array(), 'titles' => isset( $data['stepsWrapper']['stepStart']['settings']['step_titles'] ) ? array_values( (array) $data['stepsWrapper']['stepStart']['settings']['step_titles'] ) : array(), 'step' => 1, ); self::fluent_walk( isset( $data['fields'] ) ? (array) $data['fields'] : array(), $state ); $notify = ''; $subject = ''; $success = ''; if ( self::table_exists( $meta_t ) ) { $metas = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, value FROM %i WHERE form_id = %d AND meta_key IN ('notifications','formSettings') ORDER BY id ASC", $meta_t, $id ), ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( (array) $metas as $m ) { $v = json_decode( (string) $m['value'], true ); if ( ! is_array( $v ) ) { continue; } if ( 'notifications' === $m['meta_key'] && '' === $notify && ( ! isset( $v['enabled'] ) || $v['enabled'] ) ) { $notify = self::plain_address( isset( $v['sendTo']['email'] ) ? $v['sendTo']['email'] : '' ); $subject = self::no_tags( isset( $v['subject'] ) ? $v['subject'] : '' ); } elseif ( 'formSettings' === $m['meta_key'] && isset( $v['confirmation']['messageToShow'] ) ) { $success = self::no_tags( $v['confirmation']['messageToShow'] ); } } } $first = isset( $state['titles'][0] ) ? self::text( $state['titles'][0] ) : ''; $out[] = Importer::result( 'fluent', 'Fluent Forms', $id, self::text( $form['title'] ), $state['fields'], $state['skipped'], array( 'submitLabel' => self::text( isset( $data['submitButton']['settings']['button_ui']['text'] ) ? $data['submitButton']['settings']['button_ui']['text'] : '' ), 'notifyEmail' => $notify, 'notifySubject' => $subject, 'successMessage' => $success, 'firstStepTitle' => $first, ) ); } return $out; } /** * Walk the Fluent Forms field tree (containers hold columns with fields). * * @param array $elements Elements. * @param array $state Collected fields, skipped, IDs, step titles (by reference). */ private static function fluent_walk( array $elements, array &$state ) { $map = array( 'input_text' => 'text', 'input_name' => 'text', 'address' => 'text', 'input_email' => 'email', 'phone' => 'tel', 'textarea' => 'textarea', 'select' => 'select', 'select_country' => 'select', 'input_radio' => 'radio', 'input_checkbox' => 'checkbox', 'multi_select' => 'checkbox', 'input_number' => 'number', 'input_date' => 'date', 'input_url' => 'url', 'input_file' => 'file', 'input_image' => 'file', ); $ignore = array( 'input_hidden', 'custom_html', 'section_break', 'recaptcha', 'hcaptcha', 'turnstile', 'custom_submit_button', 'shortcode', 'action_hook' ); foreach ( $elements as $el ) { if ( ! is_array( $el ) || empty( $el['element'] ) ) { continue; } $type = (string) $el['element']; $settings = isset( $el['settings'] ) && is_array( $el['settings'] ) ? $el['settings'] : array(); $attrs = isset( $el['attributes'] ) && is_array( $el['attributes'] ) ? $el['attributes'] : array(); if ( 'container' === $type ) { foreach ( (array) ( isset( $el['columns'] ) ? $el['columns'] : array() ) as $col ) { self::fluent_walk( isset( $col['fields'] ) ? (array) $col['fields'] : array(), $state ); } continue; } if ( 'form_step' === $type ) { $title = isset( $state['titles'][ $state['step'] ] ) ? self::text( $state['titles'][ $state['step'] ] ) : ''; $state['fields'][] = self::field( 'step', '' !== $title ? $title : __( 'Next step', 'alpeg-forms' ), false, $state['ids'] ); ++$state['step']; continue; } $label = self::text( isset( $settings['label'] ) && '' !== $settings['label'] ? $settings['label'] : ( isset( $settings['admin_field_label'] ) ? $settings['admin_field_label'] : '' ) ); $req = ! empty( $settings['validation_rules']['required']['value'] ); if ( 'terms_and_condition' === $type || 'gdpr_agreement' === $type ) { $text = isset( $settings['tnc_html'] ) && '' !== $settings['tnc_html'] ? $settings['tnc_html'] : $label; $state['fields'][] = self::field( 'consent', self::consent_label( $text ), true, $state['ids'] ); continue; } if ( ! isset( $map[ $type ] ) ) { if ( ! in_array( $type, $ignore, true ) ) { $state['skipped'][] = $type . ( $label ? ' (' . $label . ')' : '' ); } continue; } $new = $map[ $type ]; if ( 'input_name' === $type ) { // Name field = several inputs (first, last …): one "Name" field. $label = '' !== $label ? $label : __( 'Name', 'alpeg-forms' ); foreach ( (array) ( isset( $el['fields'] ) ? $el['fields'] : array() ) as $sub ) { if ( ! empty( $sub['settings']['validation_rules']['required']['value'] ) ) { $req = true; } } } $extra = array( 'placeholder' => self::text( isset( $attrs['placeholder'] ) ? $attrs['placeholder'] : ( isset( $settings['placeholder'] ) ? $settings['placeholder'] : '' ) ), 'help' => self::no_tags( isset( $settings['help_message'] ) ? $settings['help_message'] : '' ), ); if ( Fields::has_options( $new ) ) { $opts = array(); if ( ! empty( $settings['advanced_options'] ) && is_array( $settings['advanced_options'] ) ) { $opts = self::option_labels( $settings['advanced_options'], array( 'label', 'value' ) ); } elseif ( ! empty( $el['options'] ) && is_array( $el['options'] ) ) { $opts = self::option_labels( array_values( $el['options'] ), array() ); } if ( ! $opts ) { $state['skipped'][] = $type . ( $label ? ' (' . $label . ')' : '' ); continue; } $extra['options'] = $opts; } if ( 'file' === $new ) { $rules = isset( $settings['validation_rules'] ) ? $settings['validation_rules'] : array(); $bytes = isset( $rules['max_file_size']['value'] ) ? (int) $rules['max_file_size']['value'] : 0; $extra['max_size'] = $bytes > 0 ? max( 1, (int) ceil( $bytes / MB_IN_BYTES ) ) : 5; $extra['max_files'] = max( 1, min( Files::MAX_FILES, (int) ( isset( $rules['max_file_count']['value'] ) ? $rules['max_file_count']['value'] : 1 ) ) ); $types = isset( $rules['allowed_file_types']['value'] ) ? (array) $rules['allowed_file_types']['value'] : array(); if ( 'input_image' === $type ) { $extra['accept'] = 'images'; } elseif ( $types ) { $exts = array_filter( array_map( 'sanitize_key', explode( '|', implode( '|', array_map( 'strval', $types ) ) ) ) ); $extra['accept'] = 'custom'; $extra['extensions'] = implode( ', ', $exts ); } else { $extra['accept'] = 'both'; } } $state['fields'][] = self::field( $new, $label, $req, $state['ids'], $extra ); } } /* ------------------------------------------------------------------ * Helpers * ---------------------------------------------------------------- */ /** * Build a field in ALPEG Forms format. * * @param string $type Field type. * @param string $label Label. * @param bool $required Required. * @param array $ids IDs used so far (by reference). * @param array $extra Additional properties. * @return array */ private static function field( $type, $label, $required, array &$ids, array $extra = array() ) { $label = '' !== $label ? $label : ucfirst( $type ); $id = Importer::unique_id( 'consent' === $type ? 'consent' : ( 'step' === $type ? 'step' : $label ), $ids ); return array_merge( array( 'id' => $id, 'type' => $type, 'label' => $label, 'required' => (bool) $required, 'placeholder' => '', 'help' => '', 'width' => 'full', 'options' => array(), ), $extra ); } /** * Consent text: keeps links, falls back to the default text. * * @param string $text Text from the source plugin. * @return string */ private static function consent_label( $text ) { $text = trim( wp_kses( (string) $text, Fields::consent_kses() ) ); return '' !== $text ? $text : __( 'I agree that my details will be stored to process my request. More information in the privacy policy.', 'alpeg-forms' ); } /** * Option labels from a list of choices. * * @param mixed $choices Choices (strings or arrays). * @param string[] $keys Keys to try in order. * @return string[] */ private static function option_labels( $choices, array $keys ) { $out = array(); foreach ( (array) $choices as $c ) { $label = ''; if ( is_array( $c ) ) { foreach ( $keys as $k ) { if ( isset( $c[ $k ] ) && '' !== trim( (string) $c[ $k ] ) ) { $label = (string) $c[ $k ]; break; } } } elseif ( is_scalar( $c ) ) { $label = (string) $c; } $label = self::text( $label ); if ( '' !== $label ) { $out[] = $label; } } return array_values( array_unique( $out ) ); } /** * Plain text from a value (HTML removed). * * @param mixed $value Value. * @return string */ private static function text( $value ) { return is_scalar( $value ) ? trim( sanitize_text_field( wp_strip_all_tags( (string) $value ) ) ) : ''; } /** * Text without merge tags of the source plugin ({…} or […]); empty if only tags remain. * * @param mixed $value Value. * @return string */ private static function no_tags( $value ) { $text = is_scalar( $value ) ? trim( wp_strip_all_tags( (string) $value ) ) : ''; if ( preg_match( '/\{[^}]*\}|\[[a-z0-9_ -]+\]/i', $text ) ) { return ''; } return sanitize_textarea_field( $text ); } /** * A single plain email address (merge tags like {admin_email} are dropped – our default applies). * * @param mixed $value Value. * @return string */ private static function plain_address( $value ) { $value = is_scalar( $value ) ? trim( (string) $value ) : ''; $first = trim( (string) strtok( $value, ',;' ) ); return is_email( $first ) ? $first : ''; } /** * Decode stored settings: arrays stay, JSON is decoded, serialized data is read * without allowing any objects (no object injection). * * @param mixed $value Value. * @return array */ public static function decode( $value ) { if ( is_array( $value ) ) { return $value; } $value = (string) $value; if ( '' === $value ) { return array(); } if ( is_serialized( $value ) ) { $data = @unserialize( $value, array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize,WordPress.PHP.NoSilencedErrors.Discouraged -- Data written by another plugin; objects are not allowed. return is_array( $data ) ? $data : array(); } $data = json_decode( $value, true ); return is_array( $data ) ? $data : array(); } /** * Does a table exist? * * @param string $table Full table name. * @return bool */ public static function table_exists( $table ) { global $wpdb; static $cache = array(); if ( ! isset( $cache[ $table ] ) ) { $cache[ $table ] = $table === $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) ) ); // phpcs:ignore WordPress.DB } return $cache[ $table ]; } }