Codeunit 2717 Page Summary Provider Impl., source in 24

Source242526272829metadata in 24

src/System Application/App/Page Summary Provider/src/PageSummaryProviderImpl.Codeunit.al322 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------

namespace System.Integration;

using System;
using System.Text;
using System.Reflection;
using System.Environment;

/// <summary>
/// Implements functionality to get summary data for a given object.
/// </summary>
codeunit 2717 "Page Summary Provider Impl."
{
    Access = Internal;
    InherentEntitlements = X;
    InherentPermissions = X;
    Permissions = tabledata Company = r,
                  tabledata "Page Metadata" = r,
                  tabledata "Tenant Media Set" = r,
                  tabledata "Tenant Media Thumbnails" = r;

    procedure GetPageSummary(PageId: Integer; Bookmark: Text): Text
    var
        RecId: RecordId;
        ResultJsonObject: JsonObject;
    begin
        // Add header
        AddPageSummaryHeader(PageId, ResultJsonObject);

        // If show summary record is false, then exit with summary type caption
        if not PageSummarySettings.IsShowRecordSummaryEnabled() then
            exit(Format(ResultJsonObject));

        if Bookmark = '' then
            exit(Format(ResultJsonObject)); // There is no bookmark, so just return page header

        // Initialize variables
        if not Evaluate(RecId, Bookmark, 10) then begin // 10 = Evaluate string into RecordId
            AddErrorMessage(ResultJsonObject, InvalidBookmarkErrorCodeTok, InvalidBookmarkErrorMessageTxt);
            exit(Format(ResultJsonObject)); // Bookmark is invalid, so returning the information we actually have about the page
        end;
        exit(GetFieldsSummary(PageId, RecId, Bookmark, ResultJsonObject));
    end;

    procedure GetPageSummary(PageId: Integer; SystemId: Guid): Text
    var
        PageMetadata: Record "Page Metadata";
        RecId: RecordId;
        SourceRecordRef: RecordRef;
        ResultJsonObject: JsonObject;
        Bookmark: Text;
    begin
        // Add header
        AddPageSummaryHeader(PageId, ResultJsonObject);

        // Initialize variables
        if not PageMetadata.Get(PageId) then
            exit(Format(ResultJsonObject));

        SourceRecordRef.Open(PageMetadata.SourceTable);

        if not SourceRecordRef.GetBySystemId(SystemId) then begin
            AddErrorMessage(ResultJsonObject, InvalidSystemIdErrorCodeTok, InvalidSystemIdErrorMessageTxt);
            exit(Format(ResultJsonObject)); // System ID is invalid, so returning the information we actually have about the page
        end;

        AddUrl(ResultJsonObject, PageId, SourceRecordRef);

        // If show summary record is false, then exit with summary type caption
        if not PageSummarySettings.IsShowRecordSummaryEnabled() then
            exit(Format(ResultJsonObject));

        RecId := SourceRecordRef.RecordId;
        Bookmark := Format(RecId, 0, 10); // 10 = Format RecordId into string
        if Bookmark = '' then
            exit(Format(ResultJsonObject));
        exit(GetFieldsSummary(PageId, RecId, Bookmark, ResultJsonObject));
    end;

    procedure GetPageUrlBySystemID(PageId: Integer; SystemId: Guid): Text
    var
        PageMetadata: Record "Page Metadata";
        SourceRecordRef: RecordRef;
        ResultJsonObject: JsonObject;
    begin
        ResultJsonObject.Add('version', GetVersion());

        if not PageMetadata.Get(PageId) then begin
            AddErrorMessage(ResultJsonObject, PageNotFoundErrorCodeTok, StrSubstNo(PageNotFoundErrorMessageTxt, PageId));
            exit(Format(ResultJsonObject));
        end;

        SourceRecordRef.Open(PageMetadata.SourceTable);

        if not SourceRecordRef.GetBySystemId(SystemId) then begin
            AddErrorMessage(ResultJsonObject, InvalidSystemIdErrorCodeTok, InvalidSystemIdErrorMessageTxt);
            exit(Format(ResultJsonObject));
        end;

        AddUrl(ResultJsonObject, PageId, SourceRecordRef);
        Session.LogMessage('0000JAT', StrSubstNo(GetPageUrlSuccessTelemetryTxt, PageId), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PageSummaryCategoryLbl);
        exit(Format(ResultJsonObject));
    end;

    procedure GetFieldsSummary(PageId: Integer; RecId: RecordId; Bookmark: Text; var ResultJsonObject: JsonObject): Text
    var
        PageSummaryProvider: Codeunit "Page Summary Provider";
        FieldsJsonArray: JsonArray;
        Handled: Boolean;
    begin
        // Allow partner to override returned fields
        PageSummaryProvider.OnBeforeGetPageSummary(PageId, RecId, FieldsJsonArray, Handled);
        if Handled then begin // Partner overrode fields
            Session.LogMessage('0000D73', StrSubstNo(OnBeforeGetPageSummaryWasHandledTxt, PageId), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PageSummaryCategoryLbl);
            AddFieldsToResult(FieldsJsonArray, ResultJsonObject);
            exit(Format(ResultJsonObject));
        end;

        // Get summary fields
        if not TryGetPageSummaryFields(PageId, RecId, Bookmark, ResultJsonObject) then begin
            AddErrorMessage(ResultJsonObject, FailedGetSummaryFieldsCodeTok, GetLastErrorText());
            exit(Format(ResultJsonObject));
        end;

        exit(Format(ResultJsonObject));
    end;

    local procedure AddErrorMessage(var ResultJsonObject: JsonObject; ErrorCode: Text; ErrorMessage: Text)
    var
        ErrorJsonObject: JsonObject;
    begin
        Session.LogMessage('0000EAX', ErrorCode, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PageSummaryCategoryLbl);
        ErrorJsonObject.Add('code', ErrorCode);
        ErrorJsonObject.Add('message', ErrorMessage);
        ResultJsonObject.Add('error', ErrorJsonObject);
    end;

    local procedure AddUrl(var ResultJsonObject: JsonObject; PageId: Integer; SourceRecordRef: RecordRef)
    var
        Url: Text;
    begin
        Url := GetUrl(ClientType::Web, CompanyName(), ObjectType::Page, PageId, SourceRecordRef);
        ResultJsonObject.Add('url', Url);
    end;

    local procedure AddPageSummaryHeader(PageId: Integer; var ResultJsonObject: JsonObject)
    var
        PageMetadata: Record "Page Metadata";
        PageCaption: Text;
    begin
        if PageMetadata.Get(PageId) then
            PageCaption := PageMetadata.Caption
        else
            PageCaption := StrSubstNo(PageTxt, PageId);
        ResultJsonObject.Add('version', GetVersion());
        ResultJsonObject.Add('pageCaption', PageCaption);
        ResultJsonObject.Add('pageType', format(PageMetadata.PageType));
        ResultJsonObject.Add('summaryType', GetSummaryName(Enum::"Summary Type"::Caption)); // default summary type is caption
        ResultJsonObject.Add('cardPageId', PageMetadata.CardPageID);
    end;

    [TryFunction]
    local procedure TryGetPageSummaryFields(PageId: Integer; RecId: RecordId; Bookmark: Text; var ResultJsonObject: JsonObject)
    var
        PageSummaryProvider: Codeunit "Page Summary Provider";
        NavPageSummaryALFunctions: DotNet NavPageSummaryALFunctions;
        GenericList: DotNet GenericList1;
        NavPageSummaryALResponse: DotNet NavPageSummaryALResponse;
        NavPageSummaryALField: DotNet NavPageSummaryALField;
        FieldsJsonArray: JsonArray;
        PageSummaryFieldList: List of [Integer];
        PageSummaryField: Integer;
        ErrorMessage: Text;
    begin
        GenericList := NavPageSummaryALFunctions.GetSummaryFields(PageId);
        if IsNull(GenericList) then
            exit;

        foreach PageSummaryField in GenericList do
            PageSummaryFieldList.Add(PageSummaryField);
        CorrectFieldOrderingOfBrick(PageSummaryFieldList);

        // Allow partners to override fields to be shown + order
        PageSummaryProvider.OnAfterGetSummaryFields(PageId, RecId, PageSummaryFieldList);
        GenericList.Clear();
        foreach PageSummaryField in PageSummaryFieldList do
            GenericList.Add(PageSummaryField);

        if (GenericList.Count() > 0) then begin
            NavPageSummaryALResponse := NavPageSummaryALFunctions.GetSummary(PageId, Bookmark, GenericList);

            if not NavPageSummaryALResponse.Success then begin
                Session.LogMessage('0000DGV', StrSubstNo(SummaryFailureTelemetryTxt, PageId), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PageSummaryCategoryLbl);
                ErrorMessage := NavPageSummaryALResponse.ErrorMessage;
                Error(ErrorMessage);
            end;
            // Get field values
            foreach NavPageSummaryALField in NavPageSummaryALResponse.SummaryFields do
                AddPageSummaryFieldToJsonArray(NavPageSummaryALField, FieldsJsonArray);
        end;

        // Allow partner to finally override field names and values
        PageSummaryProvider.OnAfterGetPageSummary(PageId, RecId, FieldsJsonArray);
        AddFieldsToResult(FieldsJsonArray, ResultJsonObject);
    end;

    local procedure GetSummaryName(SummaryType: Enum "Summary Type"): Text;
    var
        Index: Integer;
    begin
        Index := SummaryType.Ordinals.IndexOf(SummaryType.AsInteger());
        exit(SummaryType.Names().Get(Index));
    end;

    local procedure AddFieldsToResult(var FieldsJsonArray: JsonArray; var ResultJsonObject: JsonObject)
    begin
        if FieldsJsonArray.Count() > 0 then
            ResultJsonObject.Replace('summaryType', GetSummaryName(Enum::"Summary Type"::Brick));
        ;

        ResultJsonObject.Add('fields', FieldsJsonArray);
    end;

    local procedure CorrectFieldOrderingOfBrick(var PageSummaryFieldList: List of [Integer])
    var
        TempValue: Integer;
    begin
        // Currently we just want to swap first and second field if there is more than one
        if PageSummaryFieldList.Count() <= 1 then
            exit;

        TempValue := PageSummaryFieldList.Get(1);
        PageSummaryFieldList.RemoveAt(1);
        PageSummaryFieldList.Insert(2, TempValue);
    end;

    local procedure AddPageSummaryFieldToJsonArray(NavPageSummaryALField: DotNet NavPageSummaryALField; var FieldsJsonArray: JsonArray)
    var
        FieldsJsonObject: JsonObject;
        FieldValue: Text;
        FieldType: Text;
        MimeType: Text;
    begin
        if NavPageSummaryALField.FieldType = 33794 then // 33794 == Blob
            exit;
        FieldValue := NavPageSummaryALField.Value.ToString();
        FieldType := NavPageSummaryALField.FieldType.ToString();

        // Handle pictures - 26209 == MediaSet, 26208 == Media
        if NavPageSummaryALField.FieldType = 26208 then
            ExtractPictureFromMedia(NavPageSummaryALField.Value.ToString(), FieldValue, MimeType, FieldType);
        if NavPageSummaryALField.FieldType = 26209 then
            ExtractPictureFromMediaSet(NavPageSummaryALField.Value.ToString(), FieldValue, MimeType, FieldType);

        // Add the actual field
        FieldsJsonObject.Add('caption', NavPageSummaryALField.Caption);
        if NavPageSummaryALField.ExtendedType <> NavPageSummaryALField.ExtendedType::Undefined then
            FieldsJsonObject.Add('extendedType', NavPageSummaryALField.ExtendedType.ToString());
        FieldsJsonObject.Add('fieldValue', FieldValue);
        FieldsJsonObject.Add('fieldType', FieldType);
        if MimeType <> '' then
            FieldsJsonObject.Add('mimeType', MimeType);
        FieldsJsonArray.Add(FieldsJsonObject);
    end;

    local procedure ExtractPictureFromMedia(ImageGuid: Guid; var FieldValue: Text; var MimeType: Text; var FieldType: Text)
    var
        TenantMediaThumbnails: Record "Tenant Media Thumbnails";
        Base64Convert: Codeunit "Base64 Convert";
        Data: InStream;
    begin
        FieldType := 'Media';
        // Filter on large image thumbnail
        // Whenever an image is stored, we also store a large image thumbnail in dimensions 240 x 240 (used in grid view)
        TenantMediaThumbnails.SetRange("Media Id", ImageGuid);
        TenantMediaThumbnails.SetRange("Height", 240);
        TenantMediaThumbnails.SetRange("Width", 240);
        if not TenantMediaThumbnails.FindFirst() then begin
            Clear(FieldValue);
            Clear(MimeType);
            exit;
        end;

        TenantMediaThumbnails.CalcFields(Content);
        TenantMediaThumbnails.Content.CreateInStream(Data);
        FieldValue := Base64Convert.ToBase64(Data);
        MimeType := TenantMediaThumbnails."Mime Type";
    end;

    local procedure ExtractPictureFromMediaSet(ImageGuid: Guid; var FieldValue: Text; var MimeType: Text; var FieldType: Text)
    var
        TenantMediaSet: Record "Tenant Media Set";
    begin
        TenantMediaSet.SetRange(Id, ImageGuid);
        if TenantMediaSet.FindFirst() then;
        ExtractPictureFromMedia(TenantMediaSet."Media ID".MediaId, FieldValue, MimeType, FieldType);
    end;

    procedure GetVersion(): Text[30]
    begin
        exit('1.1');
    end;

    var
        PageSummarySettings: Codeunit "Page Summary Settings";
        PageTxt: Label 'Page %1', Comment = '%1 is a whole number, ex. 10';
        PageSummaryCategoryLbl: Label 'Page Summary Provider', Locked = true;
        OnBeforeGetPageSummaryWasHandledTxt: Label 'OnBeforeGetPageSummary event was handled for page %1.', Locked = true;
        SummaryFailureTelemetryTxt: Label 'Failure to get summary for page %1.', Locked = true;
        InvalidBookmarkErrorCodeTok: Label 'InvalidBookmark', Locked = true;
        InvalidBookmarkErrorMessageTxt: Label 'The bookmark is invalid.';
        FailedGetSummaryFieldsCodeTok: Label 'FailedGettingPageSummaryFields', Locked = true;
        InvalidSystemIdErrorCodeTok: Label 'InvalidSystemId', Locked = true;
        InvalidSystemIdErrorMessageTxt: Label 'The system ID is invalid.';
        PageNotFoundErrorCodeTok: Label 'PageNotFound', Locked = true;
        PageNotFoundErrorMessageTxt: Label 'Page %1 is not found.', Comment = '%1 is a whole number, ex. 10';
        GetPageUrlSuccessTelemetryTxt: Label 'Successfully added url for page %1.', Locked = true;
}