'text', 'text' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'richtext' => 'textarea', 'select' => 'select', 'radio' => 'radio', 'checkbox' => 'checkbox', 'gdpr-checkbox' => 'checkbox', 'number' => 'number', 'number-slider' => 'number', 'rating' => 'number', 'net_promoter_score' => 'number', 'phone' => 'tel', 'address' => 'text', 'date-time' => 'date', 'url' => 'url', 'hidden' => 'hidden', 'likert_scale' => 'other', ); /** * {@inheritDoc} */ public function slug() { return 'wpforms'; } /** * {@inheritDoc} */ public function name() { return 'WPForms'; } /** * {@inheritDoc} */ public function is_available() { return defined( 'WPFORMS_VERSION' ) && function_exists( 'wpforms' ); } /** * {@inheritDoc} */ public function forms() { $posts = get_posts( array( 'post_type' => 'wpforms', 'post_status' => 'publish', 'posts_per_page' => self::MAX_FORMS, 'orderby' => 'title', 'order' => 'ASC', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); $forms = array(); foreach ( (array) $posts as $post ) { $forms[] = array( 'id' => (string) $post->ID, 'title' => self::plain( $post->post_title ), ); } return $forms; } /** * {@inheritDoc} */ public function fields( $form_id ) { $form_data = $this->form_data( $form_id ); $fields = array(); foreach ( $this->definitions( $form_data ) as $id => $field ) { $fields[] = array( 'name' => (string) $id, 'label' => $this->label( $field, $id, true ), 'type' => self::TYPE_MAP[ $field['type'] ], ); } return $fields; } /** * {@inheritDoc} */ public function register() { if ( ! $this->claim( 'hooks' ) ) { return; } /* * `wpforms_process_complete` ($fields, $entry, $form_data, $entry_id). WPForms_Process::process() * calls process_complete() only after validation, the honeypot/anti-spam token, Akismet, the * country and keyword filters and the time limit have passed, after the entry is saved and the * notification emails were attempted (a failed mail does not stop it), and never for an entry * marked as spam (`wpforms_process_anti_spam_entry_marked_as_spam` fires instead). AJAX and plain * POST submissions both run process(). `$fields` holds the formatted values (label as `name`, * choices joined with "\n"). */ add_action( 'wpforms_process_complete', array( $this, 'handle' ), 10, 4 ); } /** * Relay one completed WPForms submission. * * @param array $fields Formatted fields, keyed by field ID. * @param array $entry The raw submission ($_POST['wpforms']). * @param array $form_data The form's settings and field definitions. * @param int|string $entry_id The saved entry's ID (0 on Lite, which stores no entries). * @return void */ public function handle( $fields, $entry = array(), $form_data = array(), $entry_id = 0 ) { try { if ( ! is_array( $fields ) || ! is_array( $form_data ) || empty( $form_data['id'] ) ) { return; } $form_id = (string) $form_data['id']; $key = $form_id . ':' . ( $entry_id ? (string) $entry_id : md5( serialize( $fields ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize if ( ! $this->claim( 'entry:' . $key ) ) { return; } $values = array(); foreach ( $this->definitions( $form_data ) as $id => $field ) { if ( ! isset( $fields[ $id ] ) || ! is_array( $fields[ $id ] ) ) { continue; } $value = isset( $fields[ $id ]['value'] ) ? $fields[ $id ]['value'] : ''; // Checkboxes and multiple dropdowns arrive as one string, choices joined with "\n". if ( is_string( $value ) && ( 'checkbox' === $field['type'] || ( 'select' === $field['type'] && ! empty( $field['multiple'] ) ) ) ) { $value = explode( "\n", $value ); } $label = ! empty( $fields[ $id ]['name'] ) ? $fields[ $id ]['name'] : $this->label( $field, $id, false ); self::push( $values, $id, $label, self::TYPE_MAP[ $field['type'] ], $value ); } $title = isset( $form_data['settings']['form_title'] ) ? $form_data['settings']['form_title'] : ''; $this->send( $form_id, self::plain( $title ), $values ); } catch ( \Throwable $e ) { $this->fail( $e ); } } /** * A form's decoded settings and field definitions. * * @param string $form_id The form's post ID. * @return array */ private function form_data( $form_id ) { $post = (int) $form_id ? get_post( (int) $form_id ) : null; if ( ! $post || 'wpforms' !== $post->post_type ) { return array(); } $data = function_exists( 'wpforms_decode' ) ? wpforms_decode( $post->post_content ) : json_decode( $post->post_content, true ); return is_array( $data ) ? $data : array(); } /** * The answer fields of a form, in form order, keyed by field ID as a string. * * @param array $form_data The form's settings and field definitions. * @return array */ private function definitions( array $form_data ) { $definitions = array(); if ( empty( $form_data['fields'] ) || ! is_array( $form_data['fields'] ) ) { return $definitions; } foreach ( $form_data['fields'] as $key => $field ) { if ( ! is_array( $field ) || empty( $field['type'] ) ) { continue; } $type = (string) $field['type']; if ( ! isset( self::TYPE_MAP[ $type ] ) ) { continue; } $id = isset( $field['id'] ) ? (string) $field['id'] : (string) $key; $definitions[ $id ] = $field; } return $definitions; } /** * A field's label, or a stand-in when the founder left it empty: translated for the settings screen, * English when submitted (a submitted label must not change with the visitor's locale). * * @param array $field The field definition. * @param string $id The field ID. * @param bool $translate Whether the stand-in is for the settings screen. * @return string */ private function label( array $field, $id, $translate ) { $label = isset( $field['label'] ) ? self::plain( $field['label'] ) : ''; if ( '' !== $label ) { return $label; } /* translators: %s: a WPForms field ID. */ return $translate ? sprintf( __( 'Field %s', 'launchlist' ), $id ) : sprintf( 'Field %s', $id ); } }