0 ? add_query_arg( 'form', $form_id, $url ) : $url; } /** * Adds "Get started" to the plugin's row on the Plugins screen. * * @param array $links Action links. * @return array */ public static function action_links( array $links ): array { if ( current_user_can( self::capability() ) ) { $links = array( 'tflexforms-get-started' => sprintf( '%s', esc_url( self::url() ), esc_html__( 'Get started', 'flexsolutions-form-builder' ) ) ) + $links; } return $links; } /** * Loads the guide's styles and copy button on its page. * * @param string $hook_suffix Current admin page. */ public static function enqueue( string $hook_suffix ): void { if ( Form_Post_Type::POST_TYPE . '_page_' . self::PAGE !== $hook_suffix ) { return; } wp_enqueue_style( 'tflexforms-admin', plugins_url( 'assets/css/admin.css', PLUGIN_FILE ), array(), VERSION ); wp_enqueue_style( 'tflexforms-onboarding', plugins_url( 'assets/css/onboarding.css', PLUGIN_FILE ), array( 'tflexforms-admin' ), VERSION ); wp_enqueue_script( 'tflexforms-onboarding', plugins_url( 'assets/js/onboarding.js', PLUGIN_FILE ), array(), VERSION, array( 'in_footer' => true ) ); wp_localize_script( 'tflexforms-onboarding', 'tflexformsOnboarding', array( 'copied' => __( 'Shortcode copied.', 'flexsolutions-form-builder' ), 'failed' => __( 'Copying failed. Select the shortcode and copy it yourself.', 'flexsolutions-form-builder' ), ) ); } /** * Where the site stands, for one form (the chosen one, else the most * recently changed published form, else the newest draft). * * @param int $form_id Chosen form ID, or 0. * @return array{form:?\WP_Post,forms:\WP_Post[],published:bool,embeds:\WP_Post[],live:bool,entry:?object,spam:?object,complete:bool} */ public static function state( int $form_id = 0 ): array { $forms = get_posts( array( 'post_type' => Form_Post_Type::POST_TYPE, 'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ), 'numberposts' => 50, 'orderby' => 'modified', 'order' => 'DESC', ) ); $forms = array_values( array_filter( $forms, static fn( \WP_Post $post ): bool => current_user_can( 'edit_post', $post->ID ) ) ); $form = null; foreach ( $forms as $post ) { if ( $post->ID === $form_id ) { $form = $post; } } if ( null === $form ) { $published = array_values( array_filter( $forms, static fn( \WP_Post $post ): bool => 'publish' === $post->post_status ) ); $form = $published[0] ?? $forms[0] ?? null; } $published = null !== $form && 'publish' === $form->post_status; $embeds = null !== $form ? self::embeds( $form->ID ) : array(); $live = array() !== array_filter( $embeds, static fn( \WP_Post $post ): bool => 'publish' === $post->post_status ); $entry = null; $spam = null; if ( $published ) { $entry = Entry_Store::query( array( 'form_id' => $form->ID, 'per_page' => 1, ) )['items'][0] ?? null; if ( null === $entry ) { $spam = Entry_Store::query( array( 'form_id' => $form->ID, 'status' => Entry_Store::SPAM, 'per_page' => 1, ) )['items'][0] ?? null; } } return array( 'form' => $form, 'forms' => $forms, 'published' => $published, 'embeds' => $embeds, 'live' => $live, 'entry' => $entry, 'spam' => $spam, 'complete' => $published && null !== $entry, ); } /** * Posts and pages whose content shows a form, through the block or the * shortcode (published, scheduled, private, pending and drafts). * * @param int $form_id Form ID. * @return \WP_Post[] */ public static function embeds( int $form_id ): array { global $wpdb; $types = array_values( array_diff( get_post_types( array( 'public' => true ) ), array( 'attachment' ) ) ); if ( array() === $types ) { return array(); } // A LIKE pre-filter finds both "[tflexforms" and "wp:flexsolutions-form-builder/form"; the // exact form is checked below. Never cached: the guide reads it live. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $ids = $wpdb->get_col( $wpdb->prepare( 'SELECT ID FROM %i WHERE post_status IN ( %s, %s, %s, %s, %s ) AND post_type IN ( ' . implode( ', ', array_fill( 0, count( $types ), '%s' ) ) . ' ) AND ( post_content LIKE %s OR post_content LIKE %s ) ORDER BY post_modified DESC LIMIT 100', // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Only placeholders are added. array_merge( array( $wpdb->posts, 'publish', 'future', 'private', 'pending', 'draft' ), $types, array( '%[' . $wpdb->esc_like( Shortcode::TAG ) . '%', '%' . $wpdb->esc_like( 'wp:flexsolutions-form-builder/form' ) . '%' ) ) ) ); $found = array(); foreach ( (array) $ids as $id ) { $post = get_post( (int) $id ); if ( $post instanceof \WP_Post && in_array( $form_id, self::forms_in( $post->post_content ), true ) ) { $found[] = $post; } if ( count( $found ) >= 5 ) { break; } } return $found; } /** * IDs of the forms a post's content shows. * * @param string $content Post content. * @return int[] */ public static function forms_in( string $content ): array { $ids = array(); if ( preg_match_all( '/' . get_shortcode_regex( array( Shortcode::TAG ) ) . '/', $content, $matches, PREG_SET_ORDER ) ) { foreach ( $matches as $match ) { if ( '[' === $match[1] && ']' === $match[6] ) { continue; // [[tflexforms]] is an escaped, printed shortcode. } $atts = shortcode_parse_atts( $match[3] ); $ids[] = is_array( $atts ) ? absint( $atts['id'] ?? 0 ) : 0; } } if ( has_blocks( $content ) ) { $walk = static function ( array $blocks ) use ( &$walk, &$ids ): void { foreach ( $blocks as $block ) { if ( 'flexsolutions-form-builder/form' === $block['blockName'] ) { $ids[] = absint( $block['attrs']['formId'] ?? 0 ); } $walk( $block['innerBlocks'] ); } }; $walk( parse_blocks( $content ) ); } return array_values( array_unique( array_filter( $ids ) ) ); } /** * Creates a draft page holding the form's block and opens it in the editor. */ public static function create_page(): void { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Checked by check_admin_referer() with this ID. $form_id = isset( $_POST['form'] ) ? absint( wp_unslash( $_POST['form'] ) ) : 0; check_admin_referer( self::CREATE_PAGE . '_' . $form_id ); $form = get_post( $form_id ); if ( ! $form instanceof \WP_Post || Form_Post_Type::POST_TYPE !== $form->post_type ) { wp_die( esc_html__( 'Form not found.', 'flexsolutions-form-builder' ), '', array( 'response' => 404 ) ); } if ( ! current_user_can( 'edit_pages' ) || ! current_user_can( 'edit_post', $form_id ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to create pages.', 'flexsolutions-form-builder' ), '', array( 'response' => 403 ) ); } $page_id = wp_insert_post( array( 'post_type' => 'page', 'post_status' => 'draft', 'post_title' => $form->post_title, 'post_content' => serialize_block( array( 'blockName' => 'flexsolutions-form-builder/form', 'attrs' => array( 'formId' => $form_id ), 'innerBlocks' => array(), 'innerHTML' => '', 'innerContent' => array(), ) ), ), true ); if ( is_wp_error( $page_id ) ) { wp_die( esc_html( $page_id->get_error_message() ), '', array( 'response' => 500 ) ); } wp_safe_redirect( (string) get_edit_post_link( $page_id, 'raw' ) ); exit; } /** * Hides the guide's notices for the current user. */ public static function dismiss(): void { check_admin_referer( self::DISMISS ); if ( ! current_user_can( self::capability() ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to do that.', 'flexsolutions-form-builder' ), '', array( 'response' => 403 ) ); } update_user_meta( get_current_user_id(), self::DISMISSED_META, 1 ); $back = wp_get_referer(); wp_safe_redirect( false !== $back ? $back : admin_url( 'edit.php?post_type=' . Form_Post_Type::POST_TYPE ) ); exit; } /** * On the forms list and the form editor, names the next step until the * site has a published form with an entry or the user hides the guide. */ public static function notice(): void { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( null === $screen || Form_Post_Type::POST_TYPE !== $screen->post_type || ! in_array( $screen->base, array( 'edit', 'post' ), true ) || 'add' === $screen->action ) { return; } if ( ! current_user_can( self::capability() ) || get_user_meta( get_current_user_id(), self::DISMISSED_META, true ) ) { return; } $post = 'post' === $screen->base ? get_post() : null; $state = self::state( null !== $post ? $post->ID : 0 ); if ( $state['complete'] ) { return; } if ( null === $state['form'] ) { $text = __( 'New to Flexsolutions Form Builder? The Get Started guide takes you from a template to your first entry in three steps.', 'flexsolutions-form-builder' ); } elseif ( ! $state['published'] ) { $text = __( 'Next step: publish your form, then the Get Started guide shows you how to put it on a page.', 'flexsolutions-form-builder' ); } else { $text = __( 'Your form is published. Next step: put it on a page and send a test entry.', 'flexsolutions-form-builder' ); } $dismiss = wp_nonce_url( admin_url( 'admin-post.php?action=' . self::DISMISS ), self::DISMISS ); ?>

$state['published'], 2 => $state['live'], 3 => null !== $state['entry'], ); $done = count( array_filter( $steps ) ); ?>

1 && null !== $form ) : ?>

$state See state(). */ private static function step_form( array $state ): void { $form = $state['form']; if ( null === $form ) { echo '

' . esc_html__( 'Pick a template to start from. Each one comes with its fields, the notification email and the messages visitors see, and you can change all of it.', 'flexsolutions-form-builder' ) . '

'; echo '