Wordpress wizard in admin - step by step - createIT
Get a free advice now!

    Pick the topic
    Developer OutsourcingWeb developingApp developingDigital MarketingeCommerce systemseEntertainment systems

    Thank you for your message. It has been sent.

    WordPress wizard in admin – step by step

    August 29, 2024
    Last update: September 26, 2024
    6 min read
    1117
    0
    0
    WordPress wizard in admin – step by step

    Challenge: create wizard configurator in wp-admin

    Solution: add custom admin pages, redirect forms to next step, add JS submit and hide main menu

    Wizard configurator in wordpress dashboard area is nice thing to have. Undistracted Administrator can follow wizard steps and configure plugin settings one by one. Nowadays a lot of wordpress plugins are adding this feature (example: SEO Yoast) because it is user friendly and quick setup is something everybody likes.

    wordpress wizard configurator welcome page

    Wizard configurator in wp-admin

    In this tutorial we will learn how to create step by step forms with pagination: Back / Next Step. All admin elements will be hidden to allow user to focus on wizard only. In addition: we will add options to jump between steps and always save current form! For example: you can start in step1 and jump directly to step3. Your changes from step1 will be automatically saved!

    wizard configurator steps

    Custom admin pages

    Our plugin is using wordpress built-in function: add_menu_page to create new section in Admin Dashboard Area. add_submenu_page is used to handle steps (query param &step=step0). That’s it, having it defined – we can add as many steps as we like!

    First parameter of add_submenu_page set to null makes submenu hidden. Our wizard will be available in admin menu as: My plugin wizard.

    // ct-wp-wizard-admin/includes/class-ct-wp-wizard-admin.php
    
    public function add_menu_page()
    
    {
    
        add_menu_page(
    
            esc_html__('My plugin wizard', 'ct-admin'),
    
            esc_html__('My plugin wizard', 'ct-admin'),
    
            'manage_options',
    
            $this->get_id(),
    
            array(&$this, 'load_view'),
    
            'dashicons-admin-page'
    
        );
    
        /**
    
         * hidden view for steps
    
         */
    
        add_submenu_page(
    
            null,
    
            esc_html__('Wizard steps', 'ct-admin'),
    
            esc_html__('Wizard steps', 'ct-admin'),
    
            'manage_options',
    
            $this->get_id() . '&step=step0',
    
            array(&$this, 'load_view')
    
        );
    
    }
    admin wizard plugin example

    Loading main wizard structure

    Method load_view is including all partials: navigation for steps, alerts, content for steps and footer navigation.

    protected $views = array(
    
        'step0' => 'views/step0',
    
        'step1' => 'views/step1',
    
        'step2' => 'views/step2',
    
        'step3' => 'views/step3',
    
        'step4' => 'views/step4',
    
        'alerts' => 'views/alerts',
    
        'not-found' => 'views/not-found'
    
    );
    
    function load_view()
    
    {
    
        $this->default_values = $this->get_defaults();
    
        $this->current_page = ct_admin_wizard_current_step();
    
        $current_views = isset($this->views[$this->current_page]) ? $this->views[$this->current_page] : $this->views['not-found'];
    
        $step_data_func_name = $this->current_page . '_data';
    
        $args = [];
    
        /**
    
         * prepare data for view
    
         */
    
        if (method_exists($this, $step_data_func_name)) {
    
            $args = $this->$step_data_func_name();
    
        }
    
        /**
    
         * Default Admin Form Template
    
         */
    
        echo '<div class="ct-wizard-admin ' . $this->current_page . '">';
    
        echo '<div class="container container1">';
    
        echo '<div class="inner">';
    
        $this->includeWithVariables(ct_admin_template_server_path('views/nav', false));
    
        $this->includeWithVariables(ct_admin_template_server_path('views/alerts', false));
    
        $this->includeWithVariables(ct_admin_template_server_path($current_views, false), $args);
    
        $this->includeWithVariables(ct_admin_template_server_path('views/foot', false));
    
        echo '</div>';
    
        echo '</div>';
    
        echo '</div> <!-- / ct-wizard-admin -->';
    
    }

    Hiding parts of admin area

    For better user experience we’re going to add some CSS to hide not needed elements. Desired result: being in dashboard area – we would like to see only our wizard. Menu and footer will be hidden. Additional sr-only class is used for hiding form submit (we will use jQuery to click hidden submit button).

    /**
    
    /ct-wp-wizard-admin/assets/style.css
    
     */
    
    body.toplevel_page_ct-wizard-admin #adminmenumain {
    
        display: none !important;
    
    }
    
    body.toplevel_page_ct-wizard-admin #wpcontent {
    
        margin-top: 40px;
    
    }
    
    body.toplevel_page_ct-wizard-admin #wpcontent,
    
    body.toplevel_page_ct-wizard-admin #wpfooter {
    
        margin-left: 0;
    
    }
    
    body.toplevel_page_ct-wizard-admin .container {
    
        max-width: 600px;
    
    }
    
    .sr-only {
    
        position: absolute;
    
        width: 1px;
    
        height: 1px;
    
        padding: 0;
    
        overflow: hidden;
    
        clip: rect(0, 0, 0, 0);
    
        white-space: nowrap;
    
        -webkit-clip-path: inset(50%);
    
        clip-path: inset(50%);
    
        border: 0;
    
    }

    Wizard step (template)

    Single step is just form with fields. Submit button is hidden using CSS (sr-only class). We’re going to submit the form using JavaScript. By clicking any button or navigation with steps we will: update input ‘redirectToUrl’ (where to redirect after submit) and simulate clicking the submit input.

    Here is example template view for step3:

    <?php
    
    // ct-wp-wizard-admin/views/step3.php
    
    /** @var string $option4 */
    
    /** @var string $option5 */
    
    /** @var string $option6 */
    
    ?>
    
    <h1><?php echo esc_html__('Step3', 'ct-admin'); ?></h1>
    
    <form method="POST" action="<?php echo esc_html(admin_url('admin-post.php')); ?>" class="js-form-wizard">
    
        <input type="hidden" name="action" value="ct_admin_save">
    
        <?php wp_nonce_field('ct_admin_save', 'ct_admin'); ?>
    
        <input type="hidden" name="redirectToUrl" value="<?php echo ct_admin_wizard_step_url('step4'); ?>">
    
        <div class="row g-5">
    
            <div class="col-md-6">
    
                <fieldset class="mt-3">
    
                    <legend class="mb-3"><?php echo esc_html__('Section 1', 'ct-admin') ?></legend>
    
                    <div class="mb-3">
    
                        <label for="cookie_content"
    
                               class="form-label"><?php echo esc_html__('Option 4', 'ct-admin') ?></label>
    
                        <?php echo $option4; ?>
    
                    </div>
    
                    <div class="mb-3">
    
                        <label for="cookie_content"
    
                               class="form-label"><?php echo esc_html__('Option 4', 'ct-admin') ?></label>
    
                        <?php echo $option5; ?>
    
                    </div>
    
                    <div class="mb-3">
    
                        <label for="cookie_content"
    
                               class="form-label"><?php echo esc_html__('Option 4', 'ct-admin') ?></label>
    
                        <?php echo $option6; ?>
    
                    </div>
    
                </fieldset>
    
            </div>
    
        </div>
    
        <!-- / row -->
    
        <?php ct_admin_submit(esc_html__('Submit')); ?>
    
    </form>
    
    Javascript (jQuery) code for triggering form submit:
    
    // ct-wp-wizard-admin/assets/custom.js
    
    (function($) {
    
        $(".js-save-and-go").click(function(){
    
            const $that = $(this);
    
            const backUrl = $that.attr("href");
    
            $('.js-form-wizard input[name=redirectToUrl]').val(backUrl);
    
            $(".js-form-wizard #submit5").click();
    
        });
    
        $(".js-submit").click(function(){
    
            $(".js-form-wizard #submit5").click();
    
        });
    
    })( jQuery );

    Form fields

    Form data is saved in wp_options as serialized array of key and value. Currently we have 4 different types of fields: text input, textarea, checkbox and dropdown select.  Let’s see example of fields in step3:

    private function step3_data()
    
    {
    
        $args['option4'] = $this->render_input('ct-admin-cookie', 'option4');
    
        $args['option5'] = $this->render_input('ct-admin-cookie', 'option5');
    
        $args['option6'] = $this->render_input('ct-admin-cookie', 'option6');
    
        return $args;
    
    }

    After submit – Data from step3 will be saved in wordpress database, table wp_options – as:

    • option_name: ct-admin_cookie
    • option_value: serialized array of keys and values set in the form

    Database MYSQL is storing serialized data:

    a:9:{s:23:"cookie_content_language";s:2:"hu";s:14:"cookie_content";s:18:"das dsad sadas dsa";s:25:"cookie_popup_label_accept";s:9:"dsadsadas";s:18:"cookie_scan_period";s:15:"ct-admin-weekly";s:7:"option1";s:12:"ads233 43431";s:7:"option2";s:12:"das24 312312";s:7:"option4";s:6:"a dsad";s:7:"option5";s:5:"b ddd";s:7:"option6";s:1:"c";}

    If we decided to read this data from DB and unserialize it – this will be the result:

    array (
    
        'cookie_content_language' => 'hu',
    
        'cookie_content' => 'das dsad sadas dsa',
    
        'cookie_popup_label_accept' => 'dsadsadas',
    
        'cookie_scan_period' => 'ct-admin-weekly',
    
        'option1' => 'ads233 43431',
    
        'option2' => 'das24 312312',
    
        'option4' => 'a dsad',
    
        'option5' => 'b ddd',
    
        'option6' => 'c',
    
    )
    
    wp_options database modification

    Form fields types – example

    In our forms we can use different types of fields, including: text input, dropdown select, textarea and checkbox choice. It’s very easy to define new fields, just fill in $args array. Example for all 4 types:

    private function view999_data()
    
    {
    
        $args = [];
    
        $values = array(
    
            '' => esc_html__('Select', 'ct-admin'),
    
            'cs' => 'Čeština',
    
            'de' => 'Deutsch',
    
            'en' => 'English',
    
            'es' => 'Español',
    
            'fr' => 'Français',
    
            'hr' => 'Hrvatski',
    
            'hu' => 'Magyar',
    
            'no' => 'Norwegian',
    
            'it' => 'Italiano',
    
            'nl' => 'Nederlands',
    
            'pl' => 'Polski',
    
            'pt' => 'Português',
    
            'ro' => 'Română',
    
            'ru' => 'Русский',
    
            'sk' => 'Slovenčina',
    
            'dk' => 'Danish',
    
            'bg' => 'Bulgarian',
    
            'sv' => 'Swedish'
    
        );
    
        $args['cookie_content_language'] = $this->render_select('ct-admin-cookie', 'cookie_content_language', $values);
    
        $args['cookie_content'] = $this->render_textarea('ct-admin-cookie', 'cookie_content');
    
        $args['cookie_popup_label_accept'] = $this->render_input('ct-admin-cookie', 'cookie_popup_label_accept');
    
        $args['forgotten_automated_forget'] = $this->render_checkbox('ct-admin-forgotten', 'forgotten_automated_forget');
    
        return $args;
    
    }
    wizard in wp-admin modification

    Top navigation is defined in: /ct-wp-wizard-admin/views/nav.php . It’s showing steps from 1 to 3. But you can easily extend it and have as many steps as you would like.

    private function view999_data()
    
    {
    
        $args = [];
    
        $values = array(
    
            '' => esc_html__('Select', 'ct-admin'),
    
            'cs' => 'Čeština',
    
            'de' => 'Deutsch',
    
            'en' => 'English',
    
            'es' => 'Español',
    
            'fr' => 'Français',
    
            'hr' => 'Hrvatski',
    
            'hu' => 'Magyar',
    
            'no' => 'Norwegian',
    
            'it' => 'Italiano',
    
            'nl' => 'Nederlands',
    
            'pl' => 'Polski',
    
            'pt' => 'Português',
    
            'ro' => 'Română',
    
            'ru' => 'Русский',
    
            'sk' => 'Slovenčina',
    
            'dk' => 'Danish',
    
            'bg' => 'Bulgarian',
    
            'sv' => 'Swedish'
    
        );
    
        $args['cookie_content_language'] = $this->render_select('ct-admin-cookie', 'cookie_content_language', $values);
    
        $args['cookie_content'] = $this->render_textarea('ct-admin-cookie', 'cookie_content');
    
        $args['cookie_popup_label_accept'] = $this->render_input('ct-admin-cookie', 'cookie_popup_label_accept');
    
        $args['forgotten_automated_forget'] = $this->render_checkbox('ct-admin-forgotten', 'forgotten_automated_forget');
    
        return $args;
    
    }

    WordPress Wizard plugin configuration Previous / Next

    Nice feature will be to add additional buttons in footer: Quit Wizard, Previous Step and Next Step. Quiz Wizard is just simple link that redirect to dashboard. Other 2 buttons are submitting the form programmatically (see custom.js)

    <?php
    
    // ct-wp-wizard-admin/views/foot.php
    
    if ((!ct_admin_wizard_is_step('step0'))): ?>
    
        <?php if ((!ct_admin_wizard_is_step('step4'))): ?>
    
            <div class="float-start">
    
                <a href="<?php echo admin_url(); ?>"
    
                   class="btn btn-primary"><?php _e("Quit") ?></a>
    
                <button href="<?php echo ct_admin_wizard_prev_step(); ?>"
    
                        class="btn btn-primary js-save-and-go"><?php _e("Back") ?></button>
    
            </div>
    
            <div class="float-end">
    
                <button class="btn btn-primary js-submit"><?php _e("Next step") ?></button>
    
            </div>
    
        <?php endif; ?>
    
    <?php endif; ?>

    Wizard screencast and source code

    Working plugin source code “CT WP Admin Wizard Example” is available on GitHub : https://github.com/createit-dev/117-wordpress-wizard-in-admin-step-by-step

    Feel free to clone it and checkout it out.

    In addition – attached screencast from wp-admin area. See how plugin is working!

    wordpress custom wizard configurator

    If you are looking for custom web app development reach our team of professional developers!

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us