spivurno
9/26/2014 - 7:34 PM

Gravity Wiz // Gravity Forms // Dynamic Select (for Categories, Taxonomies and more)

Gravity Wiz // Gravity Forms // Dynamic Select (for Categories, Taxonomies and more)

<?php
/**
* Gravity Wiz // Gravity Forms // Dynamic Category Select
* 
* Allows the user to drill down from a top level category to a child category.
* 
* @version   1.0
* @author    David Smith <david@gravitywiz.com>
* @license   GPL-2.0+
* @link      http://gravitywiz.com/.../
*/
class GW_Dynamic_Select {

    protected static $script_output;

    public function __construct( $args ) {

        $this->_args = wp_parse_args( $args, array(
            'form_id'    => false,
            'field_id'   => false,
            'taxonomy'   => 'category',
            'query_args' => array()
        ) );

        extract( $this->_args ); // we want $form_id, $field_id

        add_filter( "gform_pre_render_{$form_id}", array( $this, 'populate_choices' ) );
        add_filter( "gform_admin_pre_render_{$form_id}", array( $this, 'populate_choices' ) );

        add_filter( "gform_pre_render_{$form_id}", array( $this, 'load_form_script') );
        add_filter( "gform_register_init_scripts_{$form_id}", array( $this, 'add_init_script') );

        add_filter( "wp_ajax_gwds_get_child_choices_{$form_id}_{$field_id}", array( $this, 'ajax_get_child_choices' ) );
        add_filter( "wp_ajax_nopriv_gwds_get_child_choices_{$form_id}_{$field_id}", array( $this, 'ajax_get_child_choices' ) );

    }

    public function load_form_script( $form ) {
        
        if( self::$script_output )
            return $form;

        ?>

        <script type="text/javascript">

            var GWDynSelect;

            (function($){

                GWDynSelect = function( args ) {

                    var self = this;

                    // copy all args to current object: formId, fieldId
                    for( prop in args ) {
                        if( args.hasOwnProperty( prop ) )
                            self[prop] = args[prop];
                    }

                    self.init = function() {

                        self.elem = $( '#input_' + self.formId + '_' + self.fieldId );

                        self.elem.change( function() {

                            var value = $( this ).val();

                            if( $.inArray( value, self.childlessTerms ) != -1 )
                                return;

                            var spinner = $( '<img src="' + self.spinnerUrl + '" style="position:relative;top:3px;left:3px;box-shadow:0 0 0;" />' ).insertAfter( self.elem );

                            $.post( self.ajaxUrl, {
                                value: value,
                                action: 'gwds_get_child_choices_' + self.formId + '_' + self.fieldId
                            }, function( response ) {

                                spinner.remove();

                                var terms         = $.parseJSON( response ),
                                    optionsMarkup = '';

                                for( var i = 0; i < terms.length; i++ ) {

                                    var selected = terms[i].isSelected == true ? 'selected="selected"' : '';

                                    optionsMarkup += '<option ' + selected + ' value="' + terms[i].value + '">' + terms[i].text + '</option>';

                                }

                                self.elem.html( optionsMarkup );

                                // auto-open drop down after repop
                                // http://stackoverflow.com/questions/19652085/open-dropdown-list-from-javascript-function
                                var event = document.createEvent( 'MouseEvents' );
                                event.initMouseEvent( 'mousedown', true, true, window );
                                self.elem[0].dispatchEvent( event );

                            } );

                        } );

                    }

                    self.init();

                }

            })(jQuery);

        </script>

        <?php
        self::$script_output = true;
        return $form;
    }

    public function add_init_script( $form ) {

        $args = array(
            'formId'         => $this->_args['form_id'],
            'fieldId'        => $this->_args['field_id'],
            'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
            'spinnerUrl'     => GFCommon::get_base_url() . '/images/spinner.gif',
            'childlessTerms' => $this->get_childless_terms()
        );

        $script = '; new GWDynSelect( ' . json_encode( $args ) . ' );';
        $slug = "gw_dynamic_select_{$this->_args['form_id']}_{$this->_args['field_id']}";

        GFFormDisplay::add_init_script( $this->_args['form_id'], $slug, GFFormDisplay::ON_PAGE_RENDER, $script );

    }

    public function populate_choices( $form ) {

        foreach( $form['fields'] as &$field ) {

            if( ! $this->is_applicable_field( $field ) ) {
                continue;
            }

            if( current_filter() == "gform_admin_pre_render_{$form['id']}" ) {
                $entry_id = rgget( 'lid' );
                if( $entry_id ) {
                    $entry = GFAPI::get_entry( $entry_id );
                    $selected_value = GFFormsModel::get_lead_field_value( $entry, $field );
                } else {
                    $selected_value = false;
                }
            } else {
                $selected_value = GFFormsModel::get_field_value( $field );
            }

            $field['choices'] = $this->get_terms_as_choices( $selected_value );

        }

        return $form;
    }

    public function ajax_get_child_choices() {

        $selected_value = rgpost( 'value' );
        $choices = $this->get_terms_as_choices( $selected_value );

        die( json_encode( $choices ) );
    }

    public function is_applicable_field( $field ) {
        return $field['id'] == $this->_args['field_id'];
    }

    public function get_terms_as_choices( $selected_value ) {

        if( $selected_value ) {
            $selected_term = get_term( $selected_value, $this->_args['taxonomy'] );
            if( $this->is_childless_term( $selected_term->term_id ) ) {
                $parent_id = $selected_term->parent;
            } else {
                $parent_id = $selected_term->term_id;
            }
        } else {
            $parent_id = 0;
        }

        $terms = get_terms( $this->_args['taxonomy'], array_merge(
            array( 'parent' => $parent_id ),
            $this->_args['query_args']
        ) );

        $choices = array();

        if( $parent_id > 0 ) {
            $choices[] = array(
                'text' => '&#8617; ' . __( 'Back' ),
                'value' => '_gwds_back'
            );
        }

        $choices[] = array(
            'text' => __( 'Select a category...' ),
            'value' => '',
            'isSelected' => true
        );

        foreach( $terms as $term ) {

            $has_children = ! $this->is_childless_term( $term->term_id );
            $label = $term->name;

            if( $has_children ) {
                $label .= ' &bull;';
            }

            $choice = array(
                'text'        => $label,
                'value'       => $term->term_id,
                'isSelected'  => $term->term_id == $selected_value,
                'hasChildren' => $has_children
            );

            $choices[] = $choice;
        }

        return $choices;
    }

    public function get_childless_terms() {

        $childless_terms = GFCache::get( 'gwds_get_childless_terms' );
        if( $childless_terms !== false )
            return $childless_terms;

        $terms = get_terms( $this->_args['taxonomy'], array_merge(
            array(),
            $this->_args['query_args']
        ) );

        if( is_wp_error( $terms ) ) {
            return array();
        }

        $childless_terms = array();
        $parent_terms = array_unique( wp_list_pluck( $terms, 'parent' ) );

        foreach( $terms as $term ) {
            if( ! in_array( $term->term_id, $parent_terms ) )
                $childless_terms[] = $term->term_id;
        }

        GFCache::set( 'gwds_get_childless_terms', $childless_terms );

        return $childless_terms;
    }

    public function is_childless_term( $term_id ) {
        return in_array( $term_id, $this->get_childless_terms() );
    }

}

# Configuration

new GW_Dynamic_Select( array(
    'form_id'  => 548,
    'field_id' => 2,
    'taxonomy' => 'gdoc_category',
    'query_args' => array(
        'hide_empty' => false
    )
) );