Skip to content

Conversation

arifulhoque7
Copy link
Contributor

@arifulhoque7 arifulhoque7 commented Aug 12, 2025

Close #1666

  • Remove duplicate file detection that was reusing existing media files

    • Add unique suffix (wpuf-timestamp-uniqueid) to all uploaded filenames
    • Prevent users from accidentally deleting other users' media files
    • Add wpuf_upload_file_name filter for filename customization
    • Ensure proper file ownership in multi-user environments

    This fixes a critical issue where users could unintentionally delete files
    uploaded by other users when the same filename and size were detected.
    Now each upload creates a unique file in the Media Library.

    Fixes issues reported in:

    PR/Issue Description

    Problem

    WP User Frontend was reusing existing media files when users uploaded files with the same name and size.
    This caused a critical issue where User B could unintentionally delete files uploaded by User A, breaking
    media across unrelated posts and products.

    Solution

    • Removed the duplicate file detection mechanism that was based on filename + size hash
    • Implemented automatic unique filename generation for all uploads
    • Added "wpuf" prefix along with timestamp and unique ID to prevent any conflicts
    • Ensured each upload creates a new media file with proper ownership

    Technical Changes

    • Modified includes/Ajax/Upload_Ajax.php:
      • Removed duplicate_upload() function call
      • Added wpuf_filename_unique() method
      • Removed wpuf_file_hash meta storage
      • Files now follow pattern: original-wpuf-timestamp-uniqueid.ext

    Impact

    • Prevents accidental deletion of other users' files
    • Ensures proper file ownership in WordPress Media Library
    • Eliminates broken media links in posts/products
    • Safe for WooCommerce product images and downloadable files

Summary by CodeRabbit

  • New Features

    • Uploads now receive automatically generated, unique, sanitized filenames based on the actual uploaded file, for both logged-in users and guests, reducing naming collisions.
  • Bug Fixes

    • Eliminates false “duplicate” detections and accidental reuse of existing files during upload.
    • Reduces overwrite/conflict errors and provides more consistent success/error outcomes for uploads.

@arifulhoque7 arifulhoque7 self-assigned this Aug 12, 2025
@arifulhoque7 arifulhoque7 added the needs: dev review This PR needs review by a developer label Aug 12, 2025
Copy link

coderabbitai bot commented Aug 12, 2025

Walkthrough

Removed duplicate-detection and file-hash logic from upload flow in includes/Ajax/Upload_Ajax.php; introduced a pre-upload filename renaming via a new private wpuf_filename_unique() that prefixes user/guest tokens and is filterable; file name now derived from the actual uploaded path; handle_upload now returns an array shape and docblocks updated.

Changes

Cohort / File(s) Summary of changes
Upload handling
includes/Ajax/Upload_Ajax.php
Deleted duplicate_upload() and wpuf_file_hash meta usage; added private wpuf_filename_unique($filename) which sanitizes, prefixes with user/guest token, and applies wpuf_upload_file_name filter; renamed incoming upload via wpuf_filename_unique() before processing; derive filename from uploaded file path (basename($uploaded_file['file'])); changed handle_upload return shape to structured array (['success'=>true,'attach_id'=>...] or ['error'=>...]); updated docblocks/params accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective (issue#) Addressed Explanation
Ensure unique filenames for uploaded media to prevent reuse and deletion conflicts (#1666)
Make unique-filename behavior configurable via a global setting (#1666) No new global setting or option added; only a filterable function was introduced.
Apply unique naming by default to featured image, product image, downloads, file uploads, galleries (#1666) The change enforces rename in this upload handler, but it's unclear if all listed upload entry-points (product/download-specific flows, galleries) call this handler; lack of explicit evidence in diff.

Poem

I’m a rabbit with a keyboard, hopping through each name,
I stamp them with a token so no two look the same.
No more borrowed pictures, no accidental lose,
I nibble at collisions and happily refuse.
Hooray — unique uploads! 🐰✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
includes/Ajax/Upload_Ajax.php (1)

63-72: Critical: Sanitize $_FILES data before use

The $_FILES['wpuf_file'] array is used directly without proper sanitization. While the comment says "sanitization ok", the static analysis correctly flags this as a security issue.

Apply this diff to properly sanitize the file upload data:

-$wpuf_file = isset( $_FILES['wpuf_file'] ) ? $_FILES['wpuf_file'] : []; // WPCS: sanitization ok.
-$file_name      = pathinfo( $wpuf_file['name'], PATHINFO_FILENAME );
-$file_extension = pathinfo( $wpuf_file['name'], PATHINFO_EXTENSION );
+$wpuf_file = isset( $_FILES['wpuf_file'] ) ? $_FILES['wpuf_file'] : [];
+$file_name      = pathinfo( sanitize_file_name( $wpuf_file['name'] ), PATHINFO_FILENAME );
+$file_extension = pathinfo( sanitize_file_name( $wpuf_file['name'] ), PATHINFO_EXTENSION );
🧹 Nitpick comments (6)
includes/Ajax/Upload_Ajax.php (6)

52-52: Use strict comparison for consistency

Line 52 uses loose comparison which can lead to unexpected type coercion.

Apply this diff to use strict comparison:

-if ( isset( $form_settings['require_login'] ) && $form_settings['require_login'] == 'false' ) {
+if ( isset( $form_settings['require_login'] ) && $form_settings['require_login'] === 'false' ) {

100-100: Use strict comparison for consistency

Line 100 uses loose comparison for checking image type.

Apply this diff to use strict comparison:

-if ( $image_type == 'link' ) {
+if ( $image_type === 'link' ) {

226-226: Use strict comparison for consistency

Line 226 uses loose comparison for checking the image caption option.

Apply this diff to use strict comparison:

-if ( wpuf_get_option( 'image_caption', 'wpuf_frontend_posting', 'off' ) == 'on' ) {
+if ( wpuf_get_option( 'image_caption', 'wpuf_frontend_posting', 'off' ) === 'on' ) {

271-271: Use strict comparison for consistency

Line 271 uses loose comparison for checking user ID.

Apply this diff to use strict comparison:

-if ( get_current_user_id() == absint( $attachment->post_author ) || current_user_can(
+if ( get_current_user_id() === absint( $attachment->post_author ) || current_user_can(

157-157: Remove trailing whitespace

Multiple lines have trailing whitespace that should be removed.

Remove trailing whitespace from lines 157, 300, 309, 312, and 315.

Also applies to: 300-300, 309-309, 312-312, 315-315


317-322: Consider documenting the filter parameters

The wpuf_upload_file_name filter provides good extensibility. Consider adding PHPDoc to document the filter parameters for developers.

Add PHPDoc comment above the filter:

+        /**
+         * Filter the unique filename before upload
+         *
+         * @since WPUF_SINCE
+         *
+         * @param string $new_filename The generated unique filename
+         * @param array  $context {
+         *     Context information for the filename generation
+         *
+         *     @type string $original_name Original filename
+         *     @type string $base_name     Base name without extension
+         *     @type string $extension     File extension with dot
+         *     @type string $unique_suffix Generated unique suffix
+         * }
+         */
         // Apply filter to allow customization of the unique filename
         $new_filename = apply_filters( 'wpuf_upload_file_name', $name . '-' . $unique_suffix . $ext, [
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ddcb1f and 22dbaf3.

📒 Files selected for processing (1)
  • includes/Ajax/Upload_Ajax.php (2 hunks)
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Ajax/Upload_Ajax.php

[failure] 157-157:
Whitespace found at end of line


[failure] 315-315:
Whitespace found at end of line


[failure] 312-312:
Whitespace found at end of line


[failure] 309-309:
Whitespace found at end of line


[failure] 300-300:
Whitespace found at end of line

🪛 GitHub Actions: Inspections
includes/Ajax/Upload_Ajax.php

[warning] 1-1: Step vendor/bin/phpcs includes/Ajax/Upload_Ajax.php -q --report=checkstyle | cs2pr --graceful-warnings reported PHPCS warning: Loose comparisons are not allowed. Expected: ===; Found: ==


[error] 1-1: PHPCS: Detected usage of a non-sanitized input variable: $_FILES['wpuf_file']

🔇 Additional comments (5)
includes/Ajax/Upload_Ajax.php (5)

74-76: Update return type documentation is inconsistent with actual behavior

The change from returning boolean/integer to an array containing success status and attach_id needs to be properly handled. However, the calling code correctly handles the new array structure.


155-157: Good implementation of unique filename generation

The addition of unique filename generation before upload prevents file deletion conflicts between users, which aligns perfectly with the PR objectives. This ensures each upload creates a new media file with proper ownership.


305-325: Well-designed unique filename generator with proper extensibility

The wpuf_filename_unique() method effectively prevents filename conflicts by:

  1. Sanitizing the base filename
  2. Adding a unique suffix with timestamp and uniqid
  3. Providing a filter hook for customization

This implementation successfully addresses the core issue of preventing accidental deletion of other users' files.


162-162: Filename derivation correctly uses uploaded file path

The change from using the input-provided name to the actual uploaded file path (basename( $uploaded_file['file'] )) is correct and ensures the filename reflects the actual stored file after unique naming.


174-183: Return structure properly implements array format

The updated return structure with success status and attach_id provides clearer error handling and is properly handled by the calling code in upload_file().

@arifulhoque7 arifulhoque7 requested a review from sapayth August 12, 2025 03:24
@Rubaiyat-E-Mohammad Rubaiyat-E-Mohammad added QA Approved This PR is approved by the QA team and removed needs: testing needs: dev review This PR needs review by a developer labels Aug 21, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
includes/Ajax/Upload_Ajax.php (1)

63-72: PHPCS error: unsanitized superglobal $_FILES — add an allowlist ignore with justification

Sanitizing file arrays is not appropriate; wp_handle_upload() performs the necessary validation and moves the file. Replace the WPCS note with a phpcs:ignore that targets the specific sniff and provides a justification.

-        $wpuf_file = isset( $_FILES['wpuf_file'] ) ? $_FILES['wpuf_file'] : []; // WPCS: sanitization ok.
+        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- File arrays are validated by wp_handle_upload(); manual sanitization could corrupt file metadata.
+        $wpuf_file = isset( $_FILES['wpuf_file'] ) ? $_FILES['wpuf_file'] : [];
🧹 Nitpick comments (5)
includes/Ajax/Upload_Ajax.php (5)

150-153: Tighten the handle_upload() docblock: specify shape of the return and input array

Documenting the contract here reduces guessing and helps static analysis.

- * @param array $upload_data file upload data
- *
- * @return array attachment result with success status and attach_id
+ * @param array{ name:string, type:string, tmp_name:string, error:int|string, size:int } $upload_data File upload data.
+ *
+ * @return array{ success:bool, attach_id?:int, error?:string } Attachment result.

52-58: Switch to strict comparisons to satisfy PHPCS and avoid type juggling

These are flagged in CI. Use === for string comparisons.

-            if ( isset( $form_settings['require_login'] ) && $form_settings['require_login'] == 'false' ) {
+            if ( isset( $form_settings['require_login'] ) && $form_settings['require_login'] === 'false' ) {
                 $guest_post = true;
             }
             //if it is registration form, let the user upload the file
-            if ( get_post_type( $form_id ) == 'wpuf_profile' ) {
+            if ( get_post_type( $form_id ) === 'wpuf_profile' ) {
                 $guest_post = true;
             }

100-104: Strict comparison for image type

-                if ( $image_type == 'link' ) {
+                if ( $image_type === 'link' ) {

226-227: Strict comparison for image_caption option

-        if ( wpuf_get_option( 'image_caption', 'wpuf_frontend_posting', 'off' ) == 'on' ) {
+        if ( wpuf_get_option( 'image_caption', 'wpuf_frontend_posting', 'off' ) === 'on' ) {

301-301: Remove trailing whitespace to fix PHPCS failures

PHPCS reports trailing whitespace on these lines. Please strip them (editor config or phpcbf).

Also applies to: 310-310, 313-313, 316-316, 157-157

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 22dbaf3 and de8e39f.

📒 Files selected for processing (1)
  • includes/Ajax/Upload_Ajax.php (2 hunks)
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Ajax/Upload_Ajax.php

[failure] 157-157:
Whitespace found at end of line


[failure] 316-316:
Whitespace found at end of line


[failure] 313-313:
Whitespace found at end of line


[failure] 310-310:
Whitespace found at end of line


[failure] 301-301:
Whitespace found at end of line

🪛 GitHub Actions: Inspections
includes/Ajax/Upload_Ajax.php

[warning] 1-1: PHPCS: Loose comparisons are not allowed. Expected: '==='; Found: '=='. (Command: vendor/bin/phpcs includes/Ajax/Upload_Ajax.php -q --report=checkstyle | cs2pr --graceful-warnings)


[warning] 1-1: PHPCS: Loose comparisons are not allowed. Expected: '==='; Found: '=='. (Command: vendor/bin/phpcs includes/Ajax/Upload_Ajax.php -q --report=checkstyle | cs2pr --graceful-warnings)


[error] 1-1: PHPCS: Detected usage of a non-sanitized input variable: $_FILES['wpuf_file']. (Command: vendor/bin/phpcs includes/Ajax/Upload_Ajax.php -q --report=checkstyle | cs2pr --graceful-warnings)

🔇 Additional comments (2)
includes/Ajax/Upload_Ajax.php (2)

162-164: LGTM: derive the stored name from the actual uploaded path

Using basename( $uploaded_file['file'] ) ensures the attachment title/type match the final, sanitized filename produced by wp_handle_upload(). Good call.


174-183: Safe to proceed with structured return type

Verified that handle_upload() is only invoked once within includes/Ajax/Upload_Ajax.php and that the caller already expects an array with a 'success' key:

  • includes/Ajax/Upload_Ajax.php:74 – $attach = $this->handle_upload( $upload );
    Checked immediately with if ( $attach['success'] ), consistent with the new return contract.

No other internal or external usages were found. You can resolve this change without updating additional call sites.

Comment on lines +155 to +157
// Always make filenames unique to prevent conflicts between users
$upload_data['name'] = $this->wpuf_filename_unique( $upload_data['name'] );

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Filename uniqueness strategy deviates from PR goal; make every upload globally unique with a deterministic suffix

The current approach prefixes the base name with a user token (u{user_id} or guest-...), then relies on WordPress’ -1, -2… behavior for repeat uploads by the same user. The PR objective and summary specify a suffix pattern like original-wpuf-timestamp-uniqueid.ext to guarantee uniqueness per upload and avoid any reuse scenarios. Also, using only the last 4 chars of uniqid() for guests weakens entropy and increases collision risk under load.

Recommend switching to a consistent suffix strategy for both logged-in users and guests:

  • Base name stays intact.
  • Append “-wpuf-{UTC-timestamp}-{strong-unique-id}”.
  • Keep the filter to allow overrides.

This aligns tightly with the objective, avoids reintroducing filename collisions, and makes troubleshooting easier (“wpuf” token is visible).

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 157-157:
Whitespace found at end of line

🤖 Prompt for AI Agents
In includes/Ajax/Upload_Ajax.php around lines 155-157, the current
wpuf_filename_unique strategy prefixes the basename with a user token and uses
fragile uniqid truncation for guests; change it to append a deterministic suffix
so every upload is globally unique: leave the original base name intact, append
"-wpuf-{UTC-timestamp}-{strong-unique-id}" before the extension for both
logged-in users and guests (use a stronger unique id source such as full
uniqid(true) or a hex from random_bytes for higher entropy), keep the existing
filter hook so callers can override the final filename, and ensure the result is
sanitized and respects filesystem length limits.

Comment on lines 296 to 343
/**
* Check if duplicate file
* Make filename unique by adding user ID prefix to prevent conflicts between users
* while still allowing WordPress to handle duplicates for the same user
*
* @param array $file
* @since WPUF_SINCE
*
* @param string $filename
*
* @return mixed
* @return string
*/
function duplicate_upload( $file ) {
global $wpdb;
$upload_hash = md5( $file['name'] . $file['size'] );

$match = $wpdb->get_var( $wpdb->prepare(
"SELECT post_id FROM $wpdb->postmeta m JOIN $wpdb->posts p ON p.ID = m.post_id WHERE m.meta_key = 'wpuf_file_hash' AND m.meta_value = %s AND p.post_status != 'trash' LIMIT 1;",
$upload_hash
) );
if ( $match ) {
$file['duplicate'] = $match;
private function wpuf_filename_unique( $filename ) {
$info = pathinfo( $filename );
$ext = empty( $info['extension'] ) ? '' : '.' . $info['extension'];
$name = basename( $filename, $ext );

// Sanitize the base name
$name = sanitize_file_name( $name );

// Get current user ID for user isolation
$user_id = get_current_user_id();

// For logged-in users, add user ID prefix to prevent cross-user conflicts
// For guests, add a session-based or timestamp prefix
if ( $user_id > 0 ) {
// Add user ID prefix to isolate files between users
// Format: u123-filename.ext (WordPress will handle duplicates as u123-filename-1.ext)
$unique_prefix = 'u' . $user_id;
} else {
// For guest uploads, use timestamp to ensure uniqueness
// This prevents guests from overwriting each other's files
$unique_prefix = 'guest-' . time() . '-' . substr( uniqid(), -4 );
}

return $file;

// Combine prefix with filename
// This ensures user isolation while preserving WordPress duplicate handling
$new_filename = $unique_prefix . '-' . $name . $ext;

// Apply filter to allow customization of the unique filename
$new_filename = apply_filters( 'wpuf_upload_file_name', $new_filename, [
'original_name' => $filename,
'base_name' => $name,
'extension' => $ext,
'user_id' => $user_id,
'unique_prefix' => $unique_prefix,
] );

return $new_filename;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Implement a robust, suffix-based naming scheme with higher entropy; document the filter and update @SInCE

Refactor wpuf_filename_unique() to:

  • Always append a “-wpuf-{UTC timestamp}-{unique id}” suffix to the sanitized base name (not a prefix).
  • Use wp_generate_uuid4() where available (fallback to a longer uniqid with entropy) to improve uniqueness.
  • Keep passing context to the filter, but rename the key from unique_prefix to unique_suffix (clearer and matches the new strategy).
  • Update the docblock to reflect the actual behavior and set a concrete @SInCE version.
  • Remove trailing whitespace flagged by PHPCS.

Proposed diff:

-    /**
-     * Make filename unique by adding user ID prefix to prevent conflicts between users
-     * while still allowing WordPress to handle duplicates for the same user
-     *
-     * @since WPUF_SINCE
-     * 
-     * @param string $filename
-     *
-     * @return string
-     */
-    private function wpuf_filename_unique( $filename ) {
-        $info = pathinfo( $filename );
-        $ext  = empty( $info['extension'] ) ? '' : '.' . $info['extension'];
-        $name = basename( $filename, $ext );
-        
-        // Sanitize the base name
-        $name = sanitize_file_name( $name );
-        
-        // Get current user ID for user isolation
-        $user_id = get_current_user_id();
-        
-        // For logged-in users, add user ID prefix to prevent cross-user conflicts
-        // For guests, add a session-based or timestamp prefix
-        if ( $user_id > 0 ) {
-            // Add user ID prefix to isolate files between users
-            // Format: u123-filename.ext (WordPress will handle duplicates as u123-filename-1.ext)
-            $unique_prefix = 'u' . $user_id;
-        } else {
-            // For guest uploads, use timestamp to ensure uniqueness
-            // This prevents guests from overwriting each other's files
-            $unique_prefix = 'guest-' . time() . '-' . substr( uniqid(), -4 );
-        }
-        
-        // Combine prefix with filename
-        // This ensures user isolation while preserving WordPress duplicate handling
-        $new_filename = $unique_prefix . '-' . $name . $ext;
-        
-        // Apply filter to allow customization of the unique filename
-        $new_filename = apply_filters( 'wpuf_upload_file_name', $new_filename, [
-            'original_name' => $filename,
-            'base_name'     => $name,
-            'extension'     => $ext,
-            'user_id'       => $user_id,
-            'unique_prefix' => $unique_prefix,
-        ] );
-        
-        return $new_filename;
-    }
+    /**
+     * Generate a globally-unique filename by appending a deterministic suffix.
+     *
+     * Pattern: {basename}-wpuf-{UTC YmdHis}-{uniqueid}{.ext}
+     *
+     * @since 3.7.10
+     *
+     * @param string $filename Original filename (may include extension).
+     * @return string Unique filename with suffix applied.
+     */
+    private function wpuf_filename_unique( $filename ) {
+        $info = pathinfo( $filename );
+        $ext  = empty( $info['extension'] ) ? '' : '.' . strtolower( $info['extension'] );
+        $name = sanitize_file_name( wp_basename( $filename, $ext ) );
+
+        // Always use UTC to avoid timezone-dependent strings
+        $timestamp = gmdate( 'YmdHis' );
+
+        // Strong unique id: prefer UUIDv4; fall back to high-entropy uniqid
+        if ( function_exists( 'wp_generate_uuid4' ) ) {
+            $unique_id = substr( wp_generate_uuid4(), 0, 8 );
+        } else {
+            $unique_id = substr( uniqid( '', true ), -10 );
+        }
+
+        $unique_suffix = 'wpuf-' . $timestamp . '-' . $unique_id;
+        $new_filename  = sprintf( '%s-%s%s', $name, $unique_suffix, $ext );
+
+        // Filter to allow customization of the filename strategy.
+        // Context keys intentionally documented for developers extending this behavior.
+        $new_filename = apply_filters(
+            'wpuf_upload_file_name',
+            $new_filename,
+            [
+                'original_name' => $filename,
+                'base_name'     => $name,
+                'extension'     => $ext,
+                'unique_suffix' => $unique_suffix,
+            ]
+        );
+
+        return $new_filename;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Check if duplicate file
* Make filename unique by adding user ID prefix to prevent conflicts between users
* while still allowing WordPress to handle duplicates for the same user
*
* @param array $file
* @since WPUF_SINCE
*
* @param string $filename
*
* @return mixed
* @return string
*/
function duplicate_upload( $file ) {
global $wpdb;
$upload_hash = md5( $file['name'] . $file['size'] );
$match = $wpdb->get_var( $wpdb->prepare(
"SELECT post_id FROM $wpdb->postmeta m JOIN $wpdb->posts p ON p.ID = m.post_id WHERE m.meta_key = 'wpuf_file_hash' AND m.meta_value = %s AND p.post_status != 'trash' LIMIT 1;",
$upload_hash
) );
if ( $match ) {
$file['duplicate'] = $match;
private function wpuf_filename_unique( $filename ) {
$info = pathinfo( $filename );
$ext = empty( $info['extension'] ) ? '' : '.' . $info['extension'];
$name = basename( $filename, $ext );
// Sanitize the base name
$name = sanitize_file_name( $name );
// Get current user ID for user isolation
$user_id = get_current_user_id();
// For logged-in users, add user ID prefix to prevent cross-user conflicts
// For guests, add a session-based or timestamp prefix
if ( $user_id > 0 ) {
// Add user ID prefix to isolate files between users
// Format: u123-filename.ext (WordPress will handle duplicates as u123-filename-1.ext)
$unique_prefix = 'u' . $user_id;
} else {
// For guest uploads, use timestamp to ensure uniqueness
// This prevents guests from overwriting each other's files
$unique_prefix = 'guest-' . time() . '-' . substr( uniqid(), -4 );
}
return $file;
// Combine prefix with filename
// This ensures user isolation while preserving WordPress duplicate handling
$new_filename = $unique_prefix . '-' . $name . $ext;
// Apply filter to allow customization of the unique filename
$new_filename = apply_filters( 'wpuf_upload_file_name', $new_filename, [
'original_name' => $filename,
'base_name' => $name,
'extension' => $ext,
'user_id' => $user_id,
'unique_prefix' => $unique_prefix,
] );
return $new_filename;
}
/**
* Generate a globally-unique filename by appending a deterministic suffix.
*
* Pattern: {basename}-wpuf-{UTC YmdHis}-{uniqueid}{.ext}
*
* @since 3.7.10
*
* @param string $filename Original filename (may include extension).
* @return string Unique filename with suffix applied.
*/
private function wpuf_filename_unique( $filename ) {
$info = pathinfo( $filename );
$ext = empty( $info['extension'] ) ? '' : '.' . strtolower( $info['extension'] );
$name = sanitize_file_name( wp_basename( $filename, $ext ) );
// Always use UTC to avoid timezone-dependent strings
$timestamp = gmdate( 'YmdHis' );
// Strong unique id: prefer UUIDv4; fall back to high-entropy uniqid
if ( function_exists( 'wp_generate_uuid4' ) ) {
$unique_id = substr( wp_generate_uuid4(), 0, 8 );
} else {
$unique_id = substr( uniqid( '', true ), -10 );
}
$unique_suffix = 'wpuf-' . $timestamp . '-' . $unique_id;
$new_filename = sprintf( '%s-%s%s', $name, $unique_suffix, $ext );
// Filter to allow customization of the filename strategy.
// Context keys intentionally documented for developers extending this behavior.
$new_filename = apply_filters(
'wpuf_upload_file_name',
$new_filename,
[
'original_name' => $filename,
'base_name' => $name,
'extension' => $ext,
'unique_suffix' => $unique_suffix,
]
);
return $new_filename;
}
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 316-316:
Whitespace found at end of line


[failure] 313-313:
Whitespace found at end of line


[failure] 310-310:
Whitespace found at end of line


[failure] 301-301:
Whitespace found at end of line

🤖 Prompt for AI Agents
In includes/Ajax/Upload_Ajax.php around lines 296 to 343, update
wpuf_filename_unique() to append (not prepend) a suffix of the form -wpuf-{UTC
timestamp}-{unique id} to the sanitized base name, use wp_generate_uuid4() when
available and otherwise fall back to a longer uniqid with entropy, rename the
context key passed to apply_filters from unique_prefix to unique_suffix
(containing the generated suffix), keep the rest of the context (original_name,
base_name, extension, user_id), update the method docblock to describe the
suffix-based behavior and set a concrete @since version, and remove any trailing
whitespace flagged by PHPCS; ensure the final returned filename is base + suffix
+ extension and that the filter allows overriding the full filename.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
QA Approved This PR is approved by the QA team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

File deletion conflict due to reused filenames (proposed fix)
2 participants