: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( $this->challenge_finished() ) {
if ( $this->website_has_forms() ) {
if ( $can_start === null ) {
* Start the Challenge in Form Builder.
public function init_challenge() {
if ( ! $this->challenge_can_start() ) {
$this->set_challenge_option(
[ 'status' => 'inited' ],
$this->get_challenge_option_schema()
* Include Challenge HTML.
public function challenge_html() {
if ( $this->challenge_force_skip() || ( $this->challenge_finished() && ! $this->challenge_force_start() ) ) {
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.
* @param string $state State of Challenge ('start' or 'progress').
public function challenge_modal_html( $state ) {
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'step' => $this->get_challenge_option( 'step' ),
'minutes' => $this->minutes,
* Include Challenge HTML templates specific to Form Builder.
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.
public function challenge_embed_templates_html() {
* Filter the content of the Challenge Congrats popup footer.
* @param string $footer Footer markup.
$congrats_popup_footer = apply_filters( 'wpforms_admin_challenge_embed_template_congrats_popup_footer', '' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'minutes' => $this->minutes,
'congrats_popup_footer' => $congrats_popup_footer,
* Include Challenge CTA on WPForms welcome activation screen.
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.
public function save_challenge_option_ajax() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
check_admin_referer( 'wpforms_challenge_ajax_nonce' );
if ( empty( $_POST['option_data'] ) ) {
$schema = $this->get_challenge_option_schema();
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['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 );
* Send contact form to wpforms.com via AJAX.
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'] ) ) : '';
( ! empty( $_POST['contact_data']['contact_me'] ) && $_POST['contact_data']['contact_me'] === 'true' )
$current_user = wp_get_current_user();
$email = $current_user->user_email;
$this->set_challenge_option( [ 'feedback_contact_me' => true ] );
if ( empty( $message ) && empty( $email ) ) {
'submit' => 'wpforms-submit',
4 => $this->get_challenge_license_type(),
6 => wpforms_get_license_key(),
$response = wp_remote_post( $url, $data );
if ( is_wp_error( $response ) ) {
$this->set_challenge_option( [ 'feedback_sent' => true ] );
* Get the current WPForms license type as it pertains to the challenge feedback form.
* @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.
private function challenge_force_skip() {
return defined( 'WPFORMS_SKIP_CHALLENGE' ) && WPFORMS_SKIP_CHALLENGE;