Fix PostIt attachment links to match uploaded filenames

Apply the shared filename filter before URL encoding, preserve escaped URLs and original link labels, and add regression coverage for accented and spaced filenames. Update the changelog.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Paul Schneider 2026-10-11 01:38:25 +01:00
commit 63b44b5a92
Signed by: notazof
GPG key ID: AD4B9B545FBD7D12
3 changed files with 22 additions and 9 deletions

View file

@ -20,8 +20,13 @@ namespace PostIt.Tests;
/// </summary>
public class BlogAttachmentLinkTests
{
[Fact]
public async Task Save_with_attachment_appends_absolute_link_derived_from_the_oidc_authority()
[Theory]
[InlineData("note.txt", "note.txt")]
[InlineData("INSC_E03686381_SCHNEIDER_000113378AA__Troisi\u00e8me__.PDF",
"INSC_E03686381_SCHNEIDER_000113378AA__Troisi_232me__.PDF")]
[InlineData("rapport final.pdf", "rapport%20final.pdf")]
public async Task Save_with_attachment_appends_absolute_link_derived_from_the_oidc_authority(
string originalName, string urlName)
{
const string authority = "https://idp.example.test";
@ -53,7 +58,7 @@ public class BlogAttachmentLinkTests
vm.DraftTitle = "Après";
vm.DraftArticle = "Contenu modifié.";
vm.DraftAttachments.Add(new BlogUploadFile(
"note.txt",
originalName,
System.Text.Encoding.UTF8.GetBytes("payload test"),
"text/plain"));
@ -68,12 +73,14 @@ public class BlogAttachmentLinkTests
var sent = Assert.IsType<System.Func<HttpContent>>(linkPut.body);
var multipartContent = Assert.IsType<MultipartFormDataContent>(sent());
var expectedUrl = $"{authority}/files/tester/blogs/42/note.txt";
var sentStringContent = multipartContent.First() as StringContent ;
var expectedUrl = $"{authority}/files/tester/blogs/42/{urlName}";
var sentStringContent = Assert.IsType<StringContent>(multipartContent.First());
var sentString = await sentStringContent.ReadAsStringAsync(TestContext.Current.CancellationToken);
Assert.Contains($"- [note.txt]({expectedUrl})", sentString);
using var document = System.Text.Json.JsonDocument.Parse(sentString);
var article = document.RootElement.GetProperty("article").GetString();
Assert.Contains($"- [{originalName}]({expectedUrl})", article);
// The regression shape: a relative, authority-less link.
Assert.DoesNotContain("](/files/", sentString);
Assert.DoesNotContain("](/files/", article);
}
}

View file

@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
using Yavsc.Blogspot;
using Yavsc.Api.Client;
using Yavsc.Abstract.Files;
using Yavsc.Server.Helpers;
using AvaloniaEdit.Document;
namespace PostIt.ViewModels;
@ -611,7 +612,8 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel
foreach (var attachment in attachments)
{
var relativePath = $"{EscapePathSegment(ownerSegment)}/blogs/{post.Id}/{EscapePathSegment(attachment.FileName)}";
var storedName = AbstractFileSystemHelpers.FilterFileName(attachment.FileName);
var relativePath = $"{EscapePathSegment(ownerSegment)}/blogs/{post.Id}/{EscapePathSegment(storedName)}";
var fileUrl = ResolveUserFileUrl(relativePath);
var markdownLine = $"- [{attachment.FileName}]({fileUrl})";
@ -636,7 +638,7 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel
if (!string.IsNullOrWhiteSpace(authority)
&& Uri.TryCreate(authority, UriKind.Absolute, out var baseUri))
{
return FileServerUrlHelpers.GetUserFilesUri(baseUri, relativePath).ToString();
return FileServerUrlHelpers.GetUserFilesUri(baseUri, relativePath).AbsoluteUri;
}
return $"{Yavsc.Constants.UserFilesPath}/{relativePath}";