Support

Account

Forum Replies Created

  • @hube2 ,

    Ages ago, you posted this code…

    <?php 
      
      // this action is run by ACF whenever a field is deleted
      // and is called for every field in a field group when a field group is deleted
      add_action('acf/delete_field', 'delete_acf_content_on_delete_field');
    
      function delete_acf_content_on_delete_field($field) {
        // runs when acf deletes a field
        // find all occurences of the field key in all tables and delete them
        // and the custom field associated with them
        global $wpdb;
        // remove any tables from this array that you don't want to check
        $tables = array('options', 'postmeta', 'termmeta', 'usermeta', 'commentmeta');
        foreach ($tables as $table) {
          $key_field = 'meta_key';
          $value_field = 'meta_value';
          if ($table == 'options') {
            $key_field = 'option_name';
            $value_field = 'option_value';
          }
          $table = $wpdb->{$table};
          // this query gets all key fields matching the acf key reference field
          $query = 'SELECT DISTINCT('.$key_field.')
                    FROM '.$table.'
                    WHERE '.$value_field.' = "'.$field['key'].'"';
          $results = $wpdb->get_col($query);
          if (!count($results)) {
            // no content found in this table
            continue;
          }
          // loop through keys and construct list of meta_key/option_names to delete
          $keys = array();
          foreach ($results as $key) {
            $keys[] = $key; // delete acf field key reference
            $keys[] = substr($key, 1); // delete acf field value
          }
          // do escping of all values.... just in case
          $keys = $wpdb->_escape($keys);
          // delete all of the content
          $query = 'DELETE FROM '.$table.'
                    WHERE '.$key_field.' IN ("'.implode('","', $keys).'")';
          $wpdb->query($query);
        } // end foreach table
      }
    
    ?>

    For me, it doesn’t seem to fire when I delete a field (and re-save the group).

    Any reason why, in 2023, that wouldn’t work?

    Thanks.

  • Thanks.

    Ahh, so an item in wp_postmetawould onjly be “orphaned” if I deleted the post those meta are linked to.

    That won’t necessarily solve a situation in which I would no longer want to have the postmeta at all – ie. If I stop using custom fields.

    Related –
    If I understand correctly, the Field Groups themselves are essentially stored as posts (in the wp_posts table).
    What happens if I delete a Field Group? Is that info adequately deleted from

    Thanks.

  • What’s the state of the art on this issue of deleting unused/orphaned field data?

    I read the long thread, which is now closed – https://support.advancedcustomfields.com/forums/topic/flush-unused-custom-fields/page/4/

    @marcwieland95’s whatwedo-acf-cleaner seems high-risk – blind deletion of database data
    @Bettylex’s edit-custom-fields plugin was removed from the repo on security grounds but is still on GitHub.
    – Can I learn how to delete orphaned ACF fields by searching under the general bracket of removing WordPress orphaned custom meta data, or is there something unique about ACF’s involvement?

    I am identifying fields to delete, but I’m hesitant to do so in case I have no way to remember their names. And that doesn’t even include the “how” of deleting specific fields.

  • This related to a bunch of commercial plugins, of which WP knew updates were available but which it could not do due to me not having entered license keys.

    Basically, ACF, Admin Columns Pro stuff.

    When I entered the keys, errors went away.

  • Thanks for the pointers.

    Maybe last thing… another way to maybe do it…

    Is there any way to create/store some kind of i) hidden text field and value or ii) field meta information against my Image field on the admin side… ?

    If I could add a “file path” value to the specific Image field itself (to the field “logo”), maybe I could use that to write in and store the value of the uploads child folder for that field (“company/logo”).

    I’m talking about alongside a field’s other standard ACF settings like sizes and “Required?”.

    If so, maybe this is a simpler way to do it than creating a new field group just to put it on edit-tags.php?taxonomy=company

    I see ACF Extended offers a Hidden field that include an admin-side box “Value
    – Default value in the hidden input”…

    But a) this is still separate from my Image field itself and b) it seems designed for accommodating hidden HTML input form fields (perhaps it just stores a piece of arbitrary “Value” that you can render as you like; I’m not sure yet).

  • Hmm…
    So, you can add a custom setting to a field, to all Images…
    I guess you’re saying I would hitch a new field to every Image field in the admin.

    I’m not sure if that would achieve the aim (would it?). Maybe I don’t want to work on *all* Images, ie. if the user has more than one Image field/purpose on-site…

    If the aim is to relocate images uploaded to the Image field in a group that sits on the term-edit page for all Company taxonomy terms… don’t I need to set a folder path value at the Company (taxonomy) level (ie. wp-content/uploads/{specific-folder}/{specific-subfolder}`)?

    I don’t currently have any “taxonomy” (Company) page on which I think a group/field could be added. Unless ACF can put anything on the listing page edit-tags.php?taxonomy=company … Yes, I note ACF Extended enables support for <a href=”https://www.acf-extended.com/features/field-groups/locations/taxonomy-list”>Taxonomy List</a>.

    Hmm, should I be putting a Text field on Taxonomy List for “Logo Image Directory” (eg. “/company/logos“)?

    If I then filter the actual uploads, I’d need some way to check against the above value to get the directory… ?

    Seems a bit convoluted and not necessarily like I could make it a neat plugin package… (?).

    Rather, maybe I shouldn’t bother with <em>specifying</em> an image directory and just <em>infer</em> it…

    ie. 1 /wp-content/uploads/{taxonomy_slug}/{acf_field_name}/{term_slug.ext} ?

    eg. 1 /wp-content/uploads/company/logo/microsoft.png ?

    Yes, though then I think I need to overcome two things…

    <strong>A. Application to other objects beside taxonomy…</strong>

    ie. 2. /wp-content/uploads/user/{acf_field_name}/{username.ext}

    eg. 2 /wp-content/uploads/user/avatar/billgates.png ?

    Achieved through some conditionals/check against object type? Not certain how, maybe the same way I’m already getting the URL parameters.

    <strong>B. Any way to avoid hard-coding/pre-specifying the field name and field key?</strong>

    Can “field_6140a6da30a17” and “avatar“/”logo" be auto-obtained, so that this could just work on anything, I wonder?

    Thinking in public.

  • John, thanks for the add_attachment tip.

    I also found similar code at https://wordpress.stackexchange.com/a/310689/39300

    Whilst it assumes Post is the connected object, it also works for User.

    The following code now takes the connected User’s name and uses that also for Title and Alt fields.

    All told, now I have:

    • Filter avatars uploads to a specific folder
    • Rename uploaded avatar to match username
    • Use User’s name as attached image’s Title and Alt fields

    Thanks!

    /**
     * ==============================================================================
     *             ALSO SET IMAGE TITLE & ALT TO USER/POST NAME
     *    When an image is uploaded, use the attached Post (User) edtails
     *    to set Title, Alt fields etc.
     *    cf. https://wordpress.stackexchange.com/a/310689/39300
     *    cf. https://support.advancedcustomfields.com/forums/topic/force-an-image-file-upload-to-a-particular-directory/page/2/
     * ==============================================================================
     */
    
    // https://wordpress.stackexchange.com/a/310689/39300
    function my_set_image_meta_upon_image_upload( $post_ID ) {
    
        // "the first thing that your action should do is to remove
        // itself so that it does not run again."
        remove_filter('add_attachment', 'your_function_name_here');
    
        // Check if uploaded file is an image, else do nothing
    
        if ( wp_attachment_is_image( $post_ID ) ) {
    
            $my_image_title = get_post( $post_ID )->post_title;
    
            // Added by Robert Andrews
            // Get user name to use in image details
            $user = get_user_by( 'slug', $my_image_title );
            $user_name = $user->display_name;
            $my_image_title = $user_name;
    
            // Sanitize the title:  remove hyphens, underscores & extra spaces:
            // $my_image_title = preg_replace( '%[-_]+%', ' ',  $my_image_title );
    
            // Sanitize the title:  capitalize first letter of every word (other letters lower case):
            // $my_image_title = ucwords( strtolower( $my_image_title ) );
    
            // Create an array with the image meta (Title, Caption, Description) to be updated
            // Note:  comment out the Excerpt/Caption or Content/Description lines if not needed
            $my_image_meta = array(
                'ID'        => $post_ID,            // Specify the image (ID) to be updated
                'post_title'    => $my_image_title,     // Set image Title to sanitized title
                // 'post_excerpt'  => $my_image_title,     // Set image Caption (Excerpt) to sanitized title
                // 'post_content'  => $my_image_title,     // Set image Description (Content) to sanitized title
            );
    
            // Set the image Alt-Text
            update_post_meta( $post_ID, '_wp_attachment_image_alt', 'Photo of '.$my_image_title );
    
            // Set the image meta (e.g. Title, Excerpt, Content)
            wp_update_post( $my_image_meta );
    
        } 
    }
  • This code successfully changes the filename to that of the username (+.ext).

    But how can I also alter two other Image fields – namely, Caption and Alt Text? (to be user’s Full/display/nice_name, not username).

  • Barring my if conditions being off, this is my working code for now…

    Big thanks for the help!!

    /**
     * ==============================================================================
     *             RENAME UPLOADED USER AVATAR IMAGE FILE WITH USERNAME
     *    When an image is uploaded to Edit User form through an ACF field (field_6140a6da30a17),
     *    rename file with the username of said user.
     * ==============================================================================
     */
    
    // 1. PASS USER_ID FROM USER-EDIT.PHP TO MEDIA UPLOADER, TO GET USERNAME FOR 
    // cf. https://support.advancedcustomfields.com/forums/topic/force-an-image-file-upload-to-a-particular-directory/
    // cf. https://wordpress.stackexchange.com/questions/395730/how-to-get-id-of-edit-user-page-during-wp-handle-upload-prefilter-whilst-in-med/395764?noredirect=1#comment577035_395764
    add_filter('plupload_default_params', function($params) {
        if (!function_exists('get_current_screen')) {
            return $params;
        }
        $current_screen = get_current_screen();
        if ($current_screen->id == 'user-edit') {
            $params['user_id'] = $_GET['user_id'];
        } elseif ($current_screen->id == 'profile') {
            $params['user_id'] = get_current_user_id();
        }
        return $params;
        });
    
    // 2. ON UPLOAD, DO THE RENAME
    // Filter, cf. https://wordpress.stackexchange.com/questions/168790/how-to-get-profile-user-id-when-uploading-image-via-media-uploader-on-profile-pa
    // p1: filter, p2: function to execute, p3: priority eg 10, p4: number of arguments eg 2
    add_filter('wp_handle_upload_prefilter', 'custom_upload_filter' );
    function custom_upload_filter( $file ) {
        // Working with $POST contents of AJAX Media uploader
        $theuserid = $_POST['user_id'];         // Passed from user-edit.php via plupload_default_params function
        $acffield  = $_POST['_acfuploader'];    // ACF field key, inherent in $_POST
        // If user_id was present AND ACF field is for avatar Image
        if ( ($theuserid) && ($acffield=='field_6140a6da30a17') ) {
            // Get ID's username and rename file accordingly, cf. https://stackoverflow.com/a/3261107/1375163
            $user = get_userdata( $theuserid );
            $info = pathinfo($file['name']);
            $ext  = empty($info['extension']) ? '' : '.' . $info['extension'];
            $name = basename($file['name'], $ext);
            $file['name'] = $user->user_login . $ext;
            // Carry on
            return $file;
        // Else, just use original filename
        } else {
            return $file;
        }
    }
  • Your code with no = typo and reverting _GET to $params seems to work…

    add_filter('plupload_default_params', function($params) {
      if (!function_exists('get_current_screen')) {
        return $params;
      }
      $current_screen = get_current_screen();
      if ($current_screen->id == 'user-edit') {
        $params['user_id'] = $_GET['user_id'];
      } elseif ($current_screen->id == 'profile') {
        $params['user_id'] = get_current_user_id();
      }
      return $params;
    });

    That is:

    a) The depopulated lists issue is gone.

    b) It still seems to successfully pass user_id to the uploader, allowing me to fetch the username with which to rename the file.

    ++

    Now, for a bonus…

    In my wp_handle_upload_prefilter function custom_upload_filter(), I should really be checking that the image upload in question is coming from the specific ACF Image field keyed field_6140a6da30a17…

    Do you happen to know if the AJAX $_POST in custom_upload_filter() already has access to any information about the instigating field in question, anything which would give away field_6140a6da30a17? (I see traces of it in the uploader’s HTML).

    Or would I somehow need, on user-edit.php, to a) check for its presence and b), if present, send it through the plupload_default_params function? (I presume I can stuff $params[] with multiple other values).

  • Every instance?

        // 1. Pass variables like user_ID from user-edit.php to Media uploader
        add_filter('plupload_default_params', function($_GET) {
            if (!function_exists('get_current_screen')) {
              return $_GET;
            }
            $current_screen = get_current_screen();
            if ($current_screen->id == 'user-edit') {
              $_GET['user_id'] = $_GET['user_id'];
            } elseif ($current_screen->id = 'profile') {
              $_GET['user_id'] = get_current_user_id();
            }
            return $_GET;
        });

    This results in “There has been a critical error on this website. Please check your site admin email inbox for instructions.” debug.log says: “Cannot re-assign auto-global variable _GET.”

    Changing _GET to _BLAH removes that error, but the lists are still wiped out.

  • Re: lists disappearing… maybe not related to ACP…

    I think the culprit is focused around…

    elseif ($current_screen->id = 'profile') {
              $params['user_id'] = get_current_user_id();
            }

    (If I remove that, things don’t break).

    Assuming the plupload_default_params function is getting executed on all pages (why would it?), does it maybe need a final else condition that just return $params;?

  • It may be some sort of conflict with Admin Columns Pro. Why is nothing ever easy?

    I think it’s the same for the other plupload_default_params code from StackExchange

  • I know what you mean.
    But using this code weirdly results in in admin list content (eg. user list, post list, media list) being depopulated. Counts are still good, but all rendered invisible.

  • I’ve been learning from Stackexchange.

    A. Allegedly, when the Media uploader Ajax appears, the user_id is no longer available. So there’s a solution (eventually arrived at) in calling the referrer at that point (wp_handle_upload_prefilter), then parsing out user_id, in order to fetch the user and username with which to rename the file. My working code does that.

    Still, I think I need a way to conditionalise that in two ways: i) only for uploads where the referrer is user-edit.php or profile.php, ii) only for uploads where the instigator is the ACF field with key field_6140a6da30a17. The latter one may get me back to square one – establishing a line of communication between the field group in admin and the media uploader box.

    B. The answer to my different question deals more specifically with adding parameters to an Ajax request. Don’t fully understand it yet, would need to take the time.

  • Doesn’t get_current_user_id() get the ID of the logged-in user, as opposed to the ID of the users currently visible on an Edit User form? (I, as admin, can edit other users).

  • To update the record…

    (1)

    The “Renaming” part in the following successfully renames a file to a username, retaining the file extension – but only if a valid user ID is supplied as $userid (hard-coded here as $userid=2)…

    // Filter entry, cf. https://wordpress.stackexchange.com/questions/168790/how-to-get-profile-user-id-when-uploading-image-via-media-uploader-on-profile-pa
    add_filter('wp_handle_upload_prefilter', 'my_pre_upload', 2, 2);
    // param 1: filter
    // param 2: function to execute
    // param 3: priority, eg 10
    // param 4: number of arguments, eg 2
    
    // pass the $userid as argument to your function
    function my_pre_upload($file, $userid=2){
        
        // if no user specified, get $current_user
        $userid = $userid ?: get_current_user_id();
        $user = get_userdata( $userid );
    
        // Renaming,  cf. https://stackoverflow.com/a/3261107/1375163
        $info = pathinfo($file['name']);
        $ext  = empty($info['extension']) ? '' : '.' . $info['extension'];
        $name = basename($file['name'], $ext);
        $file['name'] = $user->user_login . $ext;
    
        // Return
        return $file;
    }

    Therefore, the question becomes (still), at what point can I access user_id such that I can pass it through add_filter?

    (2)

    So, I am experimenting with whether I can set the user ID as a variable at the time the ACF field is loaded…

    // Get and remember User ID when field is loaded??
    
    function user_avatar_filter( $field ) {
    
        $myuserid = $_GET['user_id'];
        echo '<pre>load_field: GET user_id is '.$myuserid.'</pre>';
    
        // Important: return the field
        return $field;
    }
    add_filter('acf/load_field/key=field_6140a6da30a17', 'user_avatar_filter');

    For test, that code successfully echos out, to the ACF field’s part of the Edit User admin, the correct user ID based on whose Edit User page we are viewing.

    So, can that somehow be passed to my_pre_upload() as parameter $userid… ?

    I haven’t managed it, but I don’t know whether that’s down to a) my poor understanding of global/local variable context or b) that this still isn’t the way this works.

  • Interestingly, get_source_field() is in a different place in my version of ACF Pro – media.php – and it’s a <em>private</em> function (any way around that?)

    1)

    So, if I straight-up attempt to get the source field using that inside my WP upload pre-filter…

    add_filter('wp_handle_upload_prefilter', 'custom_upload_filter' );
     
    function custom_upload_filter( $file ) {
    
        // Attempt to get the ACF field
        $field = get_source_field();
    
        // Begin testing rename - not the finished article
        $file['name'] = 'and-everything-is-awesome-' . $file['name'];
        return $file;
    
    }

    … then the Media uploader warns:

    “Post-processing of the image likely failed because the server is busy or does not have enough resources. Uploading a smaller image may help. Suggested maximum size is 2500 pixels.”

    2)

    But, if I ostensibly copy the function code into my filter function…

    add_filter('wp_handle_upload_prefilter', 'custom_upload_filter' );
     
    function custom_upload_filter( $file ) {
    
        // 1. GET SOURCE FIELD, FROM MEDIA.PHP
    
        $field = false;
        // Search for field key within available data.
        // Case 1) Media modal query.
        if ( isset( $_POST['query']['_acfuploader'] ) ) {
            $field_key = (string) $_POST['query']['_acfuploader'];
    
            // Case 2) Media modal upload.
        } elseif ( isset( $_POST['_acfuploader'] ) ) {
            $field_key = (string) $_POST['_acfuploader'];
        }
    
        // Attempt to load field.
        // Note the <code>acf_get_field()</code> function will return false if not found.
        if ( isset( $field_key ) ) {
            $field = acf_get_field( $field_key );
        }
    
        // 2. GET USER'S ID AND USERNAME
     
    
        // TEST RENAME
        $file['name'] = $user_id . 'and-everything-is-awesome-' . $file['name'];
        return $file;
    
    }

    … it successfully gets the field key at that point, and therefore supports getting the field, too.

    So, let me think, what logic should I be using here… ?

    * If this is an ACF field with the specified key (since wp_handle_upload_prefilter will otherwise fire on every upload),
    * Get the User ID for the User in the current Edit page
    * Get User object and then Username slug
    * Use that to recompose the filename, but retain the original extension

    If so, I’m now hitting a non-ACF snag actually obtaining the current user from user-edit.php

    ie. the following and another method

        // 2. GET USER'S ID AND USERNAME
    
        // If is current user's profile (profile.php)
        if ( defined('IS_PROFILE_PAGE') && IS_PROFILE_PAGE ) {
            $user_id = get_current_user_id();
        // If is another user's profile page
        } elseif (! empty($_GET['user_id']) && is_numeric($_GET['user_id']) ) {
            $user_id = $_GET['user_id'];
        // Otherwise something is wrong.
        } else {
            die( 'No user id defined.' );
        }

    … provoke “An error occurred in the upload. Please try again later.” in the upload window… maybe because, the browser is still on the same page, user_id is not available at the point the “Select Image” modal has appeared?? Hmm…

  • Not sure I know how to use that.

    Do you fire it out of a first function like you did with the other pair of them, in the other thread? (per that thread, I’m already using upload_prefilter to change the upload directory for this Image field; I also want to rename the file).

    I modified my working code but it doesn’t work for the renaming…

    add_filter('acf/upload_prefilter/key=field_6140a6da30a17', 'prefilter_avatar_upload');
    function prefilter_avatar_upload($errors) {
        // This one added, but doesn't work, unsure what to do
        add_filter('wp_handle_upload_prefilter', 'avatar_upload_rename' );
        // in this filter we add a WP filter that alters the upload path
        add_filter('upload_dir', 'modify_avatar_upload_dir');
        return $errors;
    }
    
        // (Old) second filter
        function modify_avatar_upload_dir($uploads_avatars) {
            // here is where we later the path
            $uploads_avatars['path'] = $uploads_avatars['basedir'].'/users/avatars';
            $uploads_avatars['url'] = $uploads_avatars['baseurl'].'/users/avatars';
            // $uploads_avatars['subdir'] = 'avatars';
            return $uploads_avatars;
        }
    
        // + Rename the file for saving
        function avatar_upload_rename( $file ) {
            $file['name'] = 'test-prefix-' . $file['name'];
            return $file;
        }
  • I found some information about altering the upload file name

    https://developer.wordpress.org/reference/hooks/wp_handle_upload_prefilter/

    Thanks. I wonder how I would ensure that only applies to the ACF field in question, rather than filters ALL WordPress file uploads?

    (FYI – It’s on user-edit.php. I’d like to rename the file being uploaded through an Image field – specifically, by getting the ID and then username of the user currently being edited… which is proving to be the next challenge).

    Given that that I already have ACF filters going to take the Image and change the destination directory (as per the other thread), I’m not certain how it should fit together.

  • Three more things:

    +1)
    I see from the doc, when a key is passed, the prefilter should take parameter “key=”, not “name=” – so I’ve changed this.

    +2)
    Even so, looking in debug.log, I see…
    [15-Sep-2021 12:11:20 UTC] PHP Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'field_name_upload_prefilter' not found or invalid function name in /Users/MyName/Sites/sitename/wp-includes/class-wp-hook.php on line 305

    +3)
    When I amend to correct the underscore typo (field_nameupload_prefilter to field_name_upload_prefilter, the warning is solved – the file successfully uploads… to /uploads/companies – but not /uploads/companies/logos. Ergo, I may be misunderstanding the role of subdir for wp_upload_dir.

    So, the following works nicely…

    add_filter('acf/upload_prefilter/key=field_6140aa779827d', 'field_name_upload_prefilter');
    function field_name_upload_prefilter($errors) {
      // in this filter we add a WP filter that alters the upload path
      add_filter('upload_dir', 'field_name_upload_dir');
      return $errors;
    }
    
    // second filter
    function field_name_upload_dir($uploads) {
      // here is where we later the path
      $uploads['path'] = $uploads['basedir'].'/companies/logos';
      $uploads['url'] = $uploads['baseurl'].'/companies/logos';
      // $uploads['subdir'] = 'logos';
      return $uploads;
    }

    … I commented-out subdir and added the full path to basedir and baseurl. Is this acceptable?

  • I can’t get this to direct images to a folder…

    add_filter('acf/upload_prefilter/name=field_6140aa779827d', 'field_name_upload_prefilter');
    function field_nameupload_prefilter($errors) {
      echo '<pre>Hello world 1</pre>';
      // in this filter we add a WP filter that alters the upload path
      add_filter('upload_dir', 'field_name_upload_dir');
      echo '<pre>Hello world 2</pre>';
      return $errors;
    }
    
    // second filter
    function field_name_upload_dir($uploads) {
      // here is where we later the path
      $uploads['path'] = $uploads['basedir'].'/companies';
      $uploads['url'] = $uploads['baseurl'].'/companies';
      $uploads['subdir'] = 'logos';
      echo '<pre>Hello world 4</pre>';
      return $uploads;
    }

    Field type is Image, name “Company Logo” (“companylogo”, “field_6140aa779827d”).
    Aim is to upload a company logo but siphon it to a child of “uploads” – “uploads/companies/logos“.
    But it continues going to “uploads/2021/09“.

    None of my “Hello world” test lines are displayed, which may suggest the filter isn’t firing at all (?).

    Any thoughts?

  • acf/upload_prefilter/name=my_acf_upload_fieldname’ change my_acf_upload_fieldname to the name of your ACF field.

    For my_acf_upload_fieldname, how would you reference an Image field that is inside a Group? Does it matter?

  • Oh, this looks to have been a feature of ACF Extended, which even wraps native WP User field areas in metaboxes

Viewing 25 posts - 1 through 25 (of 107 total)