Skip to content

fix(media-protection): allow undoing a protected file#3189

Open
faisalahammad wants to merge 2 commits into
gocodebox:devfrom
faisalahammad:fix/3018-undo-protected-file
Open

fix(media-protection): allow undoing a protected file#3189
faisalahammad wants to merge 2 commits into
gocodebox:devfrom
faisalahammad:fix/3018-undo-protected-file

Conversation

@faisalahammad

@faisalahammad faisalahammad commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds ability to undo a protected media file by clearing the selected course or membership. The file moves back to its original public location, the protection metadata is removed, and the user sees a read-only note of the original location and a warning before unprotecting.

Fixes #3018

Changes

includes/class-llms-media-protector.php

Added:

  • ORIGINAL_FILE_META_KEY constant for storing the original public location.
  • remove_authorization_meta_from_media_post() to clear auth meta and per-user cache.
  • get_original_attached_file() to read the stored original path.

Why: Need a recorded original path before protection so we can restore it, and need a clean way to clear the auth meta and the cached is_authorized_to_view result for the current user.

includes/admin/class-llms-admin-media-protection-attachment-settings.php

Added: move_attachment_to_public_dir(), store_original_attached_file(), and files_are_identical() helper.

Changed:

  • move_attachment_to_protected_dir() now records the original _wp_attached_file before moving.
  • attachment_fields_to_save() routes an empty product value through to the unprotect method.
  • attachment_fields_to_edit() enables allow_clear on the select2, shows the original location note, and renders the unprotect warning container.

Why: Need a symmetric move-back method. The original path is recorded once so the unprotect method has a target. Conflict detection handles the case where a different file already exists at the original public location (returns WP_Error).

includes/class-llms-rest-fields.php

Changed: _llms_media_protection_product_id update_callback treats an empty value as a request to unprotect and returns WP_Error on failure.

Added: Read-only _llms_media_protection_original_file REST field exposing the original location.

Why: The block editor needs to know the original location to render the note, and the REST path needs to honor the same "clear to unprotect" semantics as the classic save path. Backward compatibility preserved by keeping the existing field as a scalar integer.

src/js/admin-media-protection-block-protect.js

Changed: data-allow_clear='true', send 0 when the select2 is cleared, display the original location and the unprotect warning, and wrap the change event listener with a cleanup so it does not leak across modal opens.

assets/js/llms-admin-media-protection-attachment-settings.js

Changed: Toggle the unprotect warning visibility when the classic media modal select2 is cleared.

Testing

10 unit tests pass (19 assertions). PHPCS clean on all modified PHP files. CodeRabbit review addressed.

Test 1: Classic media library

  1. Edit an attachment, select a course or membership, save.
  2. Reopen the attachment. Original location note and unprotect warning container are shown. Select2 allows clearing.
  3. Clear the selection, save. File moves back to original public path, authorization meta is removed.

Test 2: Block editor

  1. Add a media block, click the lock toolbar button, pick a course, save.
  2. Reopen the modal. Currently protected by and original location labels render.
  3. Clear the selection, save. Block URL updates to the public URL.

Test 3: Conflict

  1. Re-upload the same file to the original public path before unprotecting. Unprotect removes the protected duplicate.
  2. Place a different file at the original public path. Unprotect aborts and returns WP_Error so the UI can show the conflict message.

Add ability to unprotect a media file by clearing the selected course or
membership. The file is moved back to its original public location and all
protection metadata is removed.

- Record original public location in new meta key when a file is first
  protected so it can be restored later.
- Add move_attachment_to_public_dir() mirroring the existing move to the
  protected dir, with conflict detection when a different file already
  exists at the original public path.
- Route cleared product value through to the unprotect method in the
  classic attachment save path and the REST update_callback.
- Enable clearing in the select2 in both classic and block editor UIs,
  show the original location, and warn the user before unprotecting.
- Add a read-only REST field exposing the original location to the
  block editor.

Fixes gocodebox#3018
@faisalahammad faisalahammad requested a review from brianhogg as a code owner June 16, 2026 12:54
@brianhogg brianhogg moved this to Awaiting Review in Development Jun 16, 2026
@faisalahammad

Copy link
Copy Markdown
Contributor Author

The PHP 8.3 job hit a transient network error (ECONNRESET at npm ci) during dependency download. All other PHP versions (7.4, 8.0, 8.1, 8.2) passed cleanly with zero test failures. Code is solid.

Maintainer can re-trigger the failed job when ready. I don't have the perms to do it from the fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Awaiting Review

Development

Successfully merging this pull request may close these issues.

2 participants