[Feature:Forum] Allow PDF attachments#12636
Conversation
There was a problem hiding this comment.
Pull request overview
Enables discussion forum attachments to include PDFs (in addition to images) while preserving existing limits and adding UI + rendering support for PDF previews.
Changes:
- Updated client/server validation to accept either images or PDFs for forum attachments.
- Added PDF-specific rendering paths (thumbnail indicator in edit UI; iframe preview + “Open PDF” in thread/history views).
- Added/updated unit + e2e tests for valid PDF acceptance and invalid file rejection.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/test_forum.py | Adds an e2e test covering PDF upload + invalid file rejection + PDF preview rendering. |
| site/tests/app/libraries/UtilsTester.php | Adds unit tests for new server-side “image-or-PDF” upload validation. |
| site/public/js/forum.js | Extends attachment validation to PDFs; adds PDF preview rendering and updates open behavior. |
| site/public/css/forum.css | Adds styles for PDF thumbnails and inline PDF preview layout. |
| site/app/templates/forum/ThreadPostForm.twig | Updates upload UI label and accept attribute to allow PDFs. |
| site/app/templates/forum/RenderPost.twig | Updates history rendering to show PDFs with an iframe preview + open link. |
| site/app/templates/forum/EditImgTable.twig | Updates edit/history attachment table to support PDF tiles and “Attached Files” wording. |
| site/app/templates/forum/CreatePost.twig | Escapes encoded attachment JSON for safe inclusion in an HTML attribute. |
| site/app/libraries/Utils.php | Adds content-based PDF validation helpers and a new combined image-or-PDF validator. |
| site/app/libraries/ForumUtils.php | Annotates attachment type (image vs pdf) into encoded attachment payload used by the frontend. |
| site/app/controllers/forum/ForumController.php | Updates controller-side validation/error messaging to allow PDFs as valid attachments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7ba192f to
1a14c3f
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #12636 +/- ##
============================================
+ Coverage 21.64% 21.66% +0.02%
- Complexity 9654 9668 +14
============================================
Files 268 268
Lines 36244 36258 +14
Branches 487 487
============================================
+ Hits 7845 7857 +12
- Misses 27916 27918 +2
Partials 483 483
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
Please address failing CIs. |
Rkoester47
left a comment
There was a problem hiding this comment.
Thank you for your contribution. I noticed an issue when testing the changes on this PR. When I try to delete an attached image or PDF, it works correctly. However, if there are multiple images/PDFs attached to a comment, it is impossible to delete the attachments. An error displays when attempting to update a post with a delete of one or more attachments on a post with multiple attachments.
1a14c3f to
39177ca
Compare
|
Thanks for testing this, @Rkoester47! I tried to reproduce the issue locally but wasn’t able to trigger it. I tested:
All of these worked on my end. Could you share the exact steps you followed and your browser/environment? Also, if possible, any console/network errors when the update fails would be really helpful since the UI only shows a generic “Post update failed” message. I’ll keep looking into it in parallel 👍 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function isValidForumAttachment(file) { | ||
| const mime_type = file.type || ''; | ||
| const lower_name = file.name.toLowerCase(); | ||
| return mime_type.startsWith('image/') || mime_type === 'application/pdf' || lower_name.endsWith('.pdf'); |
There was a problem hiding this comment.
Client-side validation currently treats any file with a .pdf filename suffix as valid, even when the browser reports a different MIME type (e.g., text/plain). Consider only falling back to the filename extension when file.type is empty/unknown, so the UI-side validation stays meaningful and aligns better with server-side content-based validation.
| return mime_type.startsWith('image/') || mime_type === 'application/pdf' || lower_name.endsWith('.pdf'); | |
| if (!mime_type) { | |
| // Fallback to filename extension only when MIME type is empty/unknown | |
| return lower_name.endsWith('.pdf'); | |
| } | |
| return mime_type.startsWith('image/') || mime_type === 'application/pdf'; |
| // Forum attachments are validated before storage, so any non-image attachment here is treated as a PDF. | ||
| $mime_type = mime_content_type($file['path']); | ||
| $attachment_type = is_string($mime_type) && str_starts_with($mime_type, 'image/') && getimagesize($file['path']) !== false ? 'image' : 'pdf'; |
There was a problem hiding this comment.
$attachment_type defaults to 'pdf' for anything that isn’t a valid image. For defense-in-depth, it would be safer to explicitly detect PDFs (e.g., via Utils::isValidUploadedPdf) and treat any unexpected/unknown MIME type as an unsupported attachment type (render as a download link, or skip preview) rather than embedding it in an <iframe>. Also consider reusing Utils::isValidUploadedImage() here to avoid duplicating the image-validation logic.
| // Forum attachments are validated before storage, so any non-image attachment here is treated as a PDF. | |
| $mime_type = mime_content_type($file['path']); | |
| $attachment_type = is_string($mime_type) && str_starts_with($mime_type, 'image/') && getimagesize($file['path']) !== false ? 'image' : 'pdf'; | |
| // Forum attachments are validated before storage, but we still explicitly | |
| // distinguish between images, PDFs, and unsupported types for defense-in-depth. | |
| if (Utils::isValidUploadedImage($file['path'])) { | |
| $attachment_type = 'image'; | |
| } | |
| elseif (Utils::isValidUploadedPdf($file['path'])) { | |
| $attachment_type = 'pdf'; | |
| } | |
| else { | |
| $attachment_type = 'unsupported'; | |
| } |
| $attachment_check = Utils::checkUploadedImageOrPdfFile($file_post) ? 1 : 0; | ||
| if ($attachment_check === 0 && !empty($_FILES[$file_post]['tmp_name'])) { | ||
| return $this->returnUserContentToPage("Invalid file type. Please upload only image or PDF files.", $isThread, $thread_id); | ||
| } | ||
| return [$imageCheck]; | ||
| return [$attachment_check]; |
There was a problem hiding this comment.
checkGoodAttachment() only special-cases UPLOAD_ERR_NO_FILE and otherwise relies on MIME checks. If PHP sets other upload errors (e.g., size limits), tmp_name can be empty/nonexistent and the current validation path may still mark attachments as “good”, leading to missing files later when move_uploaded_file runs. Consider rejecting when any $_FILES[$file_post]['error'][i] !== UPLOAD_ERR_OK (and/or when tmp_name doesn’t exist) before returning success.
|
I pushed a follow-up commit to fix the JS lint issues and the PHPStan/type-hint errors. The remaining Cypress failure still appears to be in late_submission_warning_messages.spec.js, which looks unrelated to the forum PDF attachment changes. |
@Rkoester47 I also was unable to replicate this issue. What browser/OS are you using? |
There was a problem hiding this comment.
Great work here. Code looks good and functionality work great. If someone can replicate the issue that @Rkoester47 is experiencing then we should fix that but otherwise everything looks good! Side note for the PHP linting, it likely will not pass until you generate a new baseline with
php vendor/bin/phpstan analyze app public/index.php socket/index.php --generate-baseline --memory-limit 2G
from the site directory. Add the changed baseline.neon file to your commits and that should fix it.
|
Thanks! I regenerated the PHPStan baseline and pushed the updated file. |
I was able to investigate this further and reproduce the behavior. It seems the “Post update failed” message occurs when the edited post is submitted with an empty body, rather than being related to multiple attachments. Specifically:
In the screenshot shared above by @Rkoester47, the post body also appears to be empty, which would explain the failure. So this appears to be due to existing validation that prevents empty posts, rather than an issue with handling multiple attachments. I’ve attached a short screen recording demonstrating this behavior. Screen.Recording.2026-03-27.at.14.38.07.mov |
Farhanxikram
left a comment
There was a problem hiding this comment.
functionality review:
The PDF/image button works, and it's relatively intuitive to use. The same goes for actually viewing the documents/images on a post.
Rkoester47
left a comment
There was a problem hiding this comment.
@not-so-shubh Thank you for investigating this further. As I see it is the original functionality of the discussion forum causing this error to display, I can approve of this PR. Please ensure all checks are passing so that we will be able to merge once this PR is fully approved.
e0df932 to
7cf5866
Compare
|
@Rkoester47 @dagemcn @Farhanxikram I looked into the remaining CI failures. The Cypress failure is in |
## What is the current behavior? Discussion forum attachments currently allow valid image uploads, but reject PDF attachments. Closes Submitty#12202 ## What is the new behavior? This PR allows valid PDF attachments in discussion forum posts in addition to valid images. ## What changed? - allow forum attachments that are valid images or PDFs - validation is content-based and not extension-only - preserved existing image validation behavior - kept existing forum attachment limits unchanged - updated the forum upload UI to accept PDFs (`accept="image/*,.pdf"`) - added a separate PDF rendering path for forum attachments - kept existing inline image rendering behavior unchanged - updated edit/history views to correctly display PDF attachments - aligned attachment opening behavior with current forum behavior by using new-tab opening without popup-style window features - added unit tests for: - valid PDF accepted - invalid file rejected - existing image behavior still works - added focused e2e coverage for: - valid PDF upload - invalid file rejection - attachment visible on created post - PDF preview rendering ## Notes This implementation was informed by feedback from Submitty#12213: - attachment opening behavior was simplified to stay consistent with the current forum attachment UX - PDF handling was separated from image handling instead of forcing both through the same rendering path - existing attachment data flow was preserved to avoid breaking forum edit/history views - tests were strengthened to verify actual outcomes rather than timing-based assumptions ## Manual Testing - created a thread with a PDF attachment and verified the thread was created successfully - verified the PDF appears in the created post attachment section - verified PDF preview renders correctly - verified PDF open behavior uses a new tab - verified PDF appears correctly in the edit attachment table - verified remove/delete still works for PDF attachments - verified existing image upload, preview, and open behavior still works ## Screenshots ### Upload UI (PDF selectable)  ### PDF Preview on created post  ### Edit view showing PDF tile  --------- Co-authored-by: dagemcn <[email protected]> Co-authored-by: Barb Cutler <[email protected]>
What is the current behavior?
Discussion forum attachments currently allow valid image uploads, but reject PDF attachments.
Closes #12202
What is the new behavior?
This PR allows valid PDF attachments in discussion forum posts in addition to valid images.
What changed?
accept="image/*,.pdf")Notes
This implementation was informed by feedback from #12213:
Manual Testing
Screenshots
Upload UI (PDF selectable)
PDF Preview on created post
Edit view showing PDF tile