• On a WordPress powered site, I have a custom post type called “Free Stuff”. This CPT is used to register ebooks and other materials that can be downloaded by the user. It has a custom field created with ACF called “download_link”, where the administrator registers a URL to download the ebook.

    On the post page, to download, the user fills in a CF7 form that has a hidden Dynamic Text Extension field that is filled with the “download_link”. In the email sent to the user by CF7, the link is available for download. I use the field like this: [dynamichidden download_link “acf field=’download_link'”]

    This is working perfectly.

    Now I have the following challenge. I want to generate a shortcode that allows inserting a kind of widget referring to this post inside other regular posts.

    Using a custom function I created a new shortcode called [free_stuff] with a parameter called “id”, eg [free_stuff id=”1234″]. This shortcode generates an HTML with a title, a description and also the download form. In the id parameter I put the post id of “Free Stuff” and paste the shortcode in the post to be displayed. Below is the code that generates the shortcode:

    function material_gratuito_shortcode($atts) {
     
        $attributes = shortcode_atts(array(
            'id' => '',
        ), $atts);
    
        
        $id = $attributes['id'];
    
        
        if (empty($id)) {
            return 'Por favor, forne?a um ID válido.';
        }
    
       
        $html = '<div style="margin-bottom: 1rem!important; padding: 1rem!important; background: var(--color-content-bg-alt)!important;">';
        $html .= '<h3 style="text-align:center!important;font-weight:700;">';
        $html .= do_shortcode('[acf field="form_titulo" post_id="' . $id . '"]');
        $html .= '</h3>';
        $html .= '<p style="text-align:center;margin-bottom:1rem;">';
        $html .= do_shortcode('[acf field="form_descricao" post_id="' . $id . '"]');
        $html .= '</p>';
        $html .= do_shortcode('[contact-form-7 id="75206" title="Formulário para o blog"]');
        $html .= '</div>';
    
        return $html;
    }
    add_shortcode('free_stuff', 'material_gratuito_shortcode');

    As you can see, it will generate an HTML filled with some custom fields from the ACF of the selected post by ID. Furthermore it generates a CF7 form.

    The only problem is that inside this generated form, I need that instead of being [dynamichidden download_link "acf field='download_link'"], it should be [dynamichidden download_link "acf field='download_link' post_id='1234'"], being the ID number replaced by the top shortcode id.

    I’ve tried several things, but I can’t get it to work. Can you help me with this?

Viewing 7 replies - 1 through 7 (of 7 total)
  • Plugin Author Tessa (they/them), AuRise Creative

    (@tessawatkinsllc)

    Sure! I can help debug.

    So the real question is “how do we pass a variable through the contact-form-7 shortcode to the DTX shortcode?”

    Without some sort of hacky method, I don’t think DTX can do that (not yet anyway, sounds like building in ACF compatibility is a great idea!)

    Instead, I recommend building out your own custom form tag that can take the parameters since that is the point when a form tag has access to the attributes in a form, and you want to pass custom attributes to the form.

    To make your form tag, you’ll want to add these lines first:

    /**
     * Add Custom Shortcodes to Contact Form 7
     *
     * @return void
     */
    function prefix_cf7_acf_init_formtag()
    {
        //Add the dynamic ACF form field
        wpcf7_add_form_tag(
            'download_link', // Shortcode in Form Editor
            'prefix_download_link_handler', // Callback
            array('name-attr' => true) // Features
        );
    }
    add_action('wpcf7_init', 'prefix_cf7_acf_init_formtag');

    In your code snippet where you have:

    $html .= do_shortcode('[contact-form-7 id="75206" title="Formulário para o blog"]');

    You’ll need to pass the $id attribute to the contact-form-7 shortcode. Unfortunately, that is managed by the CF7 developers so we shouldn’t edit the callback function; we’ll just have to work with it. That function allows us to send only a few attributes:

    • id
    • title
    • html_id
    • html_name
    • html_title
    • html_class
    • output

    Until CF7 allows us to pass custom parameters through their shortcode, the safest one to mess around with, in my opinion, is html_class so I modified the code in my testing environment to this:

    $html .= do_shortcode('[contact-form-7 id="75206" title="Formulário para o blog" html_class="' . $id . '"]');

    This snippet shows how to get the current CF7 contact form, how to access the html_class attribute to get the post ID that was passed, and then use it to get the download link associated with that post.

    // Get the current Contact Form 7 form that is being processed
    $contact_form = WPCF7_ContactForm::get_current();
    if (!is_null($contact_form)) {
    
        // Get/sanitize the value of html_class that we passed in the contact-form-7 shortcode
        $post_id = sanitize_text_field($contact_form->shortcode_attr('html_class'));
    
        // Validate
        if (function_exists('get_field') && !empty($post_id) && is_numeric($post_id) && (int)$post_id > 0) {
    
            // Set the value to that of the ACF field shortcode
            $download_link = sanitize_url(get_field('download_link', $post_id));
        }
    }

    However, it is not recommended to set URLs directly as form values, since they’ll be escaped, so you’ll want to encode it.

    Finally, your callback function for your custom form tag might look like this:

    /**
     * Form Tag Handler
     *
     * @param WPCF7_FormTag $tag
     *
     * @return string HTML output of the shortcode
     */
    function prefix_download_link_handler($tag)
    {
        //Configure classes
        $class = wpcf7_form_controls_class($tag->type);
    
        //Configure input attributes
        $atts = array();
        $atts['type'] = 'hidden';
        $atts['name'] = $tag->type;
        $atts['id'] = $tag->get_id_option();
        $atts['class'] = $tag->get_class_option($class);
        $atts['tabindex'] = $tag->get_option('tabindex', 'signed_int', true);
        $atts['aria-invalid'] = 'false';
        $atts['aria-required'] = 'false';
        $atts['autocomplete'] = 'off';
        $atts['readonly'] = 'readonly';
    
        // Get the current Contact Form 7 form that is being processed
        $contact_form = WPCF7_ContactForm::get_current();
        if (!is_null($contact_form)) {
    
            // Get/sanitize the value of html_class that we passed in the contact-form-7 shortcode
            $post_id = sanitize_text_field($contact_form->shortcode_attr('html_class'));
    
            // Validate
            if (function_exists('get_field') && !empty($post_id) && is_numeric($post_id) && (int)$post_id > 0) {
    
                // Set the value to that of the ACF field shortcode
                $atts['value'] =  rawurlencode(sanitize_url(get_field('download_link', $post_id)));
            }
        }
    
        // Output the HTML consistent with CF7
        return sprintf(
            '<span class="wpcf7-form-control-wrap %s"><input %s /></span>',
            sanitize_html_class($tag->type),
            wpcf7_format_atts($atts) //This function already escapes attribute values
        );
    }

    And in the form editor, remove [dynamichidden download_link "acf field='download_link'"] and simply replace it with [download_link] to use your new custom form tag.

    Then on whatever post or page you use your custom shortcode [free_stuff id="1234"], it’ll pass that id attribute to the form to pull the download link stored in the meta for post 1234!

    Happy programming ??

    Plugin Author Tessa (they/them), AuRise Creative

    (@tessawatkinsllc)

    Of course after writing all that, I realize we could just use DTX for that! Simply add this extra shortcode and use it inside of your DTX value instead! Wow. I tend to always do the hard route first lol

    function get_acf_download_link()
    {
        // Get the current Contact Form 7 form that is being processed
        $contact_form = WPCF7_ContactForm::get_current();
        if (!is_null($contact_form)) {
    
            // Get/sanitize the value of html_class that we passed in the contact-form-7 shortcode
            $post_id = sanitize_text_field($contact_form->shortcode_attr('html_class'));
    
            // Validate
            if (function_exists('get_field') && !empty($post_id) && is_numeric($post_id) && (int)$post_id > 0) {
    
                // Set the value to that of the ACF field shortcode
                return esc_attr(rawurlencode(sanitize_url(get_field('download_link', $post_id))));
            }
        }
        return '';
    }
    add_shortcode('get_acf_download_link', 'get_acf_download_link');

    And then in the code editor:

    [dynamichidden download_link "get_acf_download_link"]

    But then still pass the $id in the contact-form-7 shortcode in your custom shortcode:

    $html .= do_shortcode('[contact-form-7 id="75206" title="Formulário para o blog" html_class="' . $id . '"]');

    Let me know if that works!

    Thread Starter Wiliam Jose Koester

    (@wiliamjk)

    It worked great, thank you so much for your dedication to help!

    The only problem is that when sanitizing the URL, it is no longer clickable in the email sent by CF7. It looks like this: https%3A%2F%2Fadvambiental.com.br%2Fdownload%2Fe-book-exemplo%2F%3Ftmstv%3D1686053354

    I’m not sure how to remove this sanitization…

    Thread Starter Wiliam Jose Koester

    (@wiliamjk)

    I just solved it! I’ve pasted the full snippet below if anyone is interested:

    function material_gratuito_shortcode($atts) {
    	
    	// Process Attributes
        $attributes = shortcode_atts(array(
            'id' => '',
        ), $atts);
    
        // Extract the attribute ID
        $id = $attributes['id'];
    
        // Check if ID is empty
        if (empty($id)) {
            return 'Você deve incluir o ID do material gratuito.';
        }
    	
    	// Construct the HTML based on the given ID
        $html = '<div style="margin-bottom: 1rem!important; padding: 1rem!important; background: var(--color-content-bg-alt)!important;">';
        $html .= '<h3 style="text-align:center!important;font-weight:700;">';
        $html .= do_shortcode('[acf field="form_titulo" post_id="' . $id . '"]');
        $html .= '</h3>';
        $html .= '<p style="text-align:center;margin-bottom:1rem;">';
        $html .= do_shortcode('[acf field="form_descricao" post_id="' . $id . '"]');
        $html .= '</p>';
        $html .= do_shortcode('[contact-form-7 id="75206" title="Formulário para o blog" html_class="' . $id . '"]');
        $html .= '</div>';
    
        return $html;
    }
    add_shortcode('material_gratuito', 'material_gratuito_shortcode');
    
    
    function get_acf_download_link()
    {
        // Get the current Contact Form 7 form that is being processed
        $contact_form = WPCF7_ContactForm::get_current();
        if (!is_null($contact_form)) {
    
            // Get/sanitize the value of html_class that we passed in the contact-form-7 shortcode
            $post_id = $contact_form->shortcode_attr('html_class');
    
            // Validate
            if (function_exists('get_field') && !empty($post_id) && is_numeric($post_id) && (int)$post_id > 0) {
    
                // Set the value to that of the ACF field shortcode
                return esc_attr(get_field('download', $post_id));
            }
        }
        return '';
    }
    add_shortcode('get_acf_download_link', 'get_acf_download_link');

    And, in the CF7 form, I included the following:

    [dynamichidden download_link "get_acf_download_link"]

    Thank you, Tessa!

    Plugin Author Tessa (they/them), AuRise Creative

    (@tessawatkinsllc)

    I’m glad you found something that worked!

    Personally, I’d keep it encoded for storage in the database, and then in the wpcf7_before_send_mail hook, simply decode it there, something like:

    function add_download_link_to_email ( $contact_form, $abort, $submission ) {
    
        //Get the posted data
        $posted_data = $submission->get_posted_data();
    
        // Only do stuff if the submitted form has this info
        if(array_key_exists('download_link', $posted_data) && !empty($posted_data['download_link'])) {
    
            // Decode/sanitize URL
            $download_url = sanitize_url(rawurldecode($posted_data['download_link'])); 
    
            if(!empty($download_url)) {
    
                // Get mail property
                $mail = $contact_form->prop('mail');
                
                // Add download link to email body
                $mail['body'] .= sprintf('<p><a href="%s" target="_blank" rel="noopener nofollow">%s</a></p>',
                    esc_url($download_url),
                    esc_html__('Download')
                );
    
                // Set mail property
                $contact_form->set_properties(array('mail' => $mail));
            }
        }
    }
    add_action( 'wpcf7_before_send_mail', 'add_download_link_to_email', 10, 3 );

    And then you wouldn’t add [download_link] in the email template as this code snippet would add it for you.

    Untrusted data comes from many sources (users, third party sites, even your own database!) and all of it needs to be checked before it’s used.

    Remember: Even admins are users, and users will enter incorrect data, either on purpose or accidentally. It’s your job to protect them from themselves.

    Sanitizing input is the process of securing/cleaning/filtering input data. Validation is preferred over sanitization because validation is more specific. But when “more specific” isn’t possible, sanitization is the next best thing.

    —Sanitizing Data

    Escaping?output is the process of securing output data by stripping out unwanted data, like malformed HTML or script tags. This process helps secure your data prior to rendering it for the end user.?

    —Escaping Data

    You can read more from the WordPress Developer Resources on Sanitizing Data and Escaping Data.

    Thread Starter Wiliam Jose Koester

    (@wiliamjk)

    This could be a native feature of Dynamic Text Extension, no?

    If it identifies that a URL is being inserted, it would automatically sanitize and then decode it when sending the email

    That way, you could help secure all of your users without them having to mess around with custom functions. I personally don’t have enough technical knowledge to do what you suggested on my own. I kind of hope the plugin to do this sort of thing.

    Plugin Author Tessa (they/them), AuRise Creative

    (@tessawatkinsllc)

    I like that idea.

Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘Dynamic text inside the form inside a shortcode with id parameter’ is closed to new replies.