This is a very common issue with programmatic BuddyPress updates.
Why it is failing:
Strict Folder Structure: BuddyPress doesn't just "know" an image exists. It looks for very specific filenames (like bp-full.jpg and bp-thumb.jpg) inside very specific folders (usually /wp-content/uploads/avatars/USER_ID/).
Caching: BuddyPress caches avatar URLs aggressively. Even if the file is uploaded, BP might still be serving the old cached URL.
The "Silent" Error: Your AJAX returns "success" because the file did upload to your /tilpimages/ folder, but the logic to copy it to the BuddyPress folder likely failed silently.
Here is the re-written, robust PHP handler for functions.php.
What this new code does:
Saves to your custom /tilpimages/ folder (as requested).
Brute Forces BuddyPress: It manually creates the required BuddyPress folders, resizes the images to the exact dimensions BP expects, and names them bp-full.jpg and bp-thumb.jpg.
Updates the Database: It forces an update to the _bp_cover_image meta key.
Clears Cache: It runs commands to tell WordPress "forget the old image."
Step 1: Update functions.php
Delete the previous tilp_handle_image_upload and tilp_set_buddypress_... functions and replace them with this strict version:
PHP
add_action( 'wp_ajax_tilp_upload_image', 'tilp_handle_image_upload' );
function tilp_handle_image_upload() {
check_ajax_referer( 'tilp_upload_nonce', 'security' );
// 1. Basic Validation
if ( ! isset( $_FILES['image_file'] ) ) {
wp_send_json_error( 'No file uploaded' );
}
$user_id = get_current_user_id();
$type = sanitize_text_field( $_POST['image_type'] ); // 'avatar' or 'cover'
$file = $_FILES['image_file'];
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
// 2. Save to your custom /tilpimages/ folder first (Source of Truth)
$upload_dir = wp_upload_dir();
$custom_dir_name = 'tilpimages';
$custom_path = $upload_dir['basedir'] . '/' . $custom_dir_name;
$custom_url = $upload_dir['baseurl'] . '/' . $custom_dir_name;
if ( ! file_exists( $custom_path ) ) {
wp_mkdir_p( $custom_path );
}
$ext = pathinfo( $file['name'], PATHINFO_EXTENSION );
$timestamp = time();
$new_filename = $timestamp . '_' . $user_id . '.' . $ext;
$master_file_path = $custom_path . '/' . $new_filename;
$master_file_url = $custom_url . '/' . $new_filename;
// Move uploaded file to /tilpimages/
if ( ! move_uploaded_file( $file['tmp_name'], $master_file_path ) ) {
wp_send_json_error( 'Failed to save to tilpimages folder.' );
}
// Compress if > 5MB
if ( filesize( $master_file_path ) > 5 * 1024 * 1024 ) {
$editor = wp_get_image_editor( $master_file_path );
if ( ! is_wp_error( $editor ) ) {
$editor->set_quality( 60 );
$editor->save( $master_file_path );
}
}
// 3. PROCESS FOR BUDDYPRESS
$bp_success = false;
if ( $type === 'avatar' ) {
$bp_success = tilp_force_bp_avatar( $master_file_path, $user_id );
} elseif ( $type === 'cover' ) {
$bp_success = tilp_force_bp_cover( $master_file_path, $user_id, $master_file_url );
}
// 4. Return Result
if ( $bp_success ) {
// Clear User Cache to ensure UI updates immediately
clean_user_cache( $user_id );
wp_cache_delete( $user_id, 'bp_last_activity' );
wp_send_json_success( array(
'url' => $master_file_url,
'message' => 'Image updated successfully'
));
} else {
wp_send_json_error( 'Saved to tilpimages, but failed to update BuddyPress profile.' );
}
}
/**
* STRICT AVATAR HANDLER
* Manually places bp-full.jpg and bp-thumb.jpg in the avatar folder.
*/
function tilp_force_bp_avatar( $source_file, $user_id ) {
// Define standard BP Avatar path: /wp-content/uploads/avatars/USER_ID/
$bp_upload_path = bp_core_avatar_upload_path();
$user_avatar_dir = $bp_upload_path . '/' . $user_id;
// Delete existing avatars first to prevent conflicts
if ( file_exists( $user_avatar_dir ) ) {
array_map( 'unlink', glob( "$user_avatar_dir/*.*" ) );
} else {
wp_mkdir_p( $user_avatar_dir );
}
$editor = wp_get_image_editor( $source_file );
if ( is_wp_error( $editor ) ) return false;
// 1. Create Full Size (bp-full.jpg) - Usually 150x150 or defined by theme
$editor->resize( BP_AVATAR_FULL_WIDTH, BP_AVATAR_FULL_HEIGHT, true );
$full_save = $editor->save( $user_avatar_dir . '/bp-full.jpg' );
// 2. Create Thumb Size (bp-thumb.jpg)
$editor->resize( BP_AVATAR_THUMB_WIDTH, BP_AVATAR_THUMB_HEIGHT, true );
$thumb_save = $editor->save( $user_avatar_dir . '/bp-thumb.jpg' );
if ( ! is_wp_error( $full_save ) ) {
return true;
}
return false;
}
/**
* STRICT COVER IMAGE HANDLER
* Manually places the image in the BP cover-image folder and updates User Meta.
*/
function tilp_force_bp_cover( $source_file, $user_id, $public_url ) {
// 1. Determine Cover Directory: /wp-content/uploads/buddypress/members/USER_ID/cover-image/
$bp = buddypress();
$upload_dir = bp_upload_dir();
$cover_dir = $upload_dir['basedir'] . '/members/' . $user_id . '/cover-image';
if ( ! file_exists( $cover_dir ) ) {
wp_mkdir_p( $cover_dir );
}
// 2. Define the new filename (BuddyPress usually generates a random name)
$filename = wp_basename( $source_file );
$destination = $cover_dir . '/' . $filename;
// 3. Copy the file
if ( ! copy( $source_file, $destination ) ) {
return false;
}
// 4. VITAL: Update the User Meta so BP knows the file is there
// This is often why the image doesn't show up—the physical file exists, but the DB doesn't know.
update_user_meta( $user_id, 'bp_cover_image', array( 'file' => $filename, 'align' => 'top' ) );
// 5. Update Custom Profile Manager meta just in case your theme uses a custom key
// Since you use Tilp_Profile_Manager, it might look for this:
update_user_meta( $user_id, 'tilp_cover_image', $public_url );
return true;
}
Step 2: Update the JavaScript (Template File)
There is a browser caching issue where the browser sees the old image URL and doesn't bother re-fetching it, even if the file changed. We need to "bust" the cache aggressively in the JavaScript.
Find this section in your template script and update the success block:
JavaScript
success: function(response) {
if(response.success) {
showStatus('Image updated successfully!', 'success');
// Cache Buster: Add a random number to the end of the URL
var uniqueURL = response.data.url + '?t=' + new Date().getTime();
// Update the Preview immediately
$(previewID).css('background-image', 'url(' + uniqueURL + ')');
// ALSO: Try to update any other images on the page (like the sidebar avatar)
// This targets the standard BP avatar classes
if(type === 'avatar') {
$('.avatar, .ut-prof-avatar').attr('src', uniqueURL);
$('.ut-set-user-label img').attr('src', uniqueURL); // Update sidebar if it has img
}
} else {
showStatus('Error: ' + response.data, 'error');
}
},
Step 3: Debugging (If it still fails)
If this still doesn't work, we need to see the server error log.
Open your wp-config.php file via FTP/File Manager.
Change define( 'WP_DEBUG', false ); to:
PHP
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Add this line inside the tilp_handle_image_upload function in functions.php right after $file = $_FILES['image_file'];:
PHP
error_log( 'Starting upload for User ' . $user_id . ' Type: ' . $type );
Try the upload again.
Check the file /wp-content/debug.log. It will tell you if there are "Permission Denied" errors when trying to write to the folders.
Why the Tilp_Profile_Manager matters
In your template, you have: $cover_image = $profile_manager->get_cover_image($user_id);.
If the class Tilp_Profile_Manager is coded to strictly look for a custom meta key (and ignores standard BuddyPress), standard BP updates won't work.
I added Step 4 in the tilp_force_bp_cover function above to attempt to fix this:
PHP
update_user_meta( $user_id, 'tilp_cover_image', $public_url );
If your specific theme uses a different meta key (like _listing_cover_image or similar), you need to find that key name in your database (table wp_usermeta) and update the code to match it.
Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/tilp.me/httpdocs/wp-content/themes/tilp-theme/inc/class-profilesettings.php:1) in /var/www/vhosts/tilp.me/httpdocs/wp-includes/sitemaps/class-wp-sitemaps-renderer.php on line 126
https://tilp.me/wp-sitemap-posts-post-1.xmlhttps://tilp.me/wp-sitemap-posts-page-1.xmlhttps://tilp.me/wp-sitemap-taxonomies-category-1.xmlhttps://tilp.me/wp-sitemap-users-1.xml