-
-
Save spivurno/8ca209fbb5068e62655ddf91678830a3 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* Gravity Perks // GP Limit Choices // Field Group | |
* | |
* Specify a group of fields that should create a unique choice to limited. For example, a Date field and Radio Button field could be | |
* combined to sell x tickets per date where the ticket type is controlled by the Radio Button field and the date is | |
* selected in the Date field. | |
* | |
* @version 1.4 | |
* @author David Smith <[email protected]> | |
* @license GPL-2.0+ | |
* @link http://gravitywiz.com/documentation/gp-limit-choices/ | |
* | |
* Plugin Name: GP Limit Choices - Field Groups | |
* Plugin URI: http://gravitywiz.com/documentation/gp-limit-choices/ | |
* Description: Specify a group of fields that should create a unique choice to be limited. | |
* Author: Gravity Wiz | |
* Version: 1.4 | |
* Author URI: http://gravitywiz.com | |
*/ | |
class GP_Limit_Choices_Field_Group { | |
public function __construct( $args = array() ) { | |
// set our default arguments, parse against the provided arguments, and store for use throughout the class | |
$this->_args = wp_parse_args( $args, array( | |
'form_id' => false, | |
'field_ids' => array(), | |
) ); | |
$this->_args['hash'] = hash_hmac( 'sha256', serialize( $this->_args ), 'gplc_field_group' ); | |
add_filter( 'gwlc_choice_counts_query', array( $this, 'limit_by_field_group' ), 10, 2 ); | |
add_action( 'wp_ajax_gplcfg_refresh_field', array( $this, 'ajax_refresh' ) ); | |
add_action( 'wp_ajax_nopriv_gplcfg_refresh_field', array( $this, 'ajax_refresh' ) ); | |
add_filter( 'gform_pre_render', array( $this, 'load_form_script' ), 10, 2 ); | |
add_filter( 'gform_register_init_scripts', array( $this, 'add_init_script' ), 10, 2 ); | |
if( isset( $_POST['action'] ) && $_POST['action'] == 'gplcfg_refresh_field' ) { | |
remove_action( 'wp', array( 'GFForms', 'maybe_process_form' ), 9 ); | |
remove_action( 'admin_init', array( 'GFForms', 'maybe_process_form' ), 9 ); | |
} | |
} | |
public function limit_by_field_group( $query, $field ) { | |
global $wpdb; | |
$field_ids = $this->_args['field_ids']; | |
if( ! $this->is_applicable_form( $field->formId ) || ! in_array( $field->id, $field_ids ) ) { | |
return $query; | |
} | |
unset( $field_ids[ array_search( $field->id, $field_ids ) ] ); | |
$field_ids = array_values( $field_ids ); | |
$form = GFAPI::get_form( $field->formId ); | |
$join = $where = array(); | |
$select = $from = ''; | |
foreach( $field_ids as $index => $field_id ) { | |
$field = GFFormsModel::get_field( $form, $field_id ); | |
$alias = sprintf( 'fgem%d', $index + 1 ); | |
$_alias = null; | |
if( $index == 0 ) { | |
$_alias = $alias; | |
$select = "SELECT DISTINCT {$alias}.entry_id"; | |
$from = "FROM {$wpdb->prefix}gf_entry_meta {$alias}"; | |
$value = $field->get_value_save_entry( GFFormsModel::get_field_value( $field ), $form, null, null, null ); | |
$where[] = $wpdb->prepare( "( {$alias}.form_id = %d AND {$alias}.meta_key = %s AND {$alias}.meta_value = %s )", $field->formId, $field_id, $value ); | |
} else { | |
$join[] = "INNER JOIN {$wpdb->prefix}gf_entry_meta {$alias} ON {$_alias}.entry_id = {$alias}.entry_id"; | |
} | |
} | |
$field_group_query = array( | |
'select' => $select, | |
'from' => $from, | |
'join' => implode( ' ', $join ), | |
'where' => sprintf( 'WHERE %s', implode( "\nAND ", $where ) ) | |
); | |
$query['where'] .= sprintf( ' AND e.id IN( %s )', implode( "\n", $field_group_query ) ); | |
return $query; | |
} | |
public function is_applicable_form( $form ) { | |
$form_id = isset( $form['id'] ) ? $form['id'] : $form; | |
return empty( $this->_args['form_id'] ) || $form_id == $this->_args['form_id']; | |
} | |
public function load_form_script( $form, $is_ajax_enabled ) { | |
$func = array( 'GP_Limit_Choices_Field_Group', 'output_script' ); | |
if( $this->is_applicable_form( $form ) && ! has_action( 'wp_footer', $func ) ) { | |
add_action( 'wp_footer', $func ); | |
add_action( 'gform_preview_footer', $func ); | |
} | |
return $form; | |
} | |
public function add_init_script( $form ) { | |
if( ! $this->is_applicable_form( $form ) ) { | |
return; | |
} | |
$target_field_id = $this->get_target_field_id( $form, $this->_args['field_ids'] ); | |
$args = array( | |
'formId' => $this->_args['form_id'], | |
'targetFieldId' => $target_field_id, | |
'triggerFieldIds' => $this->get_trigger_field_ids( $form, $this->_args['field_ids'] ), | |
'ajaxUrl' => admin_url( 'admin-ajax.php' ), | |
'hash' => $this->_args['hash'], | |
); | |
$script = 'new GPLCFieldGroup( ' . json_encode( $args ) . ' );'; | |
$slug = implode( '_', array( 'gplc_field_group', $this->_args['form_id'], $target_field_id ) ); | |
GFFormDisplay::add_init_script( $this->_args['form_id'], $slug, GFFormDisplay::ON_PAGE_RENDER, $script ); | |
} | |
public function get_target_field_id( $form, $field_ids ) { | |
foreach( $field_ids as $field_id ) { | |
$field = GFAPI::get_field( $form, $field_id ); | |
if ( gp_limit_choices()->is_applicable_field( $field ) ) { | |
return $field_id; | |
} | |
} | |
return false; | |
} | |
public function get_trigger_field_ids( $form, $field_ids ) { | |
$target_field_id = $this->get_target_field_id( $form, $field_ids ); | |
return array_values( array_filter( $field_ids, function( $field_id ) use ( $target_field_id ) { | |
return $field_id != $target_field_id; | |
} ) ); | |
} | |
public function ajax_refresh() { | |
// Object can be instantiated multiple times. Only listen for this specific configuration's hash. | |
if ( rgpost( 'hash' ) !== $this->_args['hash'] ) { | |
/** | |
* Return out if the hash doesn't match. If we exit here, other ajax_refresh() calls in other instances | |
* won't have the chance to run. | |
*/ | |
return; | |
} | |
$entry = GFFormsModel::get_current_lead(); | |
if( ! $entry ) { | |
wp_send_json_error(); | |
} | |
$form = gf_apply_filters( array( 'gform_pre_render', $entry['form_id'] ), GFAPI::get_form( $entry['form_id'] ), false, array() ); | |
$field = GFFormsModel::get_field( $form, $this->get_target_field_id( $form, $this->_args['field_ids'] ) ); | |
if( $field->get_input_type() == 'html' ) { | |
$content = GWPreviewConfirmation::preview_replace_variables( $field->content, $form ); | |
} else { | |
$value = rgpost( 'input_' . $field->id ); | |
$content = $field->get_field_content( $value, true, $form ); | |
$content = str_replace( '{FIELD}', $field->get_field_input( $form, $value, $entry ), $content ); | |
} | |
wp_send_json_success( $content ); | |
} | |
public static function output_script() { | |
?> | |
<script type="text/javascript"> | |
( function( $ ) { | |
window.GPLCFieldGroup = function( args ) { | |
var self = this; | |
// copy all args to current object: (list expected props) | |
for( prop in args ) { | |
if( args.hasOwnProperty( prop ) ) | |
self[prop] = args[prop]; | |
} | |
self.init = function() { | |
self.$form = $( '#gform_wrapper_{0}'.format( self.formId ) ); | |
self.$targetField = $( '#field_{0}_{1}'.format( self.formId, self.targetFieldId ) ); | |
self.$triggerFields = false; | |
for( var i = 0; i < self.triggerFieldIds.length; i++ ) { | |
var $field = $( '#field_{0}_{1}'.format( self.formId, self.triggerFieldIds[ i ] ) ); | |
if ( ! self.$triggerFields ) { | |
self.$triggerFields = $().add( $field ); | |
} | |
// @todo Test with multiple triggers. | |
else { | |
self.$triggerFields.add( $field ); | |
} | |
} | |
self.$triggerFields.on( 'change', function() { | |
self.refresh(); | |
} ); | |
self.refresh(); | |
}; | |
self.refresh = function() { | |
if( ! self.$targetField.is( ':visible' ) ) { | |
return; | |
} | |
var data = { | |
action: 'gplcfg_refresh_field', | |
hash: self.hash | |
}; | |
self.$form.find( 'input, select, textarea' ).each( function() { | |
if ( this.type === 'radio' ) { | |
if ( this.checked ) { | |
data[ $( this ).attr( 'name' ) ] = $( this ).val(); | |
} | |
} else { | |
data[ $( this ).attr( 'name' ) ] = $( this ).val(); | |
} | |
} ); | |
// Prevent AJAX-enabled forms from intercepting our AJAX request. | |
delete data['gform_ajax']; | |
$.post( self.ajaxUrl, data, function( response ) { | |
if( response.success ) { | |
self.$targetField.html( response.data ); | |
} | |
} ); | |
}; | |
self.init(); | |
} | |
} )( jQuery ); | |
</script> | |
<?php | |
} | |
} | |
# Configuration | |
new GP_Limit_Choices_Field_Group( array( | |
'form_id' => 1485, | |
'field_ids' => array( 1, 2 ) | |
) ); |
<?php | |
new GP_Limit_Choices_Field_Group( array( | |
'form_id' => 12, | |
'field_ids' => array( 3, 4 ) | |
) ); |
I added this hook for displaying the available slots, but its not working as well. I tihnk its not made for field groups.
add_filter( 'gplc_remove_choices', '__return_false' );
add_filter( 'gplc_pre_render_choice', 'my_add_how_many_left_message', 10, 5 );
function my_add_how_many_left_message( $choice, $exceeded_limit, $field, $form ) {
if( ! current_user_can( 'administrator' ) ) {
return $choice;
}
$choice_counts = GWLimitChoices::get_choice_counts( $form['id'], $field );
$count = rgar( $choice_counts, $choice['value'] ) ? rgar( $choice_counts, $choice['value'] ) : 0;
$limit = rgar($choice, 'limit');
$how_many_left = $limit - $count;
$message = "($how_many_left spots left)";
$choice['text'] = $choice['text'] . " $message";
return $choice;
}
Why does this snippet include
add_filter( 'gplc_disable_choices', '__return_false' );
@Wordna1 Doesn't appear to be necessary for this snippet to work. Must have been the original required configuration of the user for whom the original snippet was developed.
@Wordna1 Doesn't appear to be necessary for this snippet to work. Must have been the original required configuration of the user for whom the original snippet was developed.
Thank you for another VERY helpful snippet.
For what it's worth, I would suggest removing add_filter( 'gplc_disable_choices', '__return_false' );
if it isn't actually required to group fields.
On a separate note, since the "Spots Remaining" message doesn't dynamically update for grouped fields, this snippet shouldn't really be combined with add_filter( 'gplc_disable_choices', '__return_false' );
, which is used in, for example, the Waiting List snippet for the Limit Choices perk.
This is great! I’m hoping to use this field group snippet to manage spots, however, I need to have multiple sets of field groups on the same form. Here’s my desired configuration: I have a location field and then a series of fields that list date and time slots as dropdowns.
new GP_Limit_Choices_Field_Group( array( ‘form_id’ => 10, ‘field_ids’ => array( 7, 30, 31, 32, 33, 34, 35 ) ) );
BUT I need an array of arrays where field 7 (the location) is paired with the conditionally displayed date/time dropdown field (30 – 35). So really, it’s (7, 30) (7, 31) and so on – as only one date/time dropdown will appear at a time based on logic.
Is this possible?
EDIT: Just testing, I tried this config and it's seemed to have worked? Any issue you see with this implementation? Thanks so much for your help!
new GP_Limit_Choices_Field_Group( array(
'form_id' => 10,
'field_ids' => array( 7, 30 ),
'field_ids' => array( 7, 31 ),
'field_ids' => array( 7, 32 ),
'field_ids' => array( 7, 33 ),
'field_ids' => array( 7, 34 ),
'field_ids' => array( 7, 35 )
) );
I wouldn't expect that to work but happy to hear it does!
I think you'd probably be safe to just group them all together assuming that only one field between 30 and 35 would be visible at a time.
new GP_Limit_Choices_Field_Group( array(
'form_id' => 10,
'field_ids' => array( 7, 30, 31, 32, 33, 34, 35 ),
) );
Hi @spivurno, First of all thanks for sharing this snippet.
I'm using it for binding gp-lc with a date. Now I have a question. How can I bind an amount field to the limit count. So the amount decreases from the limit instead of the "submit"
I've understood this already works with products choices and their quantity field. Unfortunately I do not want to use this for a product.
@jaimyvangerrevink There is no ready way to do this without using Product fields. The Product + Quantity fields provide an implicit structure that doesn't exist with other field types. Definitely something we'll consider as we develop this functionality into a perk.
@spivurno Getting a fatal error with this snippet.
Update: Uninstalled the custom site plugin storing this snippet, reinstalled, and things are working again.
Fatal error: Uncaught Error: Call to undefined function rgpost() in /.../public_html/wp-content/plugins/gp-limit-choices-field-groups/gp-limit-choices-field-groups.php:41
This snippet is critical for a client right now.
Technical Information About the Site:
WordPress v5.5
Gravity Forms v2.4.20
PHP v7.2.33
@mike-weiner Fixed!
Thank you for the quick response and fix! It is greatly appreciated.
🧙 Heads-up! This snippet has been migrated to the Gravity Wiz Snippet Library.
Hi,
I have a question:
I combined a date field with a select field. On the select field i enabled limits and added available slots them (for example 10 on each selection).
When the form is submited it works fine. It detects when the slots are used and display a message and the selection is gone.
The Problem is, that when the form is loaded selection with 0 slots available are still there.
Even when all selections are used.
How can I do that, so the selection will not be displayed when the slots are used?
BR
Marc