-
Notifications
You must be signed in to change notification settings - Fork 217
Description
Category
- Bug
Describe the bug
Attempting to delete a SharePoint list attachment with special characters in the file name produces an exception.
Steps to reproduce
Given:
- spListItem = a PnP.Core.Model.SharePoint.ListItem
- The list item has an attachment with a name like FW You're all booked to fly XXX on 27 Feb 2020, reference XXXX.eml (note the ' in "You're")
await spListItem.EnsurePropertiesAsync(p => p.AttachmentFiles);
IAttachment[] oldAttachmentsArray = spListItem.AttachmentFiles.AsRequested().ToArray();
for (int i=oldAttachmentsArray.Count()-1; i>=0; i--)
{
try
{
await oldAttachmentsArray[i].DeleteAsync();
} catch (Exception ex)
{
LogException(ex, "Failed to delete existing attachment '{AttachmentName}'.", new object[1] { oldAttachmentsArray[i].FileName });
}
}The DeleteAsync above produces a "SharePoint Rest service exception" with an error code of "Microsoft.SharePoint.Client.InvalidClientQueryException" and the message "The expression "web/lists/getbyid(guid'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')/items(244)/attachmentfiles/getbyfilename('FW You're all booked to fly XXX on 27 Feb 2020, reference XXXX.eml')" is not valid."
Expected behavior
The attachment should be deleted without an exception.
Environment details (development & target environment)
- SDK version: [PnP.Core 1.6.0]
- OS: [Windows 10]
- SDK used in: [Azure Function App]
- Framework: [.NET Core v3.1]
- Browser(s): [N/A]
- Tooling: [Visual Studio 2022]
- Additional details: The more context you can provide, the easier it is (and therefore quicker) to help.
Additional context
I did some searching of the repo in hopes of spotting an easy fix, or at least saving someone a bit of time. I'm guessing the problem starts in src/sdk/PnP.Core/Model/SharePoint/Core/Internal/Attachment.cs:
[SharePointType("SP.Attachment", Uri = "_api/web/lists/getbyid(guid'{List.Id}')/items({Parent.Id})/attachmentfiles/getbyfilename('{Id}')", LinqGet = "_api/web/lists/getbyid(guid'{List.Id}')/items({Parent.Id})/attachmentfiles")]
internal sealed class Attachment : BaseDataModel<IAttachment>, IAttachmentThere needs to be some mechanism to encode that {Id}. AttachmentCollection.FileUpload (which works) encodes the file name like this:
var encodedServerFileName = WebUtility.UrlEncode(newFile.FileName.Replace("'", "''")).Replace("+", "%20");
string fileCreateRequest = $"_api/web/lists/getbyid(guid'{{List.Id}}')/items({{Parent.Id}})/attachmentfiles/addusingpath(decodedUrl='{encodedServerFileName}')";