diff --git a/app/RSpade/Core/Files/File_Attachment_Controller.php b/app/RSpade/Core/Files/File_Attachment_Controller.php index ebe445cc3..a2a7fbe63 100644 --- a/app/RSpade/Core/Files/File_Attachment_Controller.php +++ b/app/RSpade/Core/Files/File_Attachment_Controller.php @@ -56,7 +56,7 @@ use App\RSpade\Core\Session\Session; * * GET /_download/:key - Download file as attachment * GET /_inline/:key - View file inline (browser display) - * GET /_thumbnail/:key/:type/:width/:height? - Generate static thumbnail (WebP) + * GET /_thumbnail/dynamic/:key/:type/:width/:height? - Generate dynamic thumbnail (WebP) * GET /_icon_by_extension/:extension - Get file type icon as PNG * * FILE RESPONSE PATTERN: @@ -561,7 +561,7 @@ class File_Attachment_Controller extends Rsx_Controller_Abstract /** * Generate dynamic thumbnail for image attachments * - * Route: /_thumbnail/:key/:type/:width/:height? + * Route: /_thumbnail/dynamic/:key/:type/:width/:height? * * Security: Checks file.thumbnail.authorize only * @@ -570,7 +570,7 @@ class File_Attachment_Controller extends Rsx_Controller_Abstract * @param int $width Thumbnail width in pixels * @param int $height Optional thumbnail height in pixels */ - #[Route('/_thumbnail/:key/:type/:width/:height?', methods: ['GET'])] + #[Route('/_thumbnail/dynamic/:key/:type/:width/:height?', methods: ['GET'])] #[Auth('Permission::anybody()')] public static function thumbnail(Request $request, array $params = []) { diff --git a/app/RSpade/Core/Files/File_Attachment_Model.php b/app/RSpade/Core/Files/File_Attachment_Model.php index 69380bf4d..cf9badb12 100644 --- a/app/RSpade/Core/Files/File_Attachment_Model.php +++ b/app/RSpade/Core/Files/File_Attachment_Model.php @@ -264,9 +264,9 @@ class File_Attachment_Model extends Rsx_Site_Model_Abstract public function get_thumbnail_url($type = 'fit', $width = 400, $height = null) { if ($height === null) { - return url("/_thumbnail/{$this->key}/{$type}/{$width}"); + return url("/_thumbnail/dynamic/{$this->key}/{$type}/{$width}"); } - return url("/_thumbnail/{$this->key}/{$type}/{$width}/{$height}"); + return url("/_thumbnail/dynamic/{$this->key}/{$type}/{$width}/{$height}"); } /** diff --git a/app/RSpade/Core/Files/THUMBNAILS.md b/app/RSpade/Core/Files/THUMBNAILS.md index 2eb8a8047..12a359c17 100755 --- a/app/RSpade/Core/Files/THUMBNAILS.md +++ b/app/RSpade/Core/Files/THUMBNAILS.md @@ -53,7 +53,7 @@ Edge case: Identical file content uploaded with different extensions (e.g., `doc ### Request Processing -**Route**: `/_thumbnail/:key/:type/:width/:height` (dynamic) or `/_thumbnail/preset/:key/:preset_name` (preset) +**Route**: `/_thumbnail/dynamic/:key/:type/:width/:height` (dynamic) or `/_thumbnail/preset/:key/:preset_name` (preset) **Common Flow** (via `generate_and_serve_thumbnail()`): 1. Validate authorization (`file.thumbnail.authorize` event) diff --git a/app/RSpade/man/file_upload.txt b/app/RSpade/man/file_upload.txt index d04ecc123..fe2ed3ba3 100755 --- a/app/RSpade/man/file_upload.txt +++ b/app/RSpade/man/file_upload.txt @@ -397,7 +397,7 @@ HTTP ENDPOINTS POST /_upload - Upload file GET /_download/:key - Download file GET /_inline/:key - View file inline - GET /_thumbnail/:key/:type/:width/:height? - Generate thumbnail + GET /_thumbnail/dynamic/:key/:type/:width/:height? - Generate dynamic thumbnail GET /_icon_by_extension/:extension - Get file type icon UPLOAD ENDPOINT @@ -594,9 +594,9 @@ DOWNLOAD AND INLINE VIEWING THUMBNAIL GENERATION - GET /_thumbnail/:key/:type/:width/:height? + GET /_thumbnail/dynamic/:key/:type/:width/:height? - Generate thumbnails for uploaded files with automatic fallback to file type icons. + Generate dynamic thumbnails for uploaded files with automatic fallback to file type icons. URL Parameters: key - 64-character attachment key diff --git a/app/RSpade/upstream_changes/thumbnail_url_routes_01_28.txt b/app/RSpade/upstream_changes/thumbnail_url_routes_01_28.txt new file mode 100755 index 000000000..fdbb24c66 --- /dev/null +++ b/app/RSpade/upstream_changes/thumbnail_url_routes_01_28.txt @@ -0,0 +1,69 @@ +Thumbnail URL Route Changes +Date: 2026-01-28 + +SUMMARY + Both thumbnail routes now include a literal path segment to prevent route + matching conflicts. If you manually construct thumbnail URLs (rather than + using get_thumbnail_url() or get_thumbnail_url_preset()), update them. + + - Preset: /_thumbnail/preset/{key}/{preset_name} (unchanged from earlier today) + - Dynamic: /_thumbnail/dynamic/{key}/{type}/{width}/{height} (NEW) + +AFFECTED FILES + /rsx/app/dev/attachments/dev_attachments.js + /rsx/theme/components/inputs/photo/profile_photo_input.js + + Any custom code manually constructing thumbnail URLs. + +CHANGES REQUIRED + + If you manually construct dynamic thumbnail URLs, add "dynamic" segment: + + Before: + `/_thumbnail/${key}/cover/300/200` + `/_thumbnail/${key}/fit/400` + + After: + `/_thumbnail/dynamic/${key}/cover/300/200` + `/_thumbnail/dynamic/${key}/fit/400` + + File: /rsx/app/dev/attachments/dev_attachments.js + ------------------------------------------------------------------------- + Find: + $('#thumb-profile').attr('src', `/_thumbnail/${key}/cover/96/96`); + $('#thumb-200').attr('src', `/_thumbnail/${key}/cover/200/200`); + $('#thumb-240x180').attr('src', `/_thumbnail/${key}/cover/240/180`); + + Replace with: + $('#thumb-profile').attr('src', `/_thumbnail/dynamic/${key}/cover/96/96`); + $('#thumb-200').attr('src', `/_thumbnail/dynamic/${key}/cover/200/200`); + $('#thumb-240x180').attr('src', `/_thumbnail/dynamic/${key}/cover/240/180`); + + File: /rsx/theme/components/inputs/photo/profile_photo_input.js + ------------------------------------------------------------------------- + Find: + this.state.thumbnail_url = `/_thumbnail/${this.state.attachment_key}/cover/${width}/${height}`; + + Replace with: + this.state.thumbnail_url = `/_thumbnail/dynamic/${this.state.attachment_key}/cover/${width}/${height}`; + +RECOMMENDED APPROACH + + Instead of manually constructing URLs, use the model methods which always + generate correct URLs: + + PHP: + $attachment->get_thumbnail_url('cover', 300, 200); + $attachment->get_thumbnail_url_preset('profile'); + + JavaScript (via Ajax stub): + const url = await File_Attachment_Model.get_thumbnail_url(key, 'cover', 300, 200); + +VERIFICATION + + 1. Search your codebase for manual thumbnail URL construction: + grep -r "/_thumbnail/" rsx/ --include="*.js" --include="*.php" + + 2. Update any matches that don't use get_thumbnail_url methods + + 3. Test thumbnail display in your application