Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SharePoint: Metadata #22546

Merged
merged 38 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
278fc5b
SP Initial
dynaway-kmi Jun 14, 2022
ee15cd6
file and variable naming
dynaway-kmi Jun 24, 2022
a720c9a
Request digest error handling
dynaway-kmi Jun 24, 2022
b2ac854
Naming, Spacing
dynaway-kmi Jul 5, 2022
2fbe416
added [NonDebuggable] when handling token
dynaway-kmi Jul 5, 2022
cca5983
variable naming
dynaway-kmi Jul 5, 2022
5463adf
Data Classification
dynaway-kmi Jul 5, 2022
7e11cd0
PermissionSet aded to System Application - Objects
dynaway-kmi Jul 6, 2022
9a3d43e
ListId on List Item
dynaway-kmi Jul 8, 2022
0b7c057
folder structure and empty lines
dynaway-kmi Jul 8, 2022
7e63d78
Authorization and access property
dynaway-kmi Jul 11, 2022
9a004bf
Enum comment
dynaway-kmi Jul 13, 2022
514534e
Tests
dynaway-kmi Jul 15, 2022
946ca48
request length
dynaway-kmi Jul 15, 2022
ea17965
User-Agent
dynaway-kmi Jul 15, 2022
fbd4f01
Error handling and request decoration
dynaway-kmi Jul 28, 2022
de0c7c8
Test library
dynaway-kmi Jul 29, 2022
458a16a
Authorization and tests
dynaway-kmi Aug 23, 2022
523f03c
fix
dynaway-kmi Aug 23, 2022
dfb70c9
Readme
dynaway-kmi Aug 23, 2022
2b15ecd
Readme
dynaway-kmi Aug 23, 2022
cbee7c3
DataClassification, AuthenticationCode ...
dynaway-kmi Sep 28, 2022
c486ef0
Add license headers to new code files
mazhelez Oct 4, 2022
59877ee
remove user credentials
dynaway-kmi Oct 4, 2022
c949012
remove user cred tests
dynaway-kmi Oct 4, 2022
366b9d1
Merge remote-tracking branch 'origin/kmi' into KMI
dynaway-kmi Oct 4, 2022
17f2143
Minor fixes
mazhelez Oct 5, 2022
bd0ac07
Merge branch 'main' of https://github.com/microsoft/ALAppExtensions i…
mazhelez Oct 6, 2022
16cdeb3
Update versions in app.json-s
mazhelez Oct 7, 2022
2062073
Hide "SharePoint Diagnostics" behind an interface + some minor improv…
mazhelez Oct 12, 2022
4ba44cf
Fix referenec to interface
mazhelez Oct 13, 2022
600a4de
Merge branch 'main' into KMI
mazhelez Nov 7, 2022
b0eadfa
Metadata
dynaway-kmi Mar 14, 2023
dcd0e94
Merge remote-tracking branch 'refs/remotes/origin/kmi' into kmi
dynaway-kmi Jun 9, 2023
1a6bd37
Fixing events
dynaway-kmi Jun 9, 2023
745c91c
Merge issue fix
dynaway-kmi Jun 9, 2023
428002a
Documentation
dynaway-kmi Jun 9, 2023
002198d
Merge branch 'main' into kmi
dynaway-kmi Jul 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Modules/System/SharePoint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ Downloads specified file to the client.
SPClient.DownloadFileContent(SharePointFile.OdataId, SharePointFile.Name);
```

### Update Metadata for a list item (including a file)
Updates specific metadata field for list item.
In order to update metadata of a file it needs to be accessed as list item.
```
var
SharePointFile: Record "SharePoint File";
begin
SharePointClient.UpdateListItemMetaDataField('Maintenance', 10, 'SP.Data.MaintenanceItem', 'WorkOrderNo', 'TEST0001');
```

## Error handling

### Retrieve diagnostic information
Expand Down
72 changes: 69 additions & 3 deletions Modules/System/SharePoint/src/SharePointClient.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,19 @@ codeunit 9100 "SharePoint Client"
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure GetFolderFilesByServerRelativeUrl(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary): Boolean
begin
exit(SharePointClientImpl.GetFolderFilesByServerRelativeUrl(ServerRelativeUrl, SharePointFile));
exit(SharePointClientImpl.GetFolderFilesByServerRelativeUrl(ServerRelativeUrl, SharePointFile, false));
end;

/// <summary>
/// Lists all files in the given folder.
/// </summary>
/// <param name="ServerRelativeUrl">URL of the parent folder.</param>
/// <param name="SharePointFile">Collection of the result (temporary record).</param>
/// <param name="ListAllFields">Include metadata in results.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure GetFolderFilesByServerRelativeUrl(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary; ListAllFields: Boolean): Boolean
begin
exit(SharePointClientImpl.GetFolderFilesByServerRelativeUrl(ServerRelativeUrl, SharePointFile, ListAllFields));
end;

/// <summary>
Expand Down Expand Up @@ -301,7 +313,20 @@ codeunit 9100 "SharePoint Client"
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure AddFileToFolder(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary): Boolean
begin
exit(SharePointClientImpl.AddFileToFolder(ServerRelativeUrl, SharePointFile));
exit(SharePointClientImpl.AddFileToFolder(ServerRelativeUrl, SharePointFile, false));
end;

/// <summary>
/// Adds a file to specific folder.
/// </summary>
/// <remarks>Requires UI interaction to pick a file.</remarks>
/// <param name="ServerRelativeUrl">URL of the parent folder.</param>
/// <param name="SharePointFile">Collection of the result (temporary record). Always one element.</param>
/// <param name="ListAllFields">Include metadata in results.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure AddFileToFolder(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary; ListAllFields: Boolean): Boolean
begin
exit(SharePointClientImpl.AddFileToFolder(ServerRelativeUrl, SharePointFile, ListAllFields));
end;

/// <summary>
Expand All @@ -315,7 +340,48 @@ codeunit 9100 "SharePoint Client"
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure AddFileToFolder(ServerRelativeUrl: Text; FileName: Text; var FileInStream: InStream; var SharePointFile: Record "SharePoint File" temporary): Boolean
begin
exit(SharePointClientImpl.AddFileToFolder(ServerRelativeUrl, FileName, FileInStream, SharePointFile));
exit(SharePointClientImpl.AddFileToFolder(ServerRelativeUrl, FileName, FileInStream, SharePointFile, false));
end;

/// <summary>
/// Adds a file to specific folder.
/// </summary>
/// <remarks>Does not require UI interaction.</remarks>
/// <param name="ServerRelativeUrl">URL of the parent folder.</param>
/// <param name="FileName">File name to use on SharePoint.</param>
/// <param name="FileInStream">File stream to upload.</param>
/// <param name="SharePointFile">Collection of the result (temporary record). Always one element.</param>
/// <param name="ListAllFields">Include metadata in results.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure AddFileToFolder(ServerRelativeUrl: Text; FileName: Text; var FileInStream: InStream; var SharePointFile: Record "SharePoint File" temporary; ListAllFields: Boolean): Boolean
dynaway-kmi marked this conversation as resolved.
Show resolved Hide resolved
begin
exit(SharePointClientImpl.AddFileToFolder(ServerRelativeUrl, FileName, FileInStream, SharePointFile, ListAllFields));
end;

/// <summary>
/// Updates metadata field for lsit item.
/// </summary>
/// <param name="ListTitle">The title of the list.</param>
/// <param name="ListItemEntityTypeFullName">The Entity Type for the list. Parameter can be found on a list object (ListItemEntityType).</param>
/// <param name="FieldName">The name of the metadata field.</param>
/// <param name="FieldValue">Value.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure UpdateListItemMetaDataField(ListTitle: Text; ItemId: Integer; ListItemEntityTypeFullName: Text; FieldName: Text; FieldValue: Text): Boolean
begin
exit(SharePointClientImpl.UpdateListItemMetaDataField(ListTitle, ItemId, ListItemEntityTypeFullName, FieldName, FieldValue));
end;

/// <summary>
/// Updates metadata field for lsit item.
/// </summary>
/// <param name="ListTitle">The GUID of the list.</param>
/// <param name="ListItemEntityTypeFullName">The Entity Type for the list. Parameter can be found on a list object (ListItemEntityType).</param>
/// <param name="FieldName">The name of the metadata field.</param>
/// <param name="FieldValue">Value.</param>
/// <returns>True if the operation was successful; otherwise - false.</returns>
procedure UpdateListItemMetaDataField(ListId: Guid; ItemId: Integer; ListItemEntityTypeFullName: Text; FieldName: Text; FieldValue: Text): Boolean
begin
exit(SharePointClientImpl.UpdateListItemMetaDataField(ListId, ItemId, ListItemEntityTypeFullName, FieldName, FieldValue));
end;

#endregion
Expand Down
75 changes: 72 additions & 3 deletions Modules/System/SharePoint/src/SharePointClientImpl.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ codeunit 9101 "SharePoint Client Impl."
SharePointUriBuilder.SetMethod('Lists', ListId);
SharePointUriBuilder.SetObject('items');


SharePointRequestHelper.SetAuthorization(Authorization);
SharePointOperationResponse := SharePointRequestHelper.Get(SharePointUriBuilder);
if not SharePointOperationResponse.GetDiagnostics().IsSuccessStatusCode() then
Expand Down Expand Up @@ -456,14 +457,16 @@ codeunit 9101 "SharePoint Client Impl."
exit(true);
end;

procedure GetFolderFilesByServerRelativeUrl(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary): Boolean
procedure GetFolderFilesByServerRelativeUrl(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary; ListAllFields: Boolean): Boolean
var
SharePointFileParser: Codeunit "SharePoint File";
Result: Text;
begin
SharePointUriBuilder.ResetPath();
SharePointUriBuilder.SetMethod('GetFolderByServerRelativeUrl', ServerRelativeUrl);
SharePointUriBuilder.SetObject('Files');
if ListAllFields then
SharePointUriBuilder.AddQueryParameter('$expand', 'ListItemAllFields');

SharePointRequestHelper.SetAuthorization(Authorization);
SharePointOperationResponse := SharePointRequestHelper.Get(SharePointUriBuilder);
Expand Down Expand Up @@ -539,7 +542,7 @@ codeunit 9101 "SharePoint Client Impl."
exit(true);
end;

procedure AddFileToFolder(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary): Boolean
procedure AddFileToFolder(ServerRelativeUrl: Text; var SharePointFile: Record "SharePoint File" temporary; ListAllFields: Boolean): Boolean
var
SharePointFileParser: Codeunit "SharePoint File";
SharePointHttpContent: Codeunit "SharePoint Http Content";
Expand All @@ -555,6 +558,8 @@ codeunit 9101 "SharePoint Client Impl."
SharePointUriBuilder.SetMethod('GetFolderByServerRelativeUrl', ServerRelativeUrl);
SharePointUriBuilder.SetObject('Files');
SharePointUriBuilder.SetMethod('add', 'url', '''' + FileName + '''');
if ListAllFields then
SharePointUriBuilder.AddQueryParameter('$expand', 'ListItemAllFields');

SharePointHttpContent.FromFileInStream(FileInStream);
SharePointRequestHelper.SetAuthorization(Authorization);
Expand All @@ -567,7 +572,7 @@ codeunit 9101 "SharePoint Client Impl."
exit(true);
end;

procedure AddFileToFolder(ServerRelativeUrl: Text; FileName: Text; var FileInStream: InStream; var SharePointFile: Record "SharePoint File" temporary): Boolean
procedure AddFileToFolder(ServerRelativeUrl: Text; FileName: Text; var FileInStream: InStream; var SharePointFile: Record "SharePoint File" temporary; ListAllFields: Boolean): Boolean
var
SharePointFileParser: Codeunit "SharePoint File";
SharePointHttpContent: Codeunit "SharePoint Http Content";
Expand All @@ -578,6 +583,8 @@ codeunit 9101 "SharePoint Client Impl."
SharePointUriBuilder.SetMethod('GetFolderByServerRelativeUrl', ServerRelativeUrl);
SharePointUriBuilder.SetObject('Files');
SharePointUriBuilder.SetMethod('add', 'url', '''' + FileName + '''');
if ListAllFields then
SharePointUriBuilder.AddQueryParameter('$expand', 'ListItemAllFields');

SharePointHttpContent.FromFileInStream(FileInStream);
SharePointRequestHelper.SetAuthorization(Authorization);
Expand All @@ -589,6 +596,68 @@ codeunit 9101 "SharePoint Client Impl."
SharePointFileParser.ParseSingleReturnValue(Result, SharePointFile);
exit(true);
end;

procedure UpdateListItemMetaDataField(ListTitle: Text; ItemId: Integer; ListItemEntityTypeFullName: Text; FieldName: Text; FieldValue: Text): Boolean
JesperSchulz marked this conversation as resolved.
Show resolved Hide resolved
var
SharePointHttpContent: Codeunit "SharePoint Http Content";
Metadata, Payload : JsonObject;
Txt: Text;
begin
SharePointUriBuilder.ResetPath();
SharePointUriBuilder.SetObject('lists');
SharePointUriBuilder.SetMethod('GetByTitle', ListTitle);
SharePointUriBuilder.SetMethod('items', ItemId);

Metadata.Add('type', ListItemEntityTypeFullName);
Payload.Add('__metadata', Metadata);
Payload.Add(FieldName, FieldValue);

SharePointHttpContent.FromJson(Payload);

SharePointHttpContent.GetContent().ReadAs(Txt);

SharePointHttpContent.SetRequestDigest(GetRequestDigest(SharePointUriBuilder.GetHost()));
SharePointHttpContent.SetXHttpMethod('MERGE');
SharePointHttpContent.SetIfMatch('*');

SharePointOperationResponse := SharePointRequestHelper.Patch(SharePointUriBuilder, SharePointHttpContent);
if not SharePointOperationResponse.GetDiagnostics().IsSuccessStatusCode() then
exit(false);

SharePointOperationResponse.GetResultAsText(Txt);
Message(Txt);
exit(true);
end;

procedure UpdateListItemMetaDataField(ListId: Guid; ItemId: Integer; ListItemEntityTypeFullName: Text; FieldName: Text; FieldValue: Text): Boolean
var
SharePointHttpContent: Codeunit "SharePoint Http Content";
Metadata, Payload : JsonObject;
Txt: Text;
begin
SharePointUriBuilder.ResetPath();
SharePointUriBuilder.SetMethod('Lists', ListId);
SharePointUriBuilder.SetMethod('items', ItemId);

Metadata.Add('type', ListItemEntityTypeFullName);
Payload.Add('__metadata', Metadata);
Payload.Add(FieldName, FieldValue);

SharePointHttpContent.FromJson(Payload);

SharePointHttpContent.GetContent().ReadAs(Txt);

SharePointHttpContent.SetRequestDigest(GetRequestDigest(SharePointUriBuilder.GetHost()));
SharePointHttpContent.SetXHttpMethod('MERGE');
SharePointHttpContent.SetIfMatch('*');

SharePointOperationResponse := SharePointRequestHelper.Patch(SharePointUriBuilder, SharePointHttpContent);
if not SharePointOperationResponse.GetDiagnostics().IsSuccessStatusCode() then
exit(false);

SharePointOperationResponse.GetResultAsText(Txt);
exit(true);
end;
#endregion

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ codeunit 9107 "SharePoint Http Content"
ContentLength: Integer;
ContentType: Text;
RequestDigest: Text;
XHttpMethod: Text;
IfMatch: Text;

procedure FromFileInStream(var FileInStream: Instream)
begin
Expand Down Expand Up @@ -55,6 +57,26 @@ codeunit 9107 "SharePoint Http Content"
exit(RequestDigest);
end;

procedure SetXHttpMethod(XHttpMethodValue: Text)
begin
XHttpMethodValue := XHttpMethodValue;
end;

procedure GetXHttpMethod(): Text;
begin
exit(XHttpMethod);
end;

procedure SetIfMatch(IfMatchValue: Text)
begin
IfMatch := IfMatchValue;
end;

procedure GetIfMatch(): Text;
begin
exit(IfMatch);
end;

local procedure GetContentLength(var SourceInStream: InStream) Length: Integer
var
MemoryStream: DotNet MemoryStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ codeunit 9109 "SharePoint Request Helper"
OperationResponse := SendRequest(PrepareRequestMsg("Http Request Type"::POST, SharePointUriBuilder, SharePointHttpContent));
end;

procedure Patch(SharePointUriBuilder: Codeunit "SharePoint Uri Builder"; SharePointHttpContent: Codeunit "SharePoint Http Content") OperationResponse: Codeunit "SharePoint Operation Response"
begin
OperationResponse := SendRequest(PrepareRequestMsg("Http Request Type"::PATCH, SharePointUriBuilder, SharePointHttpContent));
end;

[NonDebuggable]
local procedure PrepareRequestMsg(HttpRequestType: Enum "Http Request Type"; SharePointUriBuilder: Codeunit "SharePoint Uri Builder") RequestMessage: HttpRequestMessage
var
Expand All @@ -60,6 +65,15 @@ codeunit 9109 "SharePoint Request Helper"
Headers.Add('Accept', 'application/json;odata=verbose');
Headers.Add('User-Agent', GetUserAgentString());

if SharePointHttpContent.GetIfMatch() <> '' then
Headers.Add('If-Match', SharePointHttpContent.GetIfMatch());

if SharePointHttpContent.GetRequestDigest() <> '' then
Headers.Add('X-RequestDigest', SharePointHttpContent.GetRequestDigest());

if SharePointHttpContent.GetXHttpMethod() <> '' then
Headers.Add('X-HTTP-Method', SharePointHttpContent.GetXHttpMethod());

if SharePointHttpContent.GetContentLength() > 0 then begin
HttpContent := SharePointHttpContent.GetContent();
HttpContent.GetHeaders(Headers);
Expand All @@ -70,12 +84,11 @@ codeunit 9109 "SharePoint Request Helper"
if SharePointHttpContent.GetContentType() <> '' then
Headers.Add('Content-Type', SharePointHttpContent.GetContentType());

Headers.Add('X-RequestDigest', SharePointHttpContent.GetRequestDigest());
RequestMessage.Content(HttpContent);
end;
end;

[NonDebuggable]
//[NonDebuggable]
local procedure SendRequest(HttpRequestMessage: HttpRequestMessage) OperationResponse: Codeunit "SharePoint Operation Response"
dynaway-kmi marked this conversation as resolved.
Show resolved Hide resolved
var
HttpResponseMessage: HttpResponseMessage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ codeunit 9110 "SharePoint Uri Builder"
SetMethodTxt: Label '/%1(''%2'')', Comment = '%1 - method name, %2 - method parameter', Locked = true;
SetMethodRawTxt: Label '/%1(%2)', Comment = '%1 - method name, %2 - method parameter', Locked = true;
SetMethodGuidTxt: Label '/%1(guid''%2'')', Comment = '%1 - method name, %2 - method parameter', Locked = true;
QueryParameters: Dictionary of [Text, Text];

procedure GetHost(): Text
var
Expand All @@ -24,6 +25,11 @@ codeunit 9110 "SharePoint Uri Builder"
exit(NewUri.GetHost());
end;

procedure AddQueryParameter(ParameterName: Text; ParameterValue: Text)
begin
QueryParameters.Add(ParameterName, ParameterValue);
end;

procedure Initialize(NewServerName: Text; NewNamespace: Text)
begin
ServerName := NewServerName;
Expand Down Expand Up @@ -104,25 +110,37 @@ codeunit 9110 "SharePoint Uri Builder"
procedure GetUri(): Text
var
FullUri: Text;
ParameterName, ParameterValue : Text;
begin
FullUri := UriLbl + Uri + '/';
FullUri := FullUri.Replace('{server_name}', ServerName.TrimStart('/').TrimEnd('/')).Replace('{namespace}', Namespace.TrimStart('/').TrimEnd('/'));
if QueryParameters.Count() > 0 then begin
JesperSchulz marked this conversation as resolved.
Show resolved Hide resolved
FullUri += '?';
foreach ParameterName in QueryParameters.Keys() do begin
QueryParameters.Get(ParameterName, ParameterValue);
FullUri += EscapeDataString(ParameterName) + '=' + EscapeDataString(ParameterValue) + '&';
end;
FullUri := FullUri.TrimEnd('&');
end;
exit(FullUri);
end;

procedure ResetPath()
begin
Uri := '';
Clear(QueryParameters);
end;

procedure SetPath(NewPath: Text)
begin
Uri := NewPath;
Clear(QueryParameters);
end;

procedure ResetPath(Id: Text)
begin
Uri := UriLbl;
Clear(QueryParameters);
Uri := Uri.Replace('{server_name}', ServerName.TrimStart('/').TrimEnd('/')).Replace('{namespace}', Namespace.TrimStart('/').TrimEnd('/'));
Uri := Id.Replace(Uri, '');
end;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,19 @@ codeunit 9106 "SharePoint File"
if Payload.Get('type', JToken) then
SharePointFile.OdataType := CopyStr(JToken.AsValue().AsText(), 1, MaxStrLen(SharePointFile.OdataType));
end;

if Payload.Get('ListItemAllFields', JToken) then begin
Payload := JToken.AsObject();

if Payload.Get('Id', JToken) then
SharePointFile.Id := JToken.AsValue().AsInteger();

ProcessSharePointFileMetadata(JToken, SharePointFile);
end;
end;

[IntegrationEvent(false, false)]
dynaway-kmi marked this conversation as resolved.
Show resolved Hide resolved
local procedure ProcessSharePointFileMetadata(Metadata: JsonToken; SharePointFile: Record "SharePoint File" temporary)
begin
end;
}
Loading