feat: reprocess existing .elpx media attachments (#42)#43
Merged
Conversation
Add a safe way to (re)process .elpx attachments that already live in the Media Library but were never extracted — e.g. uploaded before the plugin was active, or stored through a flow such as Formidable Forms that bypasses the upload handler. Previously such attachments had no extraction directory or eXeLearning metadata, so the [exelearning] shortcode fell back to a download link instead of rendering the preview iframe. - Add ExeLearning_Reprocessor: a single home for the validate -> extract -> commit -> cleanup primitives, with reprocess(), needs_reprocessing() and candidate-query helpers. Extracts to a fresh directory before touching any metadata and removes the previous extraction only on success, so it is idempotent and leaves existing content intact on failure. - Refactor the REST API save/create flows to delegate to the reprocessor instead of duplicating the extraction logic. - Add a "Reprocess eXeLearning file" bulk action to the Media Library plus an admin notice summarising the result. - Add a WP-CLI command: wp exelearning reprocess --id=<id> | --all | --force. - Expose the security .htaccess writer so reprocessed extraction roots are guarded even on sites that installed the plugin after files were uploaded. Tested via new ReprocessorTest and MediaLibraryReprocessTest covering every acceptance criterion (extraction + metadata, no-preview files, invalid files, idempotency, failure preserving the prior extraction, the shortcode rendering the iframe after reprocessing, and the candidate query). Closes #42
Contributor
Test in WordPress PlaygroundTest the plugin with the code from this branch:
|
…I from coverage - Regenerate exelearning.pot with the new reprocess feature strings. - Translate them into es_ES (fixes the CI "Check untranslated strings" gate) and into all other shipped locales: ca, ca_valencia, de_DE, eo, eu, gl_ES, it_IT, pt_PT and ro_RO (with its 3 plural forms). Regenerate every .mo. - Add MediaLibraryReprocessTest cases for render_reprocess_admin_notice (success, warning, and the two silent paths). - Exclude includes/class-cli-command.php from coverage: it only loads under WP-CLI, which is unavailable to PHPUnit (same rationale as the existing exclusions). Overall line coverage stays at 79.36%.
eXeLearning source projects are ZIP archives whose only reliable signature is an inner content.xml. A genuine project can land in the Media Library with a .zip extension (renamed, or stored by a flow such as Formidable Forms). The reprocessor previously rejected those purely by extension. Broaden only the reprocessor (bulk action, WP-CLI, single --id) to also accept .zip attachments, gated by content validation. The upload path, MIME registration and UI stay .elpx-only, so backup zips and the plugin's own web/SCORM/IMS exports (which have no content.xml) are never treated as eXeLearning. - Add ACCEPTED_EXTENSIONS (elpx, zip) and split detection into is_exelearning_candidate() (cheap extension check) and is_eligible() (content-validates .zip via ExeLearning_Elp_File_Service::validate_elp_file). - reprocess() accepts .elpx or .zip; extraction still validates content and returns a clear error for a non-eXeLearning archive without writing metadata. - needs_reprocessing()/scan queries match .elpx OR .zip and filter by is_eligible(), so plain archives are never auto-flagged. - Bulk handler skips ineligible items (incl. plain zips); CLI uses the renamed candidate query and eligibility guard. No new user-facing strings (reuse existing translated messages), so the i18n gate is unaffected. New ReprocessorTest/MediaLibraryReprocessTest cases cover valid vs plain .zip across reprocess, needs_reprocessing, the candidate query and the bulk action. Line coverage 79.26%.
…#42) Make the reprocessing capability discoverable where users actually click. In the default grid view, opening an unprocessed .elpx/.zip showed nothing eXeLearning and no way to act on it: the modal JS only ran when the prepared attachment data carried the `exelearning` blob, which exists only once a file is extracted. - REST: add POST /exelearning/v1/reprocess/{id} (permission check_edit_permission) that delegates to ExeLearning_Reprocessor::reprocess() and returns the save response (preview_url) or a clear WP_Error. Reuses the existing content gate, so a non-eXeLearning .zip is rejected without writing metadata. - Media library: expose `exelearningReprocessable` (unprocessed candidate the user may edit; cheap extension check, no per-item ZIP I/O while browsing) and localize a new `exelearningMediaSettings` object (REST root + wp_rest nonce) plus the new button strings. - Modal JS: render a "Process as eXeLearning" button + "not processed yet" hint for reprocessable attachments (both the single-column details and the two-column attachment-info actions), call the REST endpoint with X-WP-Nonce, and on success refresh the attachment so the preview renders. Errors are shown inline (no blocking dialogs). New strings translated in es_ES (CI gate) and all shipped locales; .pot/.mo regenerated. Tests cover the REST route/handler (success, invalid, non-candidate, permission) and the exelearningReprocessable flag. Verified end-to-end in the browser: clicking the button on a .zip eXeLearning project extracts it and the preview appears. Line coverage 79.51%.
…cept it Reprocessing a .zip made it previewable, but the editor and export-bootstrap paths are .elpx-only and aborted with "This file is not an eXeLearning file (.elpx)." once the now-processed file showed an Edit/Download affordance. Once extraction has confirmed the archive is a real eXeLearning project, rename the underlying .zip to the canonical .elpx (unique filename) and update the attachment via update_attached_file(). The file then behaves as a first-class eXeLearning source everywhere — preview, edit, export and save — with no changes to the existing .elpx extension guards. Embedding is by attachment ID, so shortcodes keep working; the move is non-fatal (the preview still works if it fails). Covered by ReprocessorTest::test_reprocess_renames_valid_zip_to_elpx and verified end-to-end via WP-CLI (a reprocessed .zip becomes .elpx and is editor-accepted). Line coverage 79.55%.
CI's WPCS flagged two issues the local container's phpcs missed: - Generic.Commenting.DocComment.LongNotCapital: the doc long description started with the lowercase brand 'eXeLearning'; reworded to start with a capital. - Use the correct ignore code WordPress.WP.AlternativeFunctions.rename_rename (not file_system_operations_rename) for the in-place rename().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #42.
Adds a safe, reusable way to (re)process
.elpxattachments that already live in the Media Library but were never extracted — for example uploaded before the plugin was active, or stored through a flow such as Formidable Forms that bypasses the upload handler. In that state the attachment has no extraction directory and no eXeLearning metadata, so[exelearning id="123"]falls back to a download link instead of rendering the preview iframe.What changed
ExeLearning_Reprocessor(new) — single home for thevalidate → extract → commit → cleanupprimitives, plusreprocess(),needs_reprocessing(),is_elpx_attachment()and the candidate-query helpers. It extracts to a fresh directory before touching any metadata and removes the previous extraction only after the new one is committed, so it is idempotent and leaves existing content intact on failure.upload.php, with an admin notice summarising reprocessed / skipped / failed counts.wp exelearning reprocess --id=<id> | --all | --all --force..htaccess— exposed as a reusable static writer so reprocessed extraction roots are guarded even on sites that installed the plugin after files were uploaded.Acceptance criteria
.elpxattachments without_exelearning_extractedcan be processed after activationwp-content/uploads/exelearning/<hash>/_exelearning_extractedand_exelearning_has_previewconsistently[exelearning id="ATTACHMENT_ID"]renders the preview iframe.elpxreturns a clear admin-facing errorTesting
ReprocessorTest(15 cases) andMediaLibraryReprocessTest(4 cases) cover every acceptance criterion, including the shortcode rendering the iframe after reprocessing and failure preserving the prior extraction.StaticEditorInstallerTest::test_is_editor_installed_returns_false_when_missing) is pre-existing and unrelated — it fails because a localdist/static/build is present, independent of this change.PHPCSclean on all new/modified files.wp help exelearning reprocessand a real run).