Skip Navigation

[Resolved] Is it possible to "replace" rather than add new attachements via Toolset Forms?

This thread is resolved. Here is a description of the problem and solution.

Problem:

The customer noticed that users were able to have multiple photos attached to their profiles in the WordPress Admin, even though only one photo should be allowed. He wanted to ensure that if a user removes their profile photo and resubmits the form, the photo is permanently deleted. Additionally, if a user chooses a new photo, it should replace the existing one.

Solution:

We explained that Toolset Forms does not natively support this feature but provided a workaround using custom code:

1- Use the cred_save_data action hook to trigger a PHP function when a user submits the post form.

2- In the PHP function:
• Retrieve images attached to the current post using get_attached_media.
• Access images stored in the custom repeating image field using get_post_meta.
• Compare these sets of images to identify and remove unused files using wp_delete_attachment.

Relevant Documentation:
https://toolset.com/forums/topic/delete-images-from-server-after-front-end-cred-form-update/#post-1604187
https://toolset.com/documentation/programmer-reference/cred-api/
https://developer.wordpress.org/reference/functions/get_attached_media/
https://codex.wordpress.org/Function_Reference/wp_delete_attachment

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
- 9:00 – 12:00 9:00 – 12:00 9:00 – 12:00 9:00 – 12:00 9:00 – 12:00 -
- 13:00 – 18:00 13:00 – 18:00 13:00 – 18:00 13:00 – 18:00 13:00 – 18:00 -

Supporter timezone: America/Sao_Paulo (GMT-03:00)

This topic contains 14 replies, has 2 voices.

Last updated by simonM-5 4 months, 1 week ago.

Assisted by: Mateus Getulio.

Author
Posts
#2705953
Screenshot 2024-07-03 at 17.33.09.png

Hi Support

On some of our Toolset Post Forms our users use to create their profiles, we allow them to upload a profile photo into a custom field. On an Edit version of a Post Form, we allow them to choose a new picture, or to remove their picture (as it is not a required field) and resubmit their profile.

An example of the Edit form settings in screenshot.

We noticed some users have multiple photos in their profile (in the WordPress Admin, not in the front end), however since we only allow to have one photo, is there a way to prevent multiple photos from being attached to the record?

In essence if they remove their photo and resubmit the form, we would like to remove the photo attachment permanently, or if they choose another photo, it should REPLACE the existing profile photo.

Is this possible without too much hassle, eg by changing a setting? It seems so silly to permanently store all those unused photos which the users can never again access.

Thanks and kind regards
Simon

#2706111

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

I'm afraid that the Toolset Forms plugin does not include this feature natively, there's a feature request but I can't give you an estimate of when it will be available.

However, you can implement a workaround using custom code. Here’s how:

1- When a user submits the post form, use the action hook cred_save_data to trigger a PHP function. You can find more information on this hook here: API Documentation

2- In your PHP function:

- Retrieve the images attached to the current post using the get_attached_media function: Get Attached Media.
- Access the images stored in your custom repeating image field using get_post_meta: Get Post Meta.
- Compare these two sets of images, identify the unused files, and remove them using wp_delete_attachment: Delete Attachment.

Another user shared a working example here that might help you:

https://toolset.com/forums/topic/delete-images-from-server-after-front-end-cred-form-update/#post-1604187

I hope this helps!
Mateus

#2706117

Hi Mateus

Firstly good news that it's in the pipeline, it really is a big gap and causes huge unnecessary disk usage. 🙂

Moving forward, I'm not an a PHP expert, but this looks promising. Our photos are stored in a custom field, and I don't see any mention of any custom fields in the code, so I'm wondering if these are attachments on the user record, rather than attachments to a custom post type record...

1) can you confirm, based on that code, if it would be just be photos that are removed, and not PDFs, DOCXs etc? For example, our users upload CVs to us and we need to keep those!

2) would you be able to update the code to include all relevant form IDs in an array, something like:

$forms = array( 56261, 56251, 56255, 56244, 56272, 56274, 69846 );
	if ( in_array($form_data['id'],$forms) ){

instead of:

if (($form_data['id']==88) || ($form_data['id']==114)), something like

Kind regards
Simon

#2706271

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

Thank you for your feedback and for providing additional context. I'll address your questions and requests below.

1- File Types to be Removed: The provided code example primarily targets attachments related to images. To ensure that only image files are removed (and not PDFs, DOCXs, etc.), you can modify the code to specifically target image MIME types. Here's an updated version of the code to do that:

/* Custom Action to Cleanup files from database and server upon submitting specific cred forms */
add_action('cred_save_data','images_cleanup', 10, 2);
function images_cleanup($post_id, $form_data) {
    $forms = array(56261, 56251, 56255, 56244, 56272, 56274, 69846);
    if (in_array($form_data['id'], $forms)) {
        $type = get_post_type($post_id, $form_data);
        if ($type == 'profile') {
            global $wpdb;
            $allposts = $wpdb->get_results($wpdb->prepare(
                "SELECT id FROM wp_posts 
                WHERE post_parent=%d 
                AND post_type='attachment' 
                AND post_mime_type LIKE 'image/%%'
                AND guid NOT IN (SELECT meta_value FROM wp_postmeta 
                                 WHERE post_id=%d 
                                 AND (meta_key LIKE 'wpcf-user-gallery' 
                                      OR meta_key LIKE '_wp_attached_file' 
                                      OR meta_key LIKE 'wpcf-user-photo'))", 
                $post_id, $post_id));
            foreach ($allposts as $singlepost) { 
                $post_id_n1 = $singlepost->id;
                wp_delete_attachment($post_id_n1, true);
            }
        }
    }
}

This updated version includes a condition to check the MIME type of the attachments and only delete those that are images. Please perform tests with test user to make sure it is working as intended.

2- Form IDs in an Array: I have updated the code to include the form IDs in an array as you requested. This will allow the function to work with all specified forms:

$forms = array(56261, 56251, 56255, 56244, 56272, 56274, 69846);
if (in_array($form_data['id'], $forms)) {
    // your code here
}

With these adjustments, the script should now only remove image files from the specified forms and ignore other types of files like PDFs and DOCXs.

If you need further assistance or have any more questions, feel free to reach out.

Kind regards,
Mateus

#2706696
Screenshot 2024-07-07 at 12.43.00.png

Hi Mateus

Thanks very much for those minor code modifications. When I try to add the snippet to our dev site however, I am unable to save the snippet, see screenshot.

I added the snippet to our dev site. I didn't see any error when I saved the code snippet the first time, so I tested by updating a nanny profile from the front end to see if the code snippet was working.

When I refreshed the nannies in the back end, I noticed the old image attachments were still on the nanny record, so I re-examined the code snippet, which had somehow now been wiped out! It was like a blank new code snippet.

I tried deleting the snippet altogether and re-adding it, but am unsuccessful. Now I cannot save the snippet at all.

Any chance you could take a look? Happy to provide credentials to our dev site. I cannot work out what could be going wrong.

Thanks and regards
Simon

#2706793

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

I would like to request temporary access (wp-admin and FTP) to your development site to take better look at the issue. You will find the needed fields for this below the comment area when you log in to leave your next reply. The information you will enter is private which means only you and I can see and have access to it.

Our Debugging Procedures

I will be checking various settings in the backend to see if the issue can be resolved. Although I won't be making changes that affect the live site, it is still good practice to backup the site before providing us access. In the event that we do need to debug the site further, I will duplicate the site and work in a separate, local development environment to avoid affecting the live site.

Privacy and Security Policy

We have strict policies regarding privacy and access to your information. Please see:
https://wpml.org/purchase/support-policy/privacy-and-security-when-providing-debug-information-for-support/

**IMPORTANT**

- Please make a backup of site files and database before providing us access.
- If you do not see the wp-admin/FTP fields this means your post & website login details will be made PUBLIC. DO NOT post your website details unless you see the required wp-admin/FTP fields. If you do not, please ask me to enable the private box. The private box looks like this: hidden link

Please, let me know if you need any additional details. Have a nice day.

#2706857

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

I adjusted the code slightly for it to work properly.

Firstly, I changed the tables from wp_posts and wp_postmeta to wpdev_posts and wpdev_postmeta respectively, editing the prefix to match with the db prefix of the test site.

Then I checked what was that form ID(post-form-edit-native-nanny-user) and added it to the verification: 16684.

Finally, I noticed that the post type check also need some update, I changed it to nanny.

Here's what the code looks like now:

add_action('cred_save_data','images_cleanup', 10, 2);
function images_cleanup($post_id, $form_data) {
    $forms = array(16684, 56261, 56251, 56255, 56244, 56272, 56274, 69846);
    if (in_array($form_data['id'], $forms)) {
        $type = get_post_type($post_id, $form_data);
        if ($type == 'nanny') {
            global $wpdb;
            $allposts = $wpdb->get_results($wpdb->prepare(
                "SELECT id FROM wpdev_posts 
                WHERE post_parent=%d 
                AND post_type='attachment'
                AND post_mime_type LIKE 'image/%%'
                AND guid NOT IN (SELECT meta_value FROM wpdev_postmeta 
                                 WHERE post_id=%d 
                                 AND (meta_key LIKE 'wpcf-user-gallery'
                                      OR meta_key LIKE '_wp_attached_file'
                                      OR meta_key LIKE 'wpcf-user-photo'))", 
                $post_id, $post_id));
            foreach ($allposts as $singlepost) { 
                $post_id_n1 = $singlepost->id;
                wp_delete_attachment($post_id_n1, true);
            }
        }
    }
}

I added this code to the theme's functions.php file and tested changing that test user's avatar in the link you shared. After that, I checked it and the picture got changed, the old ones deleted, but the document attachments were left alone.

Can you please test it and confirm that it is working?

Thank you,
Mateus

#2707061

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

Thank you for the details and screenshots.

This is an update to let you know that I started working on it but so far I wasn't able to find a fix, I'll let you know as soon as I have more news, which shouldn't be long.

Thank you!

#2707256

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

After working on it a bit longer I got the final version of the function:

add_action('cred_before_save_data','images_cleanup', 10, 2);
function images_cleanup($form_data) {	
    // Post Form - New Native Nanny User  (ID: 16624)
    // Post Form - Edit Unverified Native Nanny User  (ID: 16630)
    // Post Form - Edit Native Nanny User  (ID: 16684)
    $forms = array(16624, 16630, 16684);
    if (in_array($form_data['id'], $forms)) {
		$post_id = $form_data['container_id'];
        $type = get_post_type($post_id, $form_data);
        if ($type == 'nanny') {
            global $wpdb;
            
            // Get the TRID for the current post
            $trid = apply_filters('wpml_element_trid', null, $post_id, 'post_' . $type);
            
            // Get all translations for this TRID
            $translations = apply_filters('wpml_get_element_translations', null, $trid, 'post_' . $type);
            
            // Collect all post IDs including translations
            $post_ids = array($post_id);
            if ($translations) {
                foreach ($translations as $translation) {
                    $post_ids[] = $translation->element_id;
                }
            }

            // Prepare the query to include all relevant post IDs
            $placeholders = implode(',', array_fill(0, count($post_ids), '%d'));
            $query = $wpdb->prepare(
                "SELECT ID FROM {$wpdb->posts} 
                WHERE post_parent IN ($placeholders) 
                AND post_type='attachment'
                AND post_mime_type LIKE 'image/%%'
                AND guid NOT IN (
                    SELECT meta_value FROM {$wpdb->postmeta} 
                    WHERE post_id IN ($placeholders)
                    AND (meta_key LIKE 'wpcf-user-gallery'
                        OR meta_key LIKE '_wp_attached_file'
                        OR meta_key LIKE 'wpcf-user-photo')
                )", 
                array_merge($post_ids, $post_ids)
            );

            $allposts = $wpdb->get_results($query);
            foreach ($allposts as $singlepost) { 
                $post_id_n1 = $singlepost->ID;
                wp_delete_attachment($post_id_n1, true);
            }
        }
    }
}

I used wpml_get_element_translations to get the ID of that CPT translations and added it to the SQL that retrieves the media.

I also had to change the hook from cred_save_data to cred_before_save_data, otherwise it'd also remove the new picture.

I tested it and it seems to be working properly, please give it a try.

Best,
Mateus

#2707268

Oi Mateus

Thanks!! I will test this thoroughly and get back to you.

It will most likely be tomorrow before i get to it, but In the meantime, could you please edit my previous response to delete the login details from 3c, as requested? Muito obrigado!

https://toolset.com/forums/topic/is-it-possible-to-replace-rather-than-add-new-attachements-via-toolset-forms/#post-2706869

Kind regards
Simon

#2707292

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

I apologize, I thought I had already edited it, my mistake.

Just in case, I went ahead and made the whole reply private to hide even the screenshots. Now it is only available for you and me.

Thank you.
Mateus

#2707510

Thanks Mateus

We have done some testing. It isn't yet 100% - but it's very close!! We're trying to reproduce some errors we encountered. Will get back to you tomorrow with more info.

Kind regards
Simon

#2707562
Screenshot 2024-07-12 at 15.49.05.png

Oi Mateus

We have done some more intense testing and have noted the following:

1) Deleting the profile photo and saving always seems to work.

2) Clicking the little X and immediately choosing a new photo before saving seems to work too.

3) However, when we have deleted a photo and saved, and then upload a new profile and save it works about 9 times out of 10. But every so often we still see the blue unknown question mark like in the previously supplied screenshots. (Question: what exactly is WordPress signalling to us here? That it can't find the photo for which there is metadata or something???)

4) Furthermore, I noticed that when the user completes a "standard registration process" (here: hidden link) that the Nanny Ad in the 3rd form is not being duplicated any more via the custom code snippet func-sync-language-duplicates-on-submission. Can it be that the new code has prevented this previously working code to run, based on precedence or similar? Before we introduced the custom code of this ticket, the Nanny Ads were getting successful WPML duplicates created.

Feel free to Register on dev if you want to track what happens at each stage of the process (you can use a real or fake email address):

Step 1/3: hidden link
- User Form - User Form - New Native Nanny User (ID: 1242)
>> user gets created with role Unverified Nanny
>> on submission, user is directed to:

Step 2/3: hidden link
- Post Form - New Native Nanny User (ID: 16624)
>> custom post type Nanny record is created, and duplicated into other language
>> on submission, user is directed to:

Step 3/3:
hidden link
- Post Form - New Nanny Ad - Unverified Nanny (ID: 23612)
>> Unverified Nanny creates a Nanny Ad
>> on submission, it is saved as custom post type Nanny Ad, and (should be!) WPML-duplicated via custom code snippet func-sync-language-duplicates-on-submission (This is no longer happening!)

Perhaps we are approaching the problem from the wrong angle because we started using code from a previous ticket. I notice in the code that the custom field wpcf-nanny-profile-photo is never explicitly mentioned.

If I were to describe what the code should execute in English simply, I would say:

- If a Nanny clears the CPT Nanny field wpcf-nanny-profile-photo and saves, then before saving the Nanny record, delete the attachment, then save, then replicate the changes to the WPML duplicate.

- If a Nanny clears the CPT Nanny field wpcf-nanny-profile-photo and chooses another photo immediately, then before saving the Nanny record, delete the old attachment, attach the new one, then save the Nanny record, then replicate the changes to the WPML duplicate.

5) I noticed also in the back end, that there is no way to "delete" the image, rather there is only a "Replace Image" button. Is that what you expect? I'm guessing that's all standard WordPress stuff, but if the button says "Replace" image, I would hope that it would be actually replacing the image and not sneakily keeping the old attachments and uploading the new ones. 😃😃

Please let me know your thoughts.

Thanks and kind regards
Simon

#2707603

Mateus Getulio
Supporter

Languages: English (English )

Timezone: America/Sao_Paulo (GMT-03:00)

Hello Simon,

Thank you for your detailed feedback and for your thorough testing.

Firstly, I'd like to clarify that while I've been guiding you through the process of using Toolset hooks and providing some custom code examples, there are certain limitations to how far we can go with custom code support. Our primary focus is on supporting the functionalities and features provided natively by Toolset.

Regarding the issues you're encountering:

Profile Photo Deletion and Replacement:
The current workaround using custom code is the best option we have at the moment for handling the deletion and replacement of images. However, this approach can sometimes be complex and may not cover all edge cases perfectly.

Feature Request:
I recommend submitting a feature request for the ability to replace images via upload directly within Toolset Forms. This would greatly simplify the process and eliminate the need for custom code.

Further Assistance with Custom Code:
If you need more advanced custom code solutions, I suggest reaching out to one of our Toolset contractors. They have more experience in providing custom solutions and can offer more in-depth assistance.

I hope this helps! Please let me know if you have any further questions or need additional guidance.

Kind regards,
Mateus

#2708264

Oi Mateus

I have submitted a feature request.

It's a bit disappointing that we were so close to a solution and now have to break off so close from the finishing line.

Kind regards
Simon