Skip to content

Order Again file uploads can lose access after rename() from ppom_files #655

Description

@pirate-bot

Summary

PPOM file and cropper uploads can become inaccessible when a later order reuses a previously uploaded file reference, such as through WooCommerce Order Again.

Expected behavior: each order that references an uploaded file retains access to its own downloadable file.

Actual behavior: one order can claim the shared uploaded file during checkout or when the file link/thumbnail is rendered, while another order can be left with a dead file link.

Impact: affected stores may see missing or incorrect customer-uploaded files on order records, with potential loss of access to files needed for fulfillment.

Customer context

  • Product / area: PPOM Pro customer report, but inspected defect is in PPOM free core upload/order handling.
  • Version: Customer-installed version not provided. Local inspected free repo is v34.0.4.
  • Environment: WordPress/WooCommerce store; exact WordPress, WooCommerce, PHP, and storage environment not provided.
  • Integration / third party: WooCommerce Order Again flow.
  • Reported error / symptom: Reused uploaded images show thumbnails or order references, but download links become dead or appear to point to another order's file.
  • Impact: Reported loss of access to customer-uploaded files between orders.

Reproduction notes

Reported workflow:

  1. Place an order with a PPOM file or cropper upload.
  2. Use WooCommerce Order Again or otherwise reuse the prior order's uploaded file reference.
  3. Complete the new order or open an order screen that renders the file link/thumbnail.
  4. Observe one order retaining the moved file while another order's link can become empty/dead.

Local runtime reproduction was not performed. Source inspection confirms the necessary code path for this workflow: metadata reuse plus rename() from a shared base upload pool.

Diagnosis

Conclusion

Confirmed product bug in PPOM free core. The inspected code stores filename references in order metadata, rehydrates the same references during Order Again, and later moves files out of the shared ppom_files pool into an order-specific folder. When two orders legitimately reference the same shared source filename, the first checkout/display path that reaches the move can remove the shared source, leaving the other order's metadata pointing to a file path that may not exist.

Where this likely occurs

  • User-visible surface: order item file/cropper display links and thumbnails in WooCommerce order views, plus checkout finalization for PPOM file and cropper fields.
  • src/Files/Handler.phpHandler::files_setup_get_directory() lines 17-34: creates/returns the shared upload directory under PPOM_UPLOAD_DIR_NAME, with no order-specific source directory at initial storage time in this path.
  • src/WooCommerce/Order/OrderHandler.phpOrderHandler::order_item_meta() lines 40-77: persists the PPOM payload on the line item as _ppom_fields, preserving filename data for later display/replay.
  • src/WooCommerce/Order/OrderHandler.phpOrderHandler::wc_order_again_compatibility() lines 338-345: copies _ppom_fields back into $cart_item_data['ppom'] when WooCommerce reorders an item. This preserves the same file reference rather than creating an independent file record.
  • src/Cart/WooCommerceCartLifecycleHooks.phpWooCommerceCartLifecycleHooks::register() line 82 and inc/woocommerce/order.phpppom_woocommerce_rename_files() lines 29-30: register and delegate the checkout file finalization path.
  • src/WooCommerce/Order/OrderHandler.phpOrderHandler::rename_files() lines 257-325: for file and cropper fields, computes ppom_files/{filename} and ppom_files/confirmed/{order_id}/{product_id}-{filename}; lines 285-290 move the base source with rename() only if it exists, and execution continues without recording a failure when it does not.
  • src/Files/Handler.phpHandler::get_file_download_url() lines 503-528: when resolving a file URL, lines 516-520 move the base source file into confirmed/{order_id}/ with rename() as a display-time side effect. If the base file is gone and no matching confirmed/edit path exists for that order, the function returns an empty URL.
  • src/WooCommerce/Order/OrderHandler.phpOrderHandler::order_value() lines 121-147 and src/Support/Helpers.phpHelpers::generate_html_for_files() lines 1805-1829: order item display for file and cropper fields calls Handler::get_file_download_url() while rendering the link/thumbnail.
  • src/Files/Handler.phpHandler::file_get_name() lines 773-779: final filenames are prefixed with product ID only for the inspected default path.
  • ppom-pro sibling inspection: inc/Core.phpCore::__construct() lines 64-69 and Core::order_again() lines 932-942 also rehydrate _ppom_fields into cart item metadata at priority 99, and no Pro override of the free PPOM\Files\Handler or PPOM\WooCommerce\Order\OrderHandler paths was found during inspection.
  • Git evidence: current namespaced implementations for Handler::get_file_download_url() and OrderHandler::rename_files() are attributed by git blame to commit a8f17ea5 (dev: refactor code (#565), 2026-04-15). A precise original version boundary for the behavior was not established; current HEAD is tagged v34.0.4.

Engineering notes

  • The failure path depends on metadata reuse plus a move operation. The inspected Order Again code copies the saved _ppom_fields structure, including file references, into a fresh cart item.
  • The display path is mutating state: rendering file/cropper order meta can move a shared source file into the currently rendered order's confirmed folder.
  • The checkout finalization path also records file metadata in $moved_files after the missing-source branch, because a nonexistent source only skips the rename() block and does not alter the later metadata append.
  • Pro was inspected because the thread came through PPOM Pro. The relevant free handlers appear to remain the active runtime paths; Pro adds another order-again rehydration hook but not a replacement for the free file move/download logic in the inspected code.
  • The report also mentions Pro FancyCropper/Texter generated filenames. That is related context but separate from the confirmed free-core path here; this triage only confirms the standard free file/cropper handling path and the Pro order-again rehydration boundary.
  • WordPress and WooCommerce internals were not inspected locally. The conclusion does not depend on unverified internals beyond WooCommerce invoking the documented hooks that this plugin registers.

Test coverage status

  • Relevant free tests found: tests/unit/test-file-helpers.phpTest_File_Helpers::testGetFileDownloadUrlMovesBaseFileToConfirmedDirectoryWithProductPrefix() lines 17-41 currently verifies that resolving a download URL removes the base file and creates the confirmed file; testGetFileDownloadUrlReturnsExistingConfirmedUrl() lines 90-107 covers already-confirmed files.
  • Relevant free tests found: tests/unit/src/WooCommerce/test-order-handler.phpOrderHandler order-again tests lines 112-153 verify _ppom_fields copying and non-array handling.
  • No direct free test for OrderHandler::rename_files() was found during inspection.
  • No relevant coverage was found during inspection for two orders sharing the same file reference, missing-source behavior after a prior move, or display-time movement causing another order's file URL to become empty.
  • Pro tests include order-again coverage around _ppom_fields, but no Pro override or test covering the free file mover path was found during inspection.

What to verify or explore next

  • May be worth reproducing on a local WooCommerce checkout with a PPOM file field: create an order with an uploaded file, use Order Again, then compare file links after checkout and after opening each order screen.
  • May be worth verifying both file and cropper fields because both branches are included in OrderHandler::rename_files() and OrderHandler::order_value().
  • May be worth running the focused PHPUnit files after a reproduction test exists: tests/unit/test-file-helpers.php and tests/unit/src/WooCommerce/test-order-handler.php.
  • If HPOS or offloaded uploads are involved in customer environments, compatibility behavior remains unverified from the current evidence.

Unknowns / follow-up

  • Customer's exact PPOM free, PPOM Pro, WooCommerce, WordPress, PHP, filesystem, and HPOS settings were not provided.
  • The attached text file was not accessed because external/customer-hosted attachment links are outside the evidence boundary for this triage.
  • The exact frequency of filename reuse outside WooCommerce Order Again was not measured. The confirmed path covers reused filename references carried through _ppom_fields.

Confidence

Confidence: 91/100

Repository inspection confirms the reported PPOM free code paths: saved order-again metadata can reuse the same uploaded filename while checkout and display flows move the shared source file with rename(), leaving later references unresolved when the source has already been moved.


Source: HelpScout #3372853954
Generated by bug-report-triage (ID: bug-report-triage_6a44bb06e76422.32738165)

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions