Codeunit 2503 Extension Operation Impl in 28

App
System Application
Namespace
System.Apps

Procedures, 30

Versions171819202122232425262728latest

Source242526272829

Source in 28

src/System Application/App/Extension Management/src/ExtensionOperationImpl.Codeunit.al466 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.Apps;

using System;
using System.Environment;
using System.Environment.Configuration;
using System.Utilities;

codeunit 2503 "Extension Operation Impl"
{
    Access = Internal;
    InherentEntitlements = X;
    InherentPermissions = X;
    SingleInstance = false;
    Permissions = tabledata Media = r,
                  tabledata "NAV App Setting" = rm,
                  tabledata "NAV App Tenant Operation" = r,
                  tabledata "Published Application" = r;

    var
        DotNetALNavAppOperationInvoker: DotNet ALNavAppOperationInvoker;
        DotNetNavAppALInstaller: DotNet NavAppALInstaller;
        OperationInvokerHasBeenCreated: Boolean;
        InstallerHasBeenCreated: Boolean;
        ExtensionFileNameTxt: Label '%1_%2_%3.zip', Comment = '%1=Name, %2=Publisher, %3=Version', Locked = true;
        CurrentOperationProgressMsg: Label 'Extension installation is in progress. Please check the Extension Installation Status page for updates.';
        ScheduledOperationMajorProgressMsg: Label 'Extension installation has been scheduled for the next major version. Please check the Extension Installation Status page for updates.';
        ScheduledOperationMinorProgressMsg: Label 'Extension installation has been scheduled for the next minor version. Please check the Extension Installation Status page for updates.';
        DownloadExtensionSourceIsNotAllowedErr: Label 'The effective policies for this package do not allow you to download the source code. Contact the extension provider for more information.';
        DialogTitleTxt: Label 'Export';
        OutExtTxt: Label 'Text Files (*.txt)|*.txt|*.*';
        InstallationFailedOpenDetailsQst: Label 'Sorry, we couldn''t install the app. Do you want to see the details?';
        InstallationFailedOpenDetailsTxt: Label 'App installation failed. User has chosen to see the details.';
        InstallationFailedDoNotOpenDetailsTxt: Label 'App installation failed. User has chosen not to check out the details.';
        PageCaptionTok: Label '%1 %2', Comment = '%1 = Page default caption %2 = App name';

    local procedure AssertIsInitialized()
    begin
        if not InstallerHasBeenCreated then begin
            DotNetNavAppALInstaller := DotNetNavAppALInstaller.NavAppALInstaller();
            InstallerHasBeenCreated := true;
        end;
    end;

    procedure DeployExtension(AppId: Guid; lcid: Integer; IsUIEnabled: Boolean; PreviewKey: Text)
    var
        NAVAppTenantOperation: Record "NAV App Tenant Operation";
        ExtensionOperationImpl: Codeunit "Extension Operation Impl";
        ExtensionMarketplace: Codeunit "Extension Marketplace";
        ExtnInstallationProgress: Page "Extn. Installation Progress";
        ExtnDeploymentStatusDetail: Page "Extn Deployment Status Detail";
        OperationId: Guid;
    begin
        CheckPermissions();
        InitializeOperationInvoker();

        OperationId := DotNetALNavAppOperationInvoker.DeployTarget(AppId, Format(lcid), PreviewKey);

        if IsUIEnabled then begin
            ExtnInstallationProgress.SetOperationIdToMonitor(OperationId);
            ExtnInstallationProgress.Caption(StrSubstNo(PageCaptionTok, ExtnInstallationProgress.Caption, GetAppName(AppId, OperationId)));
            ExtnInstallationProgress.RunModal();

            ExtensionOperationImpl.RefreshStatus(OperationId);
            NAVAppTenantOperation.Get(OperationId);

            if NAVAppTenantOperation.Status = NAVAppTenantOperation.Status::Failed then begin
                if Confirm(InstallationFailedOpenDetailsQst) then begin
                    Session.LogMessage('0000I2M', InstallationFailedOpenDetailsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', 'Extensions');
                    ExtnDeploymentStatusDetail.SetOperationRecord(NAVAppTenantOperation);
                    ExtnDeploymentStatusDetail.RunModal();
                end else
                    Session.LogMessage('0000I2N', InstallationFailedDoNotOpenDetailsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', 'Extensions');
                exit;
            end;

            if NAVAppTenantOperation.Status = NAVAppTenantOperation.Status::Completed then
                ExtensionMarketplace.RunSetupForExtension(AppId);
        end;
    end;

    procedure UploadExtension(PackageInStream: InStream; lcid: Integer)
    var
        DotNetALPackageDeploymentSchedule: DotNet ALPackageDeploymentSchedule;
    begin
        CheckPermissions();
        DotNetALPackageDeploymentSchedule := DotNetALPackageDeploymentSchedule.Immediate;
        InitializeOperationInvoker();
        DotNetALNavAppOperationInvoker.UploadPackage(PackageInStream, DotNetALPackageDeploymentSchedule, Format(lcid));
    end;

    procedure DeployAndUploadExtension(PackageInStream: InStream; lcid: Integer; DeployTo: Enum "Extension Deploy To"; SyncMode: Enum "Extension Sync Mode")
    var
        DotNetALPackageDeploymentSchedule: DotNet ALPackageDeploymentSchedule;
        DotNetALNavAppSyncMode: DotNet ALNavAppSyncMode;
    begin
        CheckPermissions();
        InitializeOperationInvoker();

        case SyncMode of
            SyncMode::Add:
                DotNetALNavAppSyncMode := DotNetALNavAppSyncMode.Add;
            SyncMode::"Force Sync":
                DotNetALNavAppSyncMode := DotNetALNavAppSyncMode.ForceSync;
        end;

        case DeployTo of
            DeployTo::"Current version":
                begin
                    DotNetALPackageDeploymentSchedule := DotNetALPackageDeploymentSchedule.Immediate;
                    DotNetALNavAppOperationInvoker.UploadPackage(PackageInStream, DotNetALPackageDeploymentSchedule, Format(lcid), DotNetALNavAppSyncMode);
                    Message(CurrentOperationProgressMsg);
                end;
            DeployTo::"Next minor version":
                begin
                    DotNetALPackageDeploymentSchedule := DotNetALPackageDeploymentSchedule.StageForNextMinorUpdate;
                    DotNetALNavAppOperationInvoker.UploadPackage(PackageInStream, DotNetALPackageDeploymentSchedule, Format(lcid), DotNetALNavAppSyncMode);
                    Message(ScheduledOperationMinorProgressMsg);
                end;
            DeployTo::"Next major version":
                begin
                    DotNetALPackageDeploymentSchedule := DotNetALPackageDeploymentSchedule.StageForNextUpdate;
                    DotNetALNavAppOperationInvoker.UploadPackage(PackageInStream, DotNetALPackageDeploymentSchedule, Format(lcid), DotNetALNavAppSyncMode);
                    Message(ScheduledOperationMajorProgressMsg);
                end;
        end;
    end;

    procedure UnpublishExtension(PackageID: Guid): Boolean
    var
        PublishedApplication: Record "Published Application";
        ExtensionInstallationImpl: Codeunit "Extension Installation Impl";
    begin
        PublishedApplication.SetRange("Package ID", PackageID);
        PublishedApplication.SetRange("Tenant Visible", true);
        PublishedApplication.SetFilter("Published As", '<>%1', PublishedApplication."Published As"::Global);

        if PublishedApplication.IsEmpty() then
            exit(false);

        if ExtensionInstallationImpl.IsInstalledByPackageId(PackageID) then
            exit(false);

        UnpublishUninstalledPerTenantExtension(PackageID);
        exit(true);
    end;

    procedure UnpublishUninstalledPerTenantExtension(PackageID: Guid)
    begin
        CheckPermissions();
        AssertIsInitialized();
        DotNetNavAppALInstaller.ALUnpublishNavTenantApp(PackageID);
    end;

    procedure GetExtensionSource(PackageId: Guid; var ExtensionSourceTempBlob: Codeunit "Temp Blob"; var CleanFileName: Text): Boolean
    var
        PublishedApplication: Record "Published Application";
        ExtensionInstallationImpl: Codeunit "Extension Installation Impl";
        DotNetNavDesignerALFunctions: DotNet NavDesignerALFunctions;
        NvOutStream: OutStream;
        FileName: Text;
        VersionString: Text;
    begin
        CheckPermissions();

        PublishedApplication.SetRange("Package ID", PackageId);
        PublishedApplication.SetRange("Tenant Visible", true);
        PublishedApplication.SetFilter("Published As", '<>%1', PublishedApplication."Published As"::Global);

        if not PublishedApplication.FindFirst() then
            exit(false);

        if not DotNetNavDesignerALFunctions.IsDownloadSourceCodeAllowedForCurrentUser(PublishedApplication."Runtime Package ID") then
            Error(DownloadExtensionSourceIsNotAllowedErr);

        ExtensionSourceTempBlob.CreateOutStream(NvOutStream);
        VersionString := ExtensionInstallationImpl.GetVersionDisplayString(PublishedApplication);

        DotNetNavDesignerALFunctions.GenerateDesignerPackageZipStreamByVersion(NvOutStream, PublishedApplication.ID, VersionString);

        FileName := StrSubstNo(ExtensionFileNameTxt, PublishedApplication.Name, PublishedApplication.Publisher, VersionString);
        CleanFileName := DotNetNavDesignerALFunctions.SanitizeDesignerFileName(FileName, '_');

        exit(true);
    end;

    procedure DownloadExtensionSource(PackageId: Guid): Boolean
    var
        TempBlob: Codeunit "Temp Blob";
        NvInStream: InStream;
        CleanFileName: Text;
    begin
        if not GetExtensionSource(PackageId, TempBlob, CleanFileName) then
            exit(false);

        TempBlob.CreateInStream(NvInStream);

        exit(DownloadFromStream(NvInStream, DialogTitleTxt, '', '*.*', CleanFileName));
    end;

    procedure DownloadDeploymentStatusDetails(OperationId: Guid)
    var
        TempBlob: Codeunit "Temp Blob";
        NavOutStream: OutStream;
        NavInStream: InStream;
        ToFile: Text;
    begin
        TempBlob.CreateOutStream(NavOutStream);
        GetDeploymentDetailedStatusMessageAsStream(OperationId, NavOutStream);

        TempBlob.CreateInStream(NavInStream);
        ToFile := 'Result' + OperationId + '.txt';
        DownloadFromStream(NavInStream, DialogTitleTxt, '', OutExtTxt, ToFile);
    end;

    procedure RefreshStatus(OperationID: Guid)
    begin
        InitializeOperationInvoker();
        DotNetALNavAppOperationInvoker.RefreshOperationStatus(OperationID);
    end;

    procedure ConfigureExtensionHttpClientRequestsAllowance(PackageId: Text; AreHttpClientRqstsAllowed: Boolean): Boolean
    var
        PublishedApplication: Record "Published Application";
        NavAppSetting: Record "NAV App Setting";
        PackageIDGuid: Guid;
    begin
        CheckPermissions();

        Evaluate(PackageIDGuid, PackageId);

        PublishedApplication.SetRange("Package ID", PackageIDGuid);
        PublishedApplication.SetRange("Tenant Visible", true);

        if not PublishedApplication.FindFirst() then
            exit(false);

        if not NavAppSetting.Get(PublishedApplication.ID) then
            exit(false);

        NavAppSetting.Validate("Allow HttpClient Requests", AreHttpClientRqstsAllowed);
        NavAppSetting.Modify(true);

        exit(true);
    end;

    local procedure InitializeOperationInvoker()
    begin
        if not OperationInvokerHasBeenCreated then begin
            DotNetALNavAppOperationInvoker := DotNetALNavAppOperationInvoker.ALNavAppOperationInvoker();
            OperationInvokerHasBeenCreated := true;
        end;
    end;

    local procedure CheckPermissions()
    var
        ExtensionInstallationImpl: Codeunit "Extension Installation Impl";
    begin
        ExtensionInstallationImpl.CheckPermissions();
    end;

    procedure GetAllExtensionDeploymentStatusEntries(var TempExtensionDeploymentStatus: Record "Extension Deployment Status" temporary)
    var
        NavAppTenantOperation: Record "NAV App Tenant Operation";
    begin
        if not NavAppTenantOperation.FindSet() then
            exit;
        repeat
            TempExtensionDeploymentStatus.TransferFields(NavAppTenantOperation, true);
            TempExtensionDeploymentStatus.Insert();
        until NavAppTenantOperation.Next() = 0;
    end;

    procedure GetDeploymentDetailedStatusMessageAsStream(OperationId: Guid; OutStream: OutStream)
    begin
        InitializeOperationInvoker();
        DotNetALNavAppOperationInvoker.GetOperationDetailedStatusMessageAsStream(OperationId, OutStream);
    end;

    procedure GetDeploymentDetailedStatusMessage(OperationId: Guid): Text
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetOperationDetailedStatusMessage(OperationId));
    end;

    procedure GetDeployOperationInfo(OperationId: Guid; var Version: Text; var Schedule: Text; var Publisher: Text; var AppName: Text; Description: Text)
    begin
        CheckPermissions();
        AppName := GetDeployOperationAppName(OperationId);
        if AppName = '' then
            AppName := Description;
        Publisher := GetDeployOperationAppPublisher(OperationId);
        Version := GetDeployOperationAppVersion(OperationId);
        Schedule := GetDeployOperationSchedule(OperationId);
    end;

    procedure GetDeployOperationInfo(OperationId: Guid; var Version: Text; var Publisher: Text; var AppName: Text; var AppId: Guid)
    begin
        CheckPermissions();
        AppName := GetDeployOperationAppName(OperationId);
        AppId := GetDeployOperationAppId(OperationId);
        Publisher := GetDeployOperationAppPublisher(OperationId);
        Version := GetDeployOperationAppVersion(OperationId);
    end;

    procedure GetDeployOperationAppId(OperationId: Guid): Guid
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetDeployOperationAppId(OperationId));
    end;

    procedure GetDeployOperationAppName(OperationId: Guid): Text
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetDeployOperationAppName(OperationId));
    end;

    procedure GetDeployOperationAppPublisher(OperationId: Guid): Text
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetDeployOperationAppPublisher(OperationId));
    end;

    procedure GetDeployOperationAppVersion(OperationId: Guid): Text
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetDeployOperationAppVersion(OperationId));
    end;

    procedure GetDeployOperationSchedule(OperationId: Guid): Text
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetDeployOperationSchedule(OperationId));
    end;

    procedure GetDeployOperationJobId(OperationId: Guid): Text
    begin
        InitializeOperationInvoker();
        exit(DotNetALNavAppOperationInvoker.GetDeployOperationJobId(OperationId));
    end;

    procedure GetLatestVersionPackageIdByAppId(AppId: Guid): Guid
    var
        PublishedApplication: Record "Published Application";
        NullGuid: Guid;
    begin
        PublishedApplication.SetRange(ID, AppId);
        PublishedApplication.SetRange("Tenant Visible", true);
        PublishedApplication.SetCurrentKey(Name, "Version Major", "Version Minor", "Version Build", "Version Revision");
        PublishedApplication.Ascending(false);

        if PublishedApplication.FindFirst() then
            exit(PublishedApplication."Package ID");

        exit(NullGuid);
    end;

    procedure GetCurrentlyInstalledVersionPackageIdByAppId(AppId: Guid): Guid
    var
        NavAppInstalledApp: Record "NAV App Installed App";
        NullGuid: Guid;
    begin
        if NavAppInstalledApp.Get(AppId) then
            exit(NavAppInstalledApp."Package ID");

        exit(NullGuid);
    end;

    procedure GetSpecificVersionPackageIdByAppId(AppId: Guid; Name: Text; VersionMajor: Integer; VersionMinor: Integer; VersionBuild: Integer; VersionRevision: Integer): Guid
    var
        PublishedApplication: Record "Published Application";
        NullGuid: Guid;
    begin
        if AppId = NullGuid then
            exit(NullGuid);

        PublishedApplication.SetRange(ID, AppId);
        PublishedApplication.SetRange("Tenant Visible", true);

        if Name <> '' then
            PublishedApplication.SetRange(Name, Name);
        if VersionMajor <> 0 then
            PublishedApplication.SetRange("Version Major", VersionMajor);
        if VersionMinor <> 0 then
            PublishedApplication.SetRange("Version Minor", VersionMinor);
        if VersionBuild <> 0 then
            PublishedApplication.SetRange("Version Build", VersionBuild);
        if VersionRevision <> 0 then
            PublishedApplication.SetRange("Version Revision", VersionRevision);

        if PublishedApplication.Count() <> 1 then
            exit(NullGuid);

        if PublishedApplication.FindFirst() then
            exit(PublishedApplication."Package ID");
    end;

    procedure GetExtensionLogo(AppId: Guid; var Logo: Codeunit "Temp Blob")
    var
        PublishedApplication: Record "Published Application";
        Media: Record Media;
        LogoInStream: InStream;
        LogoOutStream: OutStream;
    begin
        PublishedApplication.SetRange(ID, AppId);
        PublishedApplication.SetRange("Tenant Visible", true);

        if PublishedApplication.FindFirst() then begin
            Media.SetRange(ID, PublishedApplication.Logo.MediaId());

            if not Media.FindFirst() then
                exit;

            Media.CalcFields(Content);
            Media.Content.CreateInStream(LogoInStream);

            Logo.CreateOutStream(LogoOutStream);
            CopyStream(LogoOutStream, LogoInStream);
        end;
    end;

    procedure GetAppName(AppId: Guid) AppName: Text
    var
        PublishedApplication: Record "Published Application";
    begin
        if PublishedApplication.ReadPermission then begin
            PublishedApplication.SetRange(ID, AppId);
            PublishedApplication.SetRange("Tenant Visible", true);

            if PublishedApplication.FindFirst() then
                AppName := PublishedApplication.Name;
        end;
    end;

    procedure HandleOrphanedDataNotification(Notif: Notification)
    begin
        Page.Run(Page::"Delete Orphaned Extension Data");
    end;

    procedure MarkOrphanedDataAsReviewed(Notif: Notification)
    begin
        MarkAllOrphanedExtensionDataAsReviewed();
        Notif.Recall();
    end;

    internal procedure MarkAllOrphanedExtensionDataAsReviewed()
    var
        ExtensionDatabaseManagement: Codeunit "Extension Database Management";
    begin
        CheckPermissions();
        ExtensionDatabaseManagement.MarkAllOrphanedExtensionDataAsReviewed();
    end;

    internal procedure GetAppName(AppId: Guid; OperationId: Guid) AppName: Text
    begin
        AppName := GetAppName(AppId);

        if AppName = '' then
            AppName := GetDeployOperationAppName(OperationId);
    end;
}

Procedures, 30

NameParametersReturnsAccessObsolete
UploadExtension(InStream, Integer)public-
UnpublishExtension(Guid)Booleanpublic-
UnpublishUninstalledPerTenantExtension(Guid)public-
GetExtensionSource(Guid, var Codeunit Temp Blob, var Text)Booleanpublic-
DownloadExtensionSource(Guid)Booleanpublic-
DownloadDeploymentStatusDetails(Guid)public-
RefreshStatus(Guid)public-
ConfigureExtensionHttpClientRequestsAllowance(Text, Boolean)Booleanpublic-
GetAllExtensionDeploymentStatusEntries(var Record Extension Deployment Status)public-
GetDeploymentDetailedStatusMessageAsStream(Guid, OutStream)public-
GetDeploymentDetailedStatusMessage(Guid)Textpublic-
GetDeployOperationInfo(Guid, var Text, var Text, var Text, var Text, Text)public-
GetDeployOperationAppName(Guid)Textpublic-
GetDeployOperationAppPublisher(Guid)Textpublic-
GetDeployOperationAppVersion(Guid)Textpublic-
GetDeployOperationSchedule(Guid)Textpublic-
GetDeployOperationJobId(Guid)Textpublic-
GetLatestVersionPackageIdByAppId(Guid)Guidpublic-
GetCurrentlyInstalledVersionPackageIdByAppId(Guid)Guidpublic-
GetSpecificVersionPackageIdByAppId(Guid, Text, Integer, Integer, Integer, Integer)Guidpublic-
GetExtensionLogo(Guid, var Codeunit Temp Blob)public-
DeployAndUploadExtension(InStream, Integer, Enum Extension Deploy To, Enum Extension Sync Mode)public-
GetAppName(Guid)Textpublic-
GetDeployOperationInfo(Guid, var Text, var Text, var Text, var Guid)public-
GetDeployOperationAppId(Guid)Guidpublic-
GetAppName(Guid, Guid)Textinternal-
DeployExtension(Guid, Integer, Boolean, Text)public-
HandleOrphanedDataNotification(Notification)public-
MarkOrphanedDataAsReviewed(Notification)public-
MarkAllOrphanedExtensionDataAsReviewed()internal-