Follow-up from #745. The blog viewer's media "Confirm Removal" flow (now correctly owner-gated) ends in BlogEntryComponent.removeMedia(), which is a placeholder snackbar ("Media removed (placeholder)") — no backend call exists, and the backend has no endpoint to remove a single media item from a post (the only path is PUT posts/{id} replacing the whole media array, which regenerates ids and drops thumbnails/paths/comments).
Fix
Backend (spikersoft-backend):
New DELETE api/blog/posts/{postId}/media/{mediaId} — [Authorize], owner-or-staff (same pattern as DeleteBlogPost/#745), soft delete: set the existing BlogMedia.IsDeleted/DeletedAt flags (file retention stays with the purger, matching the post-level retention model). Idempotent on repeat calls.
Filter IsDeleted media out of MapToResponseDto (alongside the existing quarantine filter).
Frontend (spikersoft-angular):
BlogService.removeMediaFromPost(postId, mediaId) + wire removeMedia() to it with real success/error snackbars; drop the placeholder string.
Repos: spikersoft-backend, spikersoft-angular
Follow-up from #745. The blog viewer's media "Confirm Removal" flow (now correctly owner-gated) ends in `BlogEntryComponent.removeMedia()`, which is a placeholder snackbar ("Media removed (placeholder)") — no backend call exists, and the backend has no endpoint to remove a single media item from a post (the only path is `PUT posts/{id}` replacing the whole media array, which regenerates ids and drops thumbnails/paths/comments).
## Fix
Backend (spikersoft-backend):
- New `DELETE api/blog/posts/{postId}/media/{mediaId}` — `[Authorize]`, owner-or-staff (same pattern as DeleteBlogPost/#745), **soft delete**: set the existing `BlogMedia.IsDeleted`/`DeletedAt` flags (file retention stays with the purger, matching the post-level retention model). Idempotent on repeat calls.
- Filter `IsDeleted` media out of `MapToResponseDto` (alongside the existing quarantine filter).
Frontend (spikersoft-angular):
- `BlogService.removeMediaFromPost(postId, mediaId)` + wire `removeMedia()` to it with real success/error snackbars; drop the placeholder string.
Repos: spikersoft-backend, spikersoft-angular
spikersoft-backend PR #437 (merged to master, 5139211a): new DELETE api/blog/posts/{postId}/media/{mediaId} — [Authorize], owner-or-staff, idempotent soft delete via the existing BlogMedia.IsDeleted/DeletedAt flags (file retention stays with the purger, matching the post-level model). Soft-removed media is filtered from all responses, rejects new comments, and stops serving photograph-info.
spikersoft-angular PR #477 (merged to master, 0d1f269b): removeMedia() now calls the endpoint via BlogService.removeMediaFromPost with real success/error snackbars and a busy flag; the placeholder string is gone.
Closing.
Resolved and merged:
- **spikersoft-backend PR #437** (merged to master, `5139211a`): new `DELETE api/blog/posts/{postId}/media/{mediaId}` — `[Authorize]`, owner-or-staff, idempotent **soft delete** via the existing `BlogMedia.IsDeleted`/`DeletedAt` flags (file retention stays with the purger, matching the post-level model). Soft-removed media is filtered from all responses, rejects new comments, and stops serving photograph-info.
- **spikersoft-angular PR #477** (merged to master, `0d1f269b`): `removeMedia()` now calls the endpoint via `BlogService.removeMediaFromPost` with real success/error snackbars and a busy flag; the placeholder string is gone.
Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up from #745. The blog viewer's media "Confirm Removal" flow (now correctly owner-gated) ends in
BlogEntryComponent.removeMedia(), which is a placeholder snackbar ("Media removed (placeholder)") — no backend call exists, and the backend has no endpoint to remove a single media item from a post (the only path isPUT posts/{id}replacing the whole media array, which regenerates ids and drops thumbnails/paths/comments).Fix
Backend (spikersoft-backend):
DELETE api/blog/posts/{postId}/media/{mediaId}—[Authorize], owner-or-staff (same pattern as DeleteBlogPost/#745), soft delete: set the existingBlogMedia.IsDeleted/DeletedAtflags (file retention stays with the purger, matching the post-level retention model). Idempotent on repeat calls.IsDeletedmedia out ofMapToResponseDto(alongside the existing quarantine filter).Frontend (spikersoft-angular):
BlogService.removeMediaFromPost(postId, mediaId)+ wireremoveMedia()to it with real success/error snackbars; drop the placeholder string.Repos: spikersoft-backend, spikersoft-angular
Resolved and merged:
5139211a): newDELETE api/blog/posts/{postId}/media/{mediaId}—[Authorize], owner-or-staff, idempotent soft delete via the existingBlogMedia.IsDeleted/DeletedAtflags (file retention stays with the purger, matching the post-level model). Soft-removed media is filtered from all responses, rejects new comments, and stops serving photograph-info.0d1f269b):removeMedia()now calls the endpoint viaBlogService.removeMediaFromPostwith real success/error snackbars and a busy flag; the placeholder string is gone.Closing.