key ] = sanitize_text_field( $value ); } $option = get_option( 'wpforms_challenge' ); $option = ! $option || ! is_array( $option ) ? $schema : $option; update_option( 'wpforms_challenge', array_merge( $option, $replace ) ); } /** * Check if any forms are present on a site. * * @since 1.5.0 * * @retun bool */ public function website_has_forms() { return (bool) wpforms()->get( 'form' )->get( '', [ 'numberposts' => 1, 'nopaging' => false, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'suppress_filters' => true, ] ); } /** * Check if Challenge was started. * * @since 1.5.0 * * @return bool */ public function challenge_started() { return $this->get_challenge_option( 'status' ) === 'started'; } /** * Check if Challenge was initialized. * * @since 1.6.2 * * @return bool */ public function challenge_inited() { return $this->get_challenge_option( 'status' ) === 'inited'; } /** * Check if Challenge was paused. * * @since 1.6.2 * * @return bool */ public function challenge_paused() { return $this->get_challenge_option( 'status' ) === 'paused'; } /** * Check if Challenge was finished. * * @since 1.5.0 * * @return bool */ public function challenge_finished() { $status = $this->get_challenge_option( 'status' ); return in_array( $status, [ 'completed', 'canceled', 'skipped' ], true ); } /** * Check if Challenge is in progress. * * @since 1.5.0 * * @return bool */ public function challenge_active() { return ( $this->challenge_inited() || $this->challenge_started() || $this->challenge_paused() ) && ! $this->challenge_finished(); } /** * Force Challenge to start. * * @since 1.6.2 * * @return bool */ public function challenge_force_start() { /** * Allow force start Challenge for testing purposes. * * @since 1.6.2.2 * * @param bool $is_forced True if Challenge should be started. False by default. */ return (bool) apply_filters( 'wpforms_admin_challenge_force_start', false ); } /** * Check if Challenge can be started. * * @since 1.5.0 * * @return bool */ public function challenge_can_start() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh static $can_start = null; if ( $can_start !== null ) { return $can_start; } if ( $this->challenge_force_skip() ) { $can_start = false; } // Challenge is only available for WPForms admin pages. if ( ! wpforms_is_admin_page() && ! wpforms_is_admin_page( 'builder' ) ) { $can_start = false; return $can_start; } if ( $this->challenge_force_start() && ! $this->is_builder_page() && ! $this->is_form_embed_page() ) { $can_start = true; // No need to check something else in this case. return $can_start; } if ( $this->challenge_finished() ) { $can_start = false; } if ( $this->website_has_forms() ) { $can_start = false; } if ( $can_start === null ) { $can_start = true; } return $can_start; } /** * Start the Challenge in Form Builder. * * @since 1.5.0 */ public function init_challenge() { if ( ! $this->challenge_can_start() ) { return; } $this->set_challenge_option( wp_parse_args( [ 'status' => 'inited' ], $this->get_challenge_option_schema() ) ); } /** * Include Challenge HTML. * * @since 1.5.0 */ public function challenge_html() { if ( $this->challenge_force_skip() || ( $this->challenge_finished() && ! $this->challenge_force_start() ) ) { return; } if ( wpforms_is_admin_page() && ! wpforms_is_admin_page( 'getting-started' ) && $this->challenge_can_start() ) { // Before showing the Challenge in the `start` state we should reset the option. // In this way we ensure the Challenge will not appear somewhere in the builder where it is not should be. $this->set_challenge_option( [ 'status' => '' ] ); $this->challenge_modal_html( 'start' ); } if ( $this->is_builder_page() ) { $this->challenge_modal_html( 'progress' ); $this->challenge_builder_templates_html(); } if ( $this->is_form_embed_page() ) { $this->challenge_modal_html( 'progress' ); $this->challenge_embed_templates_html(); } } /** * Include Challenge main modal window HTML. * * @since 1.5.0 * * @param string $state State of Challenge ('start' or 'progress'). */ public function challenge_modal_html( $state ) { echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 'admin/challenge/modal', [ 'state' => $state, 'step' => $this->get_challenge_option( 'step' ), 'minutes' => $this->minutes, ], true ); } /** * Include Challenge HTML templates specific to Form Builder. * * @since 1.5.0 */ public function challenge_builder_templates_html() { echo wpforms_render( 'admin/challenge/builder' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Include Challenge HTML templates specific to form embed page. * * @since 1.5.0 */ public function challenge_embed_templates_html() { /** * Filter the content of the Challenge Congrats popup footer. * * @since 1.7.4 * * @param string $footer Footer markup. */ $congrats_popup_footer = apply_filters( 'wpforms_admin_challenge_embed_template_congrats_popup_footer', '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo wpforms_render( 'admin/challenge/embed', [ 'minutes' => $this->minutes, 'congrats_popup_footer' => $congrats_popup_footer, ], true ); } /** * Include Challenge CTA on WPForms welcome activation screen. * * @since 1.5.0 */ public function welcome_html() { if ( $this->challenge_can_start() ) { echo wpforms_render( 'admin/challenge/welcome' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } /** * Save Challenge data via AJAX. * * @since 1.5.0 */ public function save_challenge_option_ajax() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh check_admin_referer( 'wpforms_challenge_ajax_nonce' ); if ( empty( $_POST['option_data'] ) ) { wp_send_json_error(); } $schema = $this->get_challenge_option_schema(); $query = []; foreach ( $schema as $key => $value ) { if ( isset( $_POST['option_data'][ $key ] ) ) { $query[ $key ] = sanitize_text_field( wp_unslash( $_POST['option_data'][ $key ] ) ); } } if ( empty( $query ) ) { wp_send_json_error(); } if ( ! empty( $query['status'] ) && $query['status'] === 'started' ) { $query['started_date_gmt'] = current_time( 'mysql', true ); } if ( ! empty( $query['status'] ) && in_array( $query['status'], [ 'completed', 'canceled', 'skipped' ], true ) ) { $query['finished_date_gmt'] = current_time( 'mysql', true ); } if ( ! empty( $query['status'] ) && $query['status'] === 'skipped' ) { $query['started_date_gmt'] = current_time( 'mysql', true ); $query['finished_date_gmt'] = $query['started_date_gmt']; } $this->set_challenge_option( $query ); wp_send_json_success(); } /** * Send contact form to wpforms.com via AJAX. * * @since 1.5.0 */ public function send_contact_form_ajax() { check_admin_referer( 'wpforms_challenge_ajax_nonce' ); $url = 'https://wpforms.com/wpforms-challenge-feedback/'; $message = ! empty( $_POST['contact_data']['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['contact_data']['message'] ) ) : ''; $email = ''; if ( ( ! empty( $_POST['contact_data']['contact_me'] ) && $_POST['contact_data']['contact_me'] === 'true' ) || wpforms()->is_pro() ) { $current_user = wp_get_current_user(); $email = $current_user->user_email; $this->set_challenge_option( [ 'feedback_contact_me' => true ] ); } if ( empty( $message ) && empty( $email ) ) { wp_send_json_error(); } $data = [ 'body' => [ 'wpforms' => [ 'id' => 296355, 'submit' => 'wpforms-submit', 'fields' => [ 2 => $message, 3 => $email, 4 => $this->get_challenge_license_type(), 5 => wpforms()->version, 6 => wpforms_get_license_key(), ], ], ], ]; $response = wp_remote_post( $url, $data ); if ( is_wp_error( $response ) ) { wp_send_json_error(); } $this->set_challenge_option( [ 'feedback_sent' => true ] ); wp_send_json_success(); } /** * Get the current WPForms license type as it pertains to the challenge feedback form. * * @since 1.8.1 * * @return string The currently active license type. */ private function get_challenge_license_type() { $license_type = wpforms_get_license_type(); if ( $license_type === false ) { $license_type = wpforms()->is_pro() ? 'Unknown' : 'Lite'; } return ucfirst( $license_type ); } /** * Force WPForms Challenge to skip. * * @since 1.7.6 * * @return bool */ private function challenge_force_skip() { return defined( 'WPFORMS_SKIP_CHALLENGE' ) && WPFORMS_SKIP_CHALLENGE; } }