Prepopulate Appointments Additional Field and Pass Values in URL

Hi,

Scenario:

We’re giving gifts to users who come in for an appointment.

Before setting their appointment, the user selects a gift to receive. That gift is registered in the url and the user is redirected to the Appointments+ page; http://www.example.com/scedule-appointment/?gift=stapler.

In Appointments, we have Additional Fields enabled.

1. Is there a way to populate the Additional Field in the Appointment form with the variable from the URL?
2. Is there a way to hide the Additional Field?
3. Is there a way to pass the value from the hidden field along with the date and time to the confirmation page in the URL? i.e. http://www.example.com/?date=02-05-17&time=900&gift=stapler

Thank you

    Vaughan

    Hi Wendy,

    Hope you're well?

    #1 This isn't currently possible i'm afraid, but it could possibly be done with some custom coding in the plugin, though this would affect future updates.

    I haven't tested this, but the following might work to populate a field from the URL.

    Open up & edit /wp-content/plugins/appointments/includes/pro/includes/addons/app-users-additional_fields.php

    Find the following function (lines 397 - 416)

    public function inject_additional_fields ($form) {
            global $current_user;
    
            $fields = !empty($this->_data['additional_fields']) ? $this->_data['additional_fields'] : array();
            if (empty($fields)) return $form;
    
            foreach ($fields as $field) {
                $label = esc_html($field['label']);
                $clean = $this->_to_clean_name($field['label']);
                $id = "appointments-{$clean}" . md5(serialize($field));
                $type = esc_attr($field['type']);
                $user_meta_value = get_user_meta( $current_user->ID, 'app_' . $clean, true );
                $value = $user_meta_value ? $user_meta_value : ('checkbox' == $type ? 1 : '');
                $form .= "<div class='appointments-field appointments-{$clean}-field'>" .
                    '<label for="' . $id . '"><span>' . $label . '</span></label>' .
                    "<input type='{$type}' id='{$id}' class='appointments-field-entry appointments-{$clean}-field-entry' data-name='additional_fields[{$clean}]' value='{$value}' />" .
                    "</div>";
            }
            return $form;
        }

    Replace with:

    public function inject_additional_fields ($form) {
            global $current_user;
    
            $fields = !empty($this->_data['additional_fields']) ? $this->_data['additional_fields'] : array();
            if (empty($fields)) return $form;
    
            foreach ($fields as $field) {
                $label = esc_html($field['label']);
                $clean = $this->_to_clean_name($field['label']);
                $id = "appointments-{$clean}" . md5(serialize($field));
                $type = esc_attr($field['type']);
                $user_meta_value = get_user_meta( $current_user->ID, 'app_' . $clean, true );
                $value = $user_meta_value ? $user_meta_value : ('checkbox' == $type ? 1 : '');
    
                if ('gift' == $clean) { // checks if additional field name is gift
                    $value = sanitize_text_field( $_GET['gift'] ); // URL query = gift
                }
    
                $form .= "<div class='appointments-field appointments-{$clean}-field'>" .
                    '<label for="' . $id . '"><span>' . $label . '</span></label>' .
                    "<input type='{$type}' id='{$id}' class='appointments-field-entry appointments-{$clean}-field-entry' data-name='additional_fields[{$clean}]' value='{$value}' />" .
                    "</div>";
            }
            return $form;
        }

    The above assumes that the url is using ?gift=giftname
    It also assumes that the additional field name is labelled as gift too.

    #2 to hide the field we can use CSS to hide it, but let's see if it actually works first.

    You would add the following to your theme style.css

    .appointments-field appointments-gift-field {display: none;}

    I'm assuming the field label is gift.

    #3 The additional fields data can already be passed to the confirmation emails already.

    You simply add the Text Replacement to the Email content in the Apps+ settings.

    So if your field is called gift.

    You just add FIELD_GIFT to the email form content in settings, and that will be replaced with the value entered.

    Hope this helps

    Vaughan

    Hi,

    I've made a few improvements in the code I gave you above. Instead I added a filter to the plugin code.

    Rather than using the code I gave you above, can you change it to use the following instead.

    public function inject_additional_fields ($form) {
            global $current_user;
    
            $fields = !empty($this->_data['additional_fields']) ? $this->_data['additional_fields'] : array();
            if (empty($fields)) return $form;
    
            foreach ($fields as $field) {
                $label = esc_html($field['label']);
                $clean = $this->_to_clean_name($field['label']);
                $id = "appointments-{$clean}" . md5(serialize($field));
                $type = esc_attr($field['type']);
                $user_meta_value = get_user_meta( $current_user->ID, 'app_' . $clean, true );
                $value = $user_meta_value ? $user_meta_value : ('checkbox' == $type ? 1 : '');
    
                $form_content = "<div class='appointments-field appointments-{$clean}-field'>" .
                    '<label for="' . $id . '"><span>' . $label . '</span></label>' .
                    "<input type='{$type}' id='{$id}' class='appointments-field-entry appointments-{$clean}-field-entry' data-name='additional_fields[{$clean}]' value='{$value}' />" .
                    "</div>";
    
                $form .= apply_filters("appointments-custom-{$clean}-field", $form_content, $label, $clean, $id, $type, $value);
            }
            return $form;
        }

    Once done, you can then use a custom filter in a mu-plugin instead.

    Create a file called appointments_custom_functions.php

    Add the following code to this file:

    <?php
    /*
     * You can create an additional filters for each additional fieldname
     * replacing <name> with the label for your additional field
     *
     * add_filter('appointments-custom-<name>-field', 'app_custom_additional_<name>_field', 999, 6);
     *
     * Then do the same in the function name.
     *
     * function app_custom_additional_<name>_field($form_content, $label, $clean, $id, $type, $value)
     */
    add_filter('appointments-custom-gift-field', 'app_custom_additional_gift_field', 999, 6);
    function app_custom_additional_gift_field($form_content, $label, $clean, $id, $type, $value) {
        // check if the form type is a text field, if not we return without doing anything.
        if ('text' !== $type) return $form_content;
    
        // We check if the form field name is <name of field>, if not, we skip it.
        if('gift' == $clean) {
            $type = 'hidden'; // We're changing the text field type to hidden so it is hidden from display.
    
            $value = sanitize_text_field( $_GET['gift'] ); // you must sanitize the $_GET otherwise it's a huge security risk.
    
            $form_content = "<div class='appointments-field appointments-{$clean}-field'>" .
                        "<input type='{$type}' id='{$id}' class='appointments-field-entry appointments-{$clean}-field-entry' data-name='additional_fields[{$clean}]' value='{$value}' />" .
                        "</div>";
        }
    
        return $form_content;
    }

    Then upload this file to

    /wp-content/mu-plugins/

    If mu-plugins folder doesn't exist already, just create it first.

    I will ask the developers to add this filter in to the plugin code in future releases, this will then make sure that any plugin updates will not break your custom filter in mu-plugins.

    Regarding #3 I will need to spend a bit more time looking into that part for you, so bare with me on that as that's a little more complex.

    Do you mean you want the parameters passed in the URLwhen you have submitted the appointment and it redirects you to the assigned 'Thank you' page in the settings.

    Hope this helps

    Wendy

    Do you mean you want the parameters passed in the URL when you have submitted the appointment and it redirects you to the assigned 'Thank you' page in the settings.

    Yes, that is correct

    Alternatively, is there to save the appointment date/time in the user meta?

    And a BIG Thank You, by the way for going way above and beyond with the custom code, it's greatly appreciated.

    Panos

    Hi Wendy ,

    For a more accurate answer I would need a link to your actual page so I can get specific ids to use and check if you ar using free appointments or paid ones.

    If you feel comfortable with javascript, you can add the following snippet in your theme's functions.php or a mu-plugin:

    add_action( 'wp_footer', function(){
    
    	global $post;
    
    	if( ! isset( $post->ID ) || $post->ID != 58 ) return;
    
    	?>
    
    	<script type="text/javascript">
    
    		(function($){
    
    			$( document ).ready( function(){
    
    				var selected_date = '';
    
    				$( '.app_timetable_cell' ).on( 'click', function(){
    
    					var atts = $( this ).find( '.appointments_take_appointment' ).val();
    
    					var s_atts = atts.split(":");
    
    					selected_date = s_atts[3];
    
    				});
    
    				$( '.appointments-confirmation-button' ).on( 'click', function(){
    					var gift = $( '#appointments-gift7a1c191da3c36b6af6f305bec3802cd0' ).val();
    
    					history.pushState( null, null, window.location.href + '?gift=' + gift + '&date='+selected_date 	);
    
    				});
    
    			});
    
    		})(jQuery);
    
    </script>
    
    	<?php
    
    }, 10 );

    The above snippet works for free appointments and you will need to replace the id of your appointments page and the id of the element that contains the gift. As for time and date, currently it sends a datetime string which you can convert with php to date and time.

    I assume you already have a method for getting these vars from the url.

    A link to your appointments page would make things clearer

    Looking forward to your reply!

    Thanks!

    Wendy

    My apologies for my absence, we had some pressing issues come up. I'm back working on this.

    I noticed after updating the Appointments+ plugin the free gift field no longer populates.

    On Feb. 3 you gave me code to create an MU plugin. I did, and that's still there. But you also gave me code to update the file: app-users-additional_fields.php

    The code I added there is gone (from the update I'm sure) but the code I was supposed to replace has changed since then and now I can't figure out which where to put it. Can you help?

    Also, I just checked and the code you gave me on Feb. 13 is still in my themes functions.php file. And I did update the post ID to reflect to proper page.

    Would it be easier to save the appointment date & time to a custom user profile meta field? Gravity Forms lets me populate fields with user meta values, maybe that would be easier than transferring the date and time query string. What do you think?

    Can I DM the appt. page URL to you?

    Thank you again for helping me as much as you have.

    Vaughan

    Hi Wendy,

    Can you try replacing the code in your /mu-plugin file to the following instead.

    add_filter('appointments_additional_fields_field_value', 'app_custom_additional_gift_field', 999, 2);
    function app_custom_additional_gift_field($value, $field) {
        // check if the form type is a text field, if not we return without doing anything.
        if ('text' !== $field['type']) return $value;
    
        $clean = preg_replace('/[^-_a-z0-9]/', '', strtolower($field['label']));
    	if (empty($clean)) $clean = substr(md5($field['label'])), 0, 8);
    
        // We check if the form field name is <name of field>, if not, we skip it.
        if('gift' == $clean) {
            $value = sanitize_text_field( $_GET['gift'] ); // you must sanitize the $_GET otherwise it's a huge security risk.
        }
    
        return $value;
    }

    Hopefully, it should now pre-populate again for you.

    Thanks

    Vaughan

    Hi Wendy,

    Which part isn't working? The code I provided to pre-populate the gift field or the code from when Panos took over for the passing back the values?

    The code for pre-populating the value should be:

    <?php
    add_filter('appointments_additional_fields_field_value', 'app_custom_additional_gift_field', 999, 2);
    function app_custom_additional_gift_field($value, $field) {
        // check if the form type is a text field, if not we return without doing anything.
        if ('text' !== $field['type']) return $value;
    
        $clean = preg_replace('/[^-_a-z0-9]/', '', strtolower($field['label']));
    	if (empty($clean)) return $value;
    
        // We check if the form field name is <name of field>, if not, we skip it.
        if('gift' == $clean) {
            $value = sanitize_text_field( $_GET['gift'] ); // you must sanitize the $_GET otherwise it's a huge security risk.
        }
    
        return $value;
    }

    Thanks

    Wendy

    Hi Vaughan,

    That code you just posted works, thank you. The only difference I noticed was on line 7 the previous code had: $clean = substr(md5($field['label'])), 0, 8);
    Anyway, that part works now. Thank you.

    The code from Panos almost works. It shows up in the URL for a second on the appointment page, but as soon as you click 'ok' the appointment page redirects to the Thank You page (assigned in settings), it disappears.

    Panos

    Hey ther Wendy ,

    I just noticed that you mentioned you have set the "Appointment thank you page". I have modified the code a bit so it should be adding the query vars to the redirect link too. I tested it on my test site and seems to be working:

    add_action( 'wp_footer', function(){
    
    	global $post;
    	if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'app_confirmation') ) {
    
    	?>
    
    	<script type="text/javascript">
    
    		(function($){
    
    			$( document ).ready( function(){
    
    				var selected_date = '';
    
    				$( '.app_timetable_cell' ).on( 'click', function(){
    
    					var atts = $( this ).find( '.appointments_take_appointment' ).val();
    
    					var s_atts = atts.split(":");
    
    					selected_date = s_atts[3];
    
    				});
    
    				$( '.appointments-confirmation-button' ).on( 'click', function(){
    					var gift = $( '#appointments-gift7a1c191da3c36b6af6f305bec3802cd0' ).val();
    
    					if ( _appointments_data.thank_page_url ) {
    						_appointments_data.thank_page_url += '?gift=' + gift + '&date='+selected_date;
    					}
    
    					history.pushState( null, null, window.location.href + '?gift=' + gift + '&date='+selected_date 	);
    
    				});
    
    			});
    
    		})(jQuery);
    
    </script>
    
    	<?php
    
    }, 10 );

    \
    Please let us know how it goes

    Cheers!

    Vaughan

    Hi Wendy,

    Yes, it looks like Panos missed a closing brace in the code.

    Can you try this:

    add_action( 'wp_footer', function() {
    	global $post;
    
    	if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'app_confirmation') ) {
        	?>
    
            <script type="text/javascript">
                (function($){
                    $( document ).ready( function(){
                        var selected_date = '';
    
                        $( '.app_timetable_cell' ).on( 'click', function(){
                            var atts = $( this ).find( '.appointments_take_appointment' ).val();
                            var s_atts = atts.split(":");
    
                            selected_date = s_atts[3];
                        });
    
                        $( '.appointments-confirmation-button' ).on( 'click', function(){
                            var gift = $( '#appointments-gift7a1c191da3c36b6af6f305bec3802cd0' ).val();
    
                            if ( _appointments_data.thank_page_url ) {
                                _appointments_data.thank_page_url += '?gift=' + gift + '&date='+selected_date;
                            }
    
                            history.pushState( null, null, window.location.href + '?gift=' + gift + '&date='+selected_date 	);
                        });
                    });
                })(jQuery);
            </script>
            <?php
        }
    }, 10);
    Panos

    Hi Wendy ,

    Sorry for the typo

    First you need to locate the id of the age field. You can do that withyour browser inspector by right clicking on the text box where user is supposed to enter his age.

    Then replace the previous snipped with this one:

    add_action( 'wp_footer', function() {
    	global $post;
    
    	if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'app_confirmation') ) {
        	?>
    
            <script type="text/javascript">
                (function($){
                    $( document ).ready( function(){
                        var selected_date = '';
    
                        $( '.app_timetable_cell' ).on( 'click', function(){
                            var atts = $( this ).find( '.appointments_take_appointment' ).val();
                            var s_atts = atts.split(":");
    
                            selected_date = s_atts[3];
                        });
    
                        $( '.appointments-confirmation-button' ).on( 'click', function(){
                            var gift = $( '#appointments-gift7a1c191da3c36b6af6f305bec3802cd0' ).val();
                            var age = $( '#ID_OF_AGE_ELEMENT' ).val();
    
                            if ( _appointments_data.thank_page_url ) {
                                _appointments_data.thank_page_url += '?gift=' + gift + '&date='+selected_date;
                            }
    
                            history.pushState( null, null, window.location.href + '?gift=' + gift + '&date='+selected_date  + '&age=' + age );
                        });
                    });
                })(jQuery);
            </script>
            <?php
        }
    }, 10);

    and in that snippet replace
    ID_OF_AGE_ELEMENT
    with the id of the element you got from the browser inspector.

    Hope this helps

    Vaughan

    Hi Wendy,

    Try adding the missing age.

    So below:

    add_action( 'wp_footer', function() {
    	global $post;
    
    	if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'app_confirmation') ) {
        	?>
    
            <script type="text/javascript">
                (function($){
                    $( document ).ready( function(){
                        var selected_date = '';
    
                        $( '.app_timetable_cell' ).on( 'click', function(){
                            var atts = $( this ).find( '.appointments_take_appointment' ).val();
                            var s_atts = atts.split(":");
    
                            selected_date = s_atts[3];
                        });
    
                        $( '.appointments-confirmation-button' ).on( 'click', function(){
                            var gift = $( '#appointments-gift7a1c191da3c36b6af6f305bec3802cd0' ).val();
                            var age = $( '#ID_OF_AGE_ELEMENT' ).val();
    
                            if ( _appointments_data.thank_page_url ) {
                                _appointments_data.thank_page_url += '?gift=' + gift + '&date='+selected_date + '&age=' + age;
                            }
    
                            history.pushState( null, null, window.location.href + '?gift=' + gift + '&date='+selected_date  + '&age=' + age );
                        });
                    });
                })(jQuery);
            </script>
            <?php
        }
    }, 10);

    jQuery isn't my expertise, but I think that should do it.

    Cheers

    Panos

    Hey Wendy!

    You can use $_GET or $_REQUEST to get that value with php, and you can convert it using the date() function to the format you like, for example:
    '
    $date = date( 'F j, Y', $_GET['date'] );
    '
    The F j, Y is the format you mentioned on your example above : May 6, 2017

    Not sure if this is of any help to you. Please provide further information about what you need to do with the date so we could provide a more accurate answer

    Thanks!

    Panos

    Hey Wendy,

    F j Y format is for:
    F: A full textual representation of a month, such as January or March

    j: Day of the month without leading zeros
    and you'll have to add:
    S: English ordinal suffix for the day of the month, 2 characters
    based on your last post for adding the suffix "th" or "st"

    Y: A full numeric representation of a year, 4 digits

    For that time format you can use g:i a . Your date format would be in the form: "F jS, Y g:i a", so in php you could use:
    $date = date( 'F jS, Y g:i a', $_GET['date'] );

    You can read more here about date format: http://php.net/manual/en/function.date.php

    Cheers!

    Panos

    Good to know it worked

    Of what I see Vaughan also provided code for you to add in your mu-plugin file, so you didn't have to edit any plugin core file. This way you can keep these changes when plugin gets updated.

    If you have modified a plugin file could you please let us know so that we can look if there is a way to accomplish what you need using some of the available hooks?

    Also, in case you added these snippets in your theme's functions.php instead of a mu-plugin, I would recommend to create a child theme and transfer these changes to your child theme's functions.php. So they don't get lost with theme update.

    Cheers!

    Wendy

    Sooo... everything was working smoothly on the primary site so I tried on one of the other installs but the vars aren’t passing to the confirmation page.

    I checked the field id’s for the Additional Fields and realized they’re different on each install, even though they’re the same fields added in the same order.

    But in the code for the functions/mu file I can only assign one field id per variable. How does that work?

    Mahlamusa

    Hello Wendy,

    I hope you are doing great. I am sorry for the late reply with regards to this.

    But in the code for the functions/mu file I can only assign one field id per variable. How does that work?

    Are these completely separate installs or sub sites in a multisite install? If they are standalone WordPress installs, you can just modify the fields and variables to suite the new IDs for that install.

    Is there a way to use this to populate the Email field from the URL? It would need to override the email address of the user currently logged in.

    If you have the email address in the url like this:

    '?gift=' + gift + '&date='+selected_date + '&age=' + age + '&email=' + email
    Then you should first find the id of the email field you want to populate, suppose its 'user_email' then use jQuery to target that and change its value as follows:

    modifying from the JavaScript above code, just after:

    $( '.appointments-confirmation-button' ).on( 'click', function(){
     //the rest of the code her
    });

    Add the following two lines

    var email = "<?php echo $_GET['email']; ?>";
    $('#user_email').val(email);

    Just make sure you have the correct ID for the email field you want to populate and that field should be populated with the correct value.

    I hope that helps.

    Cheers,
    Mahlamusa

    Vaughan

    Mahlamusa or panos

    If it's any help, the field ID is created & hashed in /appointments/includes/pro/includes/addons/app-users-additional_fields.php

    function inject_additional_fields

    $options = appointments_get_options();
    
    	    $fields = $options['additional_fields'];
    	    if ( ! is_array( $fields ) ) {
    	    	$fields = array();
    		}
    
    	    if ( empty( $fields ) ) {
    		    return $form;
    	    }
    
    	    ob_start();
    	    foreach ( $fields as $field ) {
    		    $clean           = $this->_to_clean_name( $field['label'] );
    		    $id              = "appointments-{$clean}" . md5( serialize( $field ) );
    		    $user_meta_value = get_user_meta( $current_user->ID, 'app_' . $clean, true );
    		    $value           = $user_meta_value ? $user_meta_value : ( 'checkbox' == $field['type'] ? 1 : '' );

    Maybe you could utilise that in your jQuery to get the ID automatically, rather than hardcoding it?

    Just a thought

    Panos

    Hey Wendy ,

    In order to get the values of these additional fields network wide, instead of the elements ids we can use their class names.

    So it should be enough to change the age and gift to:

    var gift = $( '.appointments-age-field-entry' ).val();
    var age = $( '.appointments-gift-field-entry' ).val();

    So the previous script should look like:

    add_action( 'wp_footer', function() {
    	global $post;
    
    	if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'app_confirmation') ) {
        	?>
    
            <script type="text/javascript">
                (function($){
                    $( document ).ready( function(){
                        var selected_date = '';
    
                        $( '.app_timetable_cell' ).on( 'click', function(){
                            var atts = $( this ).find( '.appointments_take_appointment' ).val();
                            var s_atts = atts.split(":");
    
                            selected_date = s_atts[3];
                        });
    
                        $( '.appointments-confirmation-button' ).on( 'click', function(){
                            var gift = $( '.appointments-age-field-entry' ).val();
                            var age = $( '.appointments-gift-field-entry' ).val();
    
                            if ( _appointments_data.thank_page_url ) {
                                _appointments_data.thank_page_url += '?gift=' + gift + '&date='+selected_date + '&age=' + age;
                            }
    
                            history.pushState( null, null, window.location.href + '?gift=' + gift + '&date='+selected_date  + '&age=' + age );
                        });
                    });
                })(jQuery);
            </script>
            <?php
        }
    }, 10);

    If you have trouble regarding how to get the class of these fields, right click on them and click "Inspect" (depending on browser it could have different but similar title ) and you can get their class like in the screnshot attached.

    I'm not sure I understand what your need to do with the email. Could you please provide additional information about this? Maybe describe the process until we can receive the email and once we get it what we need to do with it

    Thanks!