Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Field Condtions (AKA "Depends") #3141

Draft
wants to merge 11 commits into
base: v3.2
Choose a base branch
from
94 changes: 80 additions & 14 deletions classes/class-pmpro-field.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,15 @@ class PMPro_Field {
public $html = '';

/**
* The default value for a field.
* The checkbox conditional field logic.
*
* @since TBD
*
* @var bool
*/
public $display_conditions = false;

/** The default value for a field.
*
* @since TBD
*
Expand Down Expand Up @@ -1164,18 +1172,26 @@ function getHTMLAttributes() {
return $html;
}

function getDependenciesJS()
{
function getDependenciesJS() {
global $pmpro_user_fields;

// Don't load this for fields that don't have display conditions enabled.
if ( empty( $this->display_conditions ) ) {
return;
}

//dependencies
if(!empty($this->depends))
{
//build the checks
if ( ! empty ( $this->depends ) ) {
$depends = array();
if ( is_object( $this->depends ) ) {
$depends[] = (array) $this->depends;
} else {
$depends = $this->depends; // Backwards compatibility for RH or fields created by code.
}

$checks_escaped = array();
foreach($this->depends as $check)
{
if(!empty($check['id']))
{
foreach( $depends as $check ) {
if ( ! empty( $check['id'] ) ) {
// If checking checkbox_grouped, need to update the $check['id'] with index of option.
$field_id = $check['id'];
$depends_checkout_box = PMPro_Field::get_checkout_box_name_for_field( $field_id );
Expand All @@ -1188,11 +1204,61 @@ function getDependenciesJS()
}
}

$checks_escaped[] = "((jQuery('#" . esc_html( $field_id ) ."')".".is(':checkbox')) "
."? jQuery('#" . esc_html( $field_id ) . ":checked').length > 0"
.":(jQuery('#" . esc_html( $field_id ) . "').val() == " . json_encode($check['value']) . " || jQuery.inArray( jQuery('#" . esc_html( $field_id ) . "').val(), " . json_encode($check['value']) . ") > -1)) ||"."(jQuery(\"input:radio[name='". esc_html( $check['id'] ) ."']:checked\").val() == ".json_encode($check['value'])." || jQuery.inArray(".json_encode($check['value']).", jQuery(\"input:radio[name='". esc_html( $field_id ) ."']:checked\").val()) > -1)";
// Apply the conditional check with jQuery now.
if ( ! empty( $check['condition'] ) ) {

// Get the parent field type we're checking against to make the jQuery conditionals easier to work with.
$parent_field_type = '';
$i = 0;
foreach ( $pmpro_user_fields as $key => $field ) {
$field_name = wp_list_pluck( $field, 'name' );
if ( in_array( $check['id'], $field_name ) ) {
$parent_field_type = $field[$i]->type;
++$i;
}
}

/**
* Loop through the conditionals and build the jQuery logic.
* This is separated out into radio buttons and every other button type, we can extend this further for specific field types.
*/
switch( $check['condition'] ) {
case 'is_empty':
if ( $parent_field_type === 'radio' ) {
$checks_escaped[] = "(jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val()) === undefined";
} else {
$checks_escaped[] = "(jQuery('#" . esc_attr( $field_id ). "').is(':checkbox') ? jQuery('#" . esc_attr( $field_id ) . ":checked').length === 0 : jQuery('#" . esc_attr( $field_id ) ."').val()) == ''";
}
break;
case 'is_not_empty':
if ( $parent_field_type === 'radio' ) {
$checks_escaped[] = "(jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").length) > 0";
} else {
$checks_escaped[] = "(jQuery('#" . esc_attr( $field_id ) ."').is(':checkbox')) ? jQuery('#" . esc_attr( $field_id ) . ":checked').length > 0 : (jQuery('#" . esc_attr( $field_id ) . "').val()) != ''";
}
break;
case 'is_not_equal_to':
$checks_escaped[] = "( jQuery('#" . esc_attr( $field_id ) . "').val() != " . json_encode( $check['value'] ) . " && jQuery('#" . esc_attr( $field_id ) . "').val() != '' ),(jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val() != ".json_encode($check['value']) .")";
break;
case 'is_equal_to':
$checks_escaped[] = "jQuery('#" . esc_attr( $field_id ) . "').val() == " . json_encode( $check['value'] ) . "|| (jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val() == ".json_encode($check['value']) .")";
break;
case 'contains':
$checks_escaped[] = "(jQuery('#" . esc_attr( $field_id ) . "').val()?.indexOf(" . json_encode( $check['value'] ) . ") > -1) || (jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val()?.indexOf(".json_encode($check['value']).") > -1)";
break;
case 'does_not_contain':
$checks_escaped[] = "(jQuery('#" . esc_attr( $field_id ) . "').val()?.indexOf(" . json_encode( $check['value'] ) . ") == -1) || (jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val()?.indexOf(".json_encode($check['value']).") == -1)";
break;
default:
break;
}
} else { // Backwards compatibility default checks.
$checks_escaped[] = "((jQuery('#" . esc_attr( $field_id ) ."')".".is(':checkbox')) "
."? jQuery('#" . esc_attr( $field_id ) . ":checked').length > 0"
.":(jQuery('#" . esc_attr( $field_id ) . "').val() == " . json_encode($check['value']) . " || jQuery.inArray( jQuery('#" . esc_attr( $field_id ) . "').val(), " . json_encode($check['value']) . ") > -1)) ||"."(jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val() == ".json_encode($check['value'])." || jQuery.inArray(".json_encode($check['value']).", jQuery(\"input:radio[name='". esc_attr( $field_id ) ."']:checked\").val()) > -1)";
}

$binds[] = "#" . esc_html( $field_id ) .",input:radio[name=". esc_html( $field_id ) ."]";
$binds[] = "#" . esc_attr( $field_id ) .",input:radio[name=". esc_attr( $field_id ) ."]";
}
}

Expand Down
20 changes: 20 additions & 0 deletions css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -2048,6 +2048,18 @@ table.pmprommpu_levels tr.remove_level td {background: #F2DEDE; }
margin: 12px 0;
}

.pmpro_userfield-field-full-width {
width: 100%;
}

#pmpro_userfield-field-setting-conditional-logic {
background-color:#F0F0F1;
padding:0.5rem;
border-radius:5px;
border:1px solid #CCC;
margin-bottom:10px;
}

.pmpro_admin-pmpro-userfields .pmpro_userfield-field-settings .pmpro_userfield-field-setting-dual {
column-count: 2;
}
Expand All @@ -2056,6 +2068,14 @@ table.pmprommpu_levels tr.remove_level td {background: #F2DEDE; }
margin: 0;
}

.pmpro_admin-pmpro-userfields .pmpro_userfield-field-settings .pmpro_userfield-field-setting-triple {
column-count: 3;
}

.pmpro_admin-pmpro-userfields .pmpro_userfield-field-settings .pmpro_userfield-field-setting-triple .pmpro_userfield-field-setting {
margin: 0;
}

.pmpro_admin-pmpro-userfields .pmpro_userfield-field-settings .pmpro_userfield-field-setting-radio span {
display: inline-block;
margin-right: 12px;
Expand Down
94 changes: 73 additions & 21 deletions includes/fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,8 @@ function pmpro_get_field_html( $field = null ) {
$field_element_class = $field->element_class;
$field_hint = $field->hint;
$field_options = $field->options;
$field_display_conditions = $field->display_conditions;
$field_conditions = $field->depends;
$field_allowed_file_types = $field->allowed_file_types;
$field_max_file_size = $field->max_file_size;
$field_default = $field->default;
Expand All @@ -1362,6 +1364,11 @@ function pmpro_get_field_html( $field = null ) {
$field_element_class = '';
$field_hint = '';
$field_options = '';
$field_display_conditions = '';
$field_conditions = new stdClass();
$field_conditions->id = '';
$field_conditions->value = '';
$field_conditions->condition = '';
$field_allowed_file_types = '';
$field_max_file_size = '';
$field_default = '';
Expand Down Expand Up @@ -1495,7 +1502,22 @@ function pmpro_get_field_html( $field = null ) {
</label>
<span class="description"><?php esc_html_e( 'Descriptive text for users or admins submitting the field.', 'paid-memberships-pro' ); ?></span>
</div> <!-- end pmpro_userfield-field-setting -->


<div class="pmpro_userfield-field-setting">
<label>
<?php esc_html_e( 'Options', 'paid-memberships-pro' ); ?><br />
<textarea name="pmpro_userfields_field_options" /><?php echo esc_textarea( $field_options );?></textarea>
</label>
<span class="description"><?php esc_html_e( 'One option per line. To set separate values and labels, use value:label.', 'paid-memberships-pro' ); ?></span>
</div> <!-- end pmpro_userfield-field-setting -->

<div class="pmpro_userfield-field-setting">
<label>
<?php esc_html_e( 'Default Value (optional)', 'paid-memberships-pro' ); ?><br />
<input type="text" name="pmpro_userfields_field_default" value="<?php echo esc_attr( $field_default ); ?>" />
</label>
</div> <!-- end pmpro_userfield-field-setting -->

<div class="pmpro_userfield-field-setting">
<div class="pmpro_userfield-field-setting pmpro_userfield-field-setting-dual">
<div class="pmpro_userfield-field-setting">
Expand All @@ -1514,33 +1536,59 @@ function pmpro_get_field_html( $field = null ) {
<span class="description"><?php printf( esc_html__( 'Enter an upload size limit for files in Megabytes (MB) or set it to 0 to use your default server upload limit. Your server upload limit is %s.', 'paid-memberships-pro' ), $server_max_upload . 'MB' ); ?></span>
</div> <!-- end pmpro_userfield-field-setting -->
</div>
</div>
<!-- Conditional logic -->
<div class="pmpro_userfield-field-full-width">
<div class="pmpro_userfield-field-setting">
<label>
<?php esc_html_e( 'Options', 'paid-memberships-pro' ); ?><br />
<textarea name="pmpro_userfields_field_options" /><?php echo esc_textarea( $field_options );?></textarea>
</label>
<span class="description"><?php esc_html_e( 'One option per line. To set separate values and labels, use value:label.', 'paid-memberships-pro' ); ?></span>
</div> <!-- end pmpro_userfield-field-setting -->

<div class="pmpro_userfield-field-setting">
<label>
<?php esc_html_e( 'Default Value (optional)', 'paid-memberships-pro' ); ?><br />
<input type="text" name="pmpro_userfields_field_default" value="<?php echo esc_attr( $field_default ); ?>" />
<label class="pmpro_clickable">
<input type="checkbox" name="pmpro_userfields_field_show_conditional_logic" value=1 <?php checked( $field_display_conditions, 1 ); ?>/>
<?php esc_html_e( 'Enable Conditional Logic', 'paid-memberships-pro' ); ?>
</label>
</div> <!-- end pmpro_userfield-field-setting -->
</div>

</div>
<div id="pmpro_userfield-field-setting-conditional-logic" style="display:none;">
<p><?php esc_html_e( "Make this field conditionally show based on another field's value. Enter the dependent field name, condition type, and an optional exact value to match below.", 'paid-memberships-pro' ); ?></p>
<div class="pmpro_userfield-field-setting pmpro_userfield-field-setting-triple">
<div class="pmpro_userfield-field-setting">
<label>
<?php esc_html_e( 'Depends on Field Name', 'paid-memberships-pro' ); ?><br>
<input type="text" name="pmpro_userfields_field_conditional_logic_field" value="<?php echo esc_attr( $field_conditions->id ); ?>" />
</label>
</div>

<div class="pmpro_userfield-field-setting">
<label>
<?php esc_html_e( 'Condition Type', 'paid-memberships-pro' ); ?><br>
<select id="pmpro_userfields_field_conditional_logic_condition" name="pmpro_userfields_field_conditional_logic_condition">
<option value=""> - </option>
<option value="is_empty" <?php selected( $field_conditions->condition, "is_empty" );?>><?php esc_attr_e( 'Is empty', 'paid-memberships-pro' ); ?></option>
<option value="is_not_empty" <?php selected( $field_conditions->condition, "is_not_empty" );?>><?php esc_attr_e( 'Is not empty', 'paid-memberships-pro' ); ?></option>
<option value="is_equal_to" <?php selected( $field_conditions->condition, "is_equal_to" );?>><?php esc_attr_e( 'Is equal to', 'paid-memberships-pro' ); ?></option>
<option value="is_not_equal_to" <?php selected( $field_conditions->condition, "is_not_equal_to" );?>><?php esc_attr_e( 'Is not equal to', 'paid-memberships-pro' ); ?></option>
<option value="contains" <?php selected( $field_conditions->condition, "contains" );?>><?php esc_attr_e( 'Contains', 'paid-memberships-pro' ); ?></option>
<option value="does_not_contain" <?php selected( $field_conditions->condition, "does_not_contain" );?>><?php esc_attr_e( 'Does not contain', 'paid-memberships-pro' ); ?></option>
</select>
</label>
</div>
<div id="pmpro_userfields_field_conditional_logic_value" class="pmpro_userfield-field-setting" style="display:none;">
<label>
<?php esc_html_e( 'Value', 'paid-memberships-pro' ); ?><br>
<input type="text" name="pmpro_userfields_field_conditional_logic_value" value="<?php echo esc_attr( $field_conditions->value ); ?>"/>
</label>
</div>
</div> <!-- end of triple -->
</div>
</div> <!-- end of full width -->
<div class="pmpro_userfield-field-actions">
<button name="pmpro_userfields_close_field" class="button button-secondary pmpro_userfields_close_field">
<?php esc_html_e( 'Close Field', 'paid-memberships-pro' ); ?>
</button>
<button name="pmpro_userfields_delete_field" class="button button-secondary is-destructive">
<?php esc_html_e( 'Delete Field', 'paid-memberships-pro' ); ?>
</button>
</div> <!-- end pmpro_userfield-field-actions -->
</div> <!-- end pmpro_userfield-field-settings -->
</div> <!-- end pmpro_userfield-group-field -->
<?php
<?php esc_html_e( 'Delete Field', 'paid-memberships-pro' ); ?>
</button>
</div> <!-- end pmpro_userfield-field-actions -->
</div> <!-- end pmpro_userfield-group-field -->
</div>
<?php
}

/**
Expand Down Expand Up @@ -1586,6 +1634,8 @@ function pmpro_get_user_fields_settings() {
$field->default = ! empty( $field->default ) ? $field->default : '';
$field->allowed_file_types = ! empty( $field->allowed_file_types ) ? $field->allowed_file_types : '';
$field->max_file_size = ! empty( $field->max_file_size ) ? $field->max_file_size : '';
$field->depends = ! empty( $field->depends ) ? $field->depends : '';
$field->display_conditions = ! empty( $field->display_conditions ) ? $field->display_conditions : '';
}
}

Expand Down Expand Up @@ -1673,6 +1723,8 @@ function pmpro_load_user_fields_from_settings() {
'options' => $options,
'levels' => $levels,
'memberslistcsv' => true,
'depends' => $settings_field->depends,
'display_conditions' => $settings_field->display_conditions,
'allowed_file_types' => $settings_field->allowed_file_types,
'max_file_size' => $settings_field->max_file_size,
'default' => $settings_field->default,
Expand Down
45 changes: 45 additions & 0 deletions js/pmpro-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,41 @@ function pmpro_userfields_prep_click_events() {
pmpro_userfields_made_a_change();
});

// Make sure the file is finished loading for this logic of conditional logic.
jQuery(document).ready(function() {
// Show the conditional fields if previously checked on page load/interaction.
if ( jQuery( '.pmpro_userfield-field-options a.edit-field' ).click( function(){
if ( jQuery(this).closest('.pmpro_userfield-group-field').find('input[name="pmpro_userfields_field_show_conditional_logic"]').is(":checked") ) {
jQuery(this).closest('.pmpro_userfield-group-field').find('#pmpro_userfield-field-setting-conditional-logic').show();
}

var condition_value = jQuery(this).closest('.pmpro_userfield-group-field').find('#pmpro_userfields_field_conditional_logic_condition');

// Show any predefined conditional fields.
if ( condition_value.val() !== 'is_empty' && condition_value.val() !== 'is_not_empty' && condition_value.val() !== '' ) {
jQuery(this).closest('.pmpro_userfield-group-field').find('#pmpro_userfields_field_conditional_logic_value').show() ;
}
}));

// Show or hide the conditional value field if the fields are not "is_empty" and "is_not_empty".
jQuery('select#pmpro_userfields_field_conditional_logic_condition').on('change', function() {
if( jQuery(this).val() !== 'is_empty' && jQuery(this).val() !== 'is_not_empty' && jQuery(this).val() !== '' ) {
jQuery(this).closest('.pmpro_userfield-field-setting').next('#pmpro_userfields_field_conditional_logic_value').show();
} else {
jQuery(this).closest('.pmpro_userfield-field-setting').next('#pmpro_userfields_field_conditional_logic_value').hide();
}
});

// Show or hide condtion fields if the checkbox is checked or not. This tries to show the first field setting after the conditonal logic checkbox which triggers to show all fields within the div as well.
jQuery('input[name="pmpro_userfields_field_show_conditional_logic"]').click(function() {
if(jQuery(this).is(":checked")) {
jQuery(this).closest('.pmpro_userfield-field-setting').next().show();
} else {
jQuery(this).closest('.pmpro_userfield-field-setting').next().hide();
}
});
});

// Delete field button.
jQuery('.pmpro_userfield-field-options a.delete-field, .pmpro_userfield-field-actions .is-destructive').unbind('click').on('click', function (event) {
var thefield = jQuery(this).closest('.pmpro_userfield-group-field');
Expand Down Expand Up @@ -515,10 +550,18 @@ function pmpro_userfields_prep_click_events() {
let field_element_class = jQuery(this).find('input[name=pmpro_userfields_field_divclass]').val();
let field_hint = jQuery(this).find('textarea[name=pmpro_userfields_field_hint]').val();
let field_options = jQuery(this).find('textarea[name=pmpro_userfields_field_options]').val();
let field_display_conditions = jQuery(this).find('input[name=pmpro_userfields_field_show_conditional_logic]').is(':checked');
let field_allowed_file_types = jQuery(this).find('input[name=pmpro_userfields_field_allowed_file_types]').val();
let field_max_file_size = jQuery(this).find('input[name=pmpro_userfields_field_max_file_size]').val();
let field_default = jQuery(this).find('input[name=pmpro_userfields_field_default]').val();

// Field conditions (aka depends)
let field_conditions = {
'id': jQuery(this).find('input[name=pmpro_userfields_field_conditional_logic_field]').val(),
'value': jQuery(this).find('input[name=pmpro_userfields_field_conditional_logic_value]').val(),
'condition': jQuery(this).find('select[name=pmpro_userfields_field_conditional_logic_condition]').val()
};

// Get level ids.
let field_levels = [];
jQuery(this).find('input[name="pmpro_userfields_field_levels[]"]:checked').each(function () {
Expand All @@ -537,6 +580,8 @@ function pmpro_userfields_prep_click_events() {
'element_class': field_element_class,
'hint': field_hint,
'options': field_options,
'display_conditions': field_display_conditions,
'depends': field_conditions,
'allowed_file_types': field_allowed_file_types,
'max_file_size': field_max_file_size,
'default': field_default
Expand Down