Codeunit 2500 Extension Installation Impl in 26
- App
- System Application
- Namespace
- System.Apps
Versions171819202122232425262728latest
Source in 26
src/System Application/App/Extension Management/src/ExtensionInstallationImpl.Codeunit.al422 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.Utilities;
using System.Environment.Configuration;
codeunit 2500 "Extension Installation Impl"
{
Access = Internal;
InherentEntitlements = X;
InherentPermissions = X;
Permissions = tabledata "NAV App Installed App" = rimd,
tabledata "Published Application" = rimd;
SingleInstance = false;
var
DotNetNavAppALInstaller: DotNet NavAppALInstaller;
InstallerHasBeenCreated: Boolean;
InstalledTxt: Label 'Installed';
NotInstalledTxt: Label 'Not Installed';
FullVersionStringTxt: Label '%1.%2.%3.%4', Comment = '%1=Version Major, %2=Version Minor, %3=Version build, %4=Version revision';
NoRevisionVersionStringTxt: Label '%1.%2.%3', Comment = '%1=Version Major, %2=Version Minor, %3=Version build';
NoBuildVersionStringTxt: Label '%1.%2', Comment = '%1=Version Major, %2=Version Minor';
PermissionErr: Label 'You do not have the required permissions to install the selected app. Contact your Partner or system administrator to install the app or assign you permissions.';
DependenciesFoundQst: Label 'The extension %1 has a dependency on one or more extensions: %2. \ \Do you want to install %1 and all of its dependencies?', Comment = '%1=name of app, %2=semicolon separated list of uninstalled dependencies';
DependentsFoundQst: Label 'The extension %1 is a dependency for one or more extensions: %2. \ \Do you want to uninstall %1 and all of its dependents?', Comment = '%1=name of app, %2=semicolon separated list of installed dependents';
AlreadyInstalledMsg: Label 'The extension %1 is already installed.', Comment = '%1=name of app';
AlreadyUninstalledMsg: Label 'The extension %1 is not installed.', Comment = '%1=name of app';
RestartActivityUninstallMsg: Label 'The %1 extension was successfully uninstalled. All active users must sign out and sign in again to see the navigation changes.', Comment = 'Indicates that users need to restart their activity to pick up new menusuite items. %1=Name of Extension';
ClearExtensionSchemaQst: Label 'Enabling Delete Extension Data will delete the tables that contain data for the %1 extension and all of its dependents on uninstall. This action cannot be undone. Do you want to continue?', Comment = '%1=name of app';
ClearExtensionSchemaMsg: Label 'You have selected to delete extension data for the %1 extension and all of its dependents: %2. Continuing uninstall will delete the tables that contain data for the %1 extension and all of its dependents. This action cannot be undone. Do you want to continue?', Comment = '%1=name of app ,%2= all dependent extensions';
ClearExtensionSchemaOrphanMsg: Label 'You have selected to delete extension data for the %1 extension. Continuing deletion will delete the tables that contain data for the %1 extension and all of its dependents. This action cannot be undone. Do you want to continue?', Comment = '%1=name of app';
NotSufficientPermissionErr: Label 'You do not have sufficient permissions to manage extensions. Please contact your administrator.';
InstallationBestPracticesUrlLbl: Label 'https://go.microsoft.com/fwlink/?linkid=2138922', Comment = 'link to the best practices and tips about the installing and publishing a new extension.', Locked = true;
DisclaimerUrlLbl: Label 'https://go.microsoft.com/fwlink/?linkid=2193002&clcid=0x409', Comment = 'link to the Business Central PTE disclaimer.', Locked = true;
PrivacyPolicyUrlLbl: Label 'https://go.microsoft.com/fwlink/?LinkId=521839', Comment = 'link to the privacy and cookies docs.', Locked = true;
ExtensionNotInstalledErr: Label 'The %1 app is not installed.', Comment = '%1 = name of extension';
procedure IsInstalledByPackageId(PackageID: Guid): Boolean
var
[SecurityFiltering(SecurityFilter::Ignored)]
NAVAppInstalledApp: Record "NAV App Installed App";
begin
// Checks whether the user is entitled to make extension changes.
if (not NAVAppInstalledApp.ReadPermission()) or (not NAVAppInstalledApp.WritePermission()) then
Error(PermissionErr);
NAVAppInstalledApp.SetRange("Package ID", PackageID);
exit(not NAVAppInstalledApp.IsEmpty());
end;
procedure IsInstalledByAppId(AppId: Guid): Boolean
var
[SecurityFiltering(SecurityFilter::Ignored)]
NAVAppInstalledApp: Record "NAV App Installed App";
begin
// Checks whether the user is entitled to make extension changes.
if (not NAVAppInstalledApp.ReadPermission()) or (not NAVAppInstalledApp.WritePermission()) then
Error(PermissionErr);
exit(NAVAppInstalledApp.Get(AppID));
end;
procedure InstallExtension(PackageId: Guid; Lcid: Integer; IsUIEnabled: Boolean): Boolean
var
PublishedApplication: Record "Published Application";
begin
CheckPermissions();
if IsUIEnabled = true then
exit(InstallExtensionWithConfirmDialog(PackageId, Lcid));
PublishedApplication.SetRange("Package ID", PackageId);
PublishedApplication.SetRange("Tenant Visible", true);
if PublishedApplication.IsEmpty() then
exit(false);
exit(InstallExtensionSilently(PackageId, Lcid));
end;
procedure InstallExtensionSilently(PackageID: Guid; Lcid: Integer): Boolean
begin
CheckPermissions();
AssertIsInitialized();
DotNetNavAppALInstaller.ALInstallNavApp(PackageID, Lcid);
if not IsInstalledByPackageId(PackageID) then
exit(false);
exit(true);
end;
procedure InstallExtensionWithConfirmDialog(PackageId: Guid; Lcid: Integer): Boolean
var
PublishedApplication: Record "Published Application";
ConfirmManagement: Codeunit "Confirm Management";
Dependencies: Text;
CanChange: Boolean;
begin
CheckPermissions();
PublishedApplication.SetRange("Package ID", PackageId);
PublishedApplication.SetRange("Tenant Visible", true);
if not PublishedApplication.FindFirst() then
exit(false);
if IsInstalledByPackageId(PackageId) then begin
Message(StrSubstNo(AlreadyInstalledMsg, PublishedApplication.Name));
exit(false);
end;
Dependencies := GetDependenciesForExtensionToInstall(PackageId);
Dependencies := GetNonExcludedApps(Dependencies);
CanChange := true;
if StrLen(Dependencies) <> 0 then
CanChange := ConfirmManagement.GetResponse(StrSubstNo(DependenciesFoundQst, PublishedApplication.Name, Dependencies), false);
if CanChange then
InstallExtensionSilently(PackageId, Lcid);
// If successfully installed, message users to restart activity for menusuites
if not IsInstalledByPackageId(PackageId) then
exit(false);
exit(true);
end;
procedure GetExtensionInstalledDisplayString(PackageId: Guid): Text
begin
if IsInstalledByPackageId(PackageId) then
exit(InstalledTxt);
exit(NotInstalledTxt);
end;
procedure GetExtensionInstalledDisplayString(Installed: Boolean): Text
begin
if Installed then
exit(InstalledTxt);
exit(NotInstalledTxt);
end;
procedure GetDependenciesForExtensionToInstall(PackageID: Guid): Text
begin
AssertIsInitialized();
exit(DotNetNavAppALInstaller.ALGetAppDependenciesToInstallString(PackageID));
end;
procedure GetDependentForExtensionToUninstall(PackageID: Guid): Text
begin
AssertIsInitialized();
exit(DotNetNavAppALInstaller.ALGetDependentAppsToUninstallString(PackageID));
end;
procedure RunExtensionSetup(AppId: Guid)
var
GuidedExperience: Codeunit "Guided Experience";
begin
if not IsInstalledByAppId(AppId) then
Error(ExtensionNotInstalledErr);
GuidedExperience.RunExtensionSetup(AppId);
end;
local procedure AssertIsInitialized()
begin
if not InstallerHasBeenCreated then begin
DotNetNavAppALInstaller := DotNetNavAppALInstaller.NavAppALInstaller();
InstallerHasBeenCreated := true;
end;
end;
procedure CheckPermissions()
begin
if not CanManageExtensions() then
Error(NotSufficientPermissionErr);
end;
procedure CanManageExtensions(): Boolean
var
ApplicationObjectMetadata: Record "Application Object Metadata";
begin
exit(ApplicationObjectMetadata.ReadPermission());
end;
procedure UninstallExtension(PackageID: Guid; IsUIEnabled: Boolean): Boolean
begin
exit(UninstallExtension(PackageID, IsUIEnabled, false));
end;
procedure UninstallExtension(PackageID: Guid; IsUIEnabled: Boolean; ClearSchema: Boolean): Boolean
var
PublishedApplication: Record "Published Application";
begin
CheckPermissions();
if IsUIEnabled = true then
exit(UninstallExtensionWithConfirmDialog(PackageID, ClearSchema, ClearSchema));
PublishedApplication.SetRange("Package ID", PackageID);
PublishedApplication.SetRange("Tenant Visible", true);
if PublishedApplication.IsEmpty() then
exit(false);
exit(UninstallExtensionSilently(PackageID, ClearSchema, ClearSchema));
end;
procedure DeleteOrphanData(AppId: Guid; ExtensionName: Text): Boolean
var
ConfirmManagement: Codeunit "Confirm Management";
begin
CheckPermissions();
if not ConfirmManagement.GetResponse(StrSubstNo(ClearExtensionSchemaOrphanMsg, ExtensionName), false) then exit(false);
exit(UninstallExtensionSilentlyByAppId(AppId, false, true));
end;
procedure RunOrphanDeletion(OrphanedApplication: Record "Extension Database Snapshot"): Boolean
var
OrphanedExtensionDetails: Page "Extn. Orphaned App Details";
begin
OrphanedExtensionDetails.SetRecord(OrphanedApplication);
exit(OrphanedExtensionDetails.RunModal() = Action::OK);
end;
local procedure UninstallExtensionSilently(PackageID: Guid; ClearData: Boolean; ClearSchema: Boolean): Boolean
begin
CheckPermissions();
AssertIsInitialized();
DotNetNavAppALInstaller.ALUninstallNavApp(PackageID, ClearData, ClearSchema);
if IsInstalledByPackageId(PackageID) then
exit(false);
exit(true);
end;
local procedure UninstallExtensionSilentlyByAppId(AppId: Guid; ClearData: Boolean; ClearSchema: Boolean): Boolean
begin
CheckPermissions();
AssertIsInitialized();
DotNetNavAppALInstaller.ALUninstallNavAppByAppId(AppId, ClearData, ClearSchema);
if IsInstalledByAppId(AppId) then
exit(false);
exit(true);
end;
procedure UninstallExtensionWithConfirmDialog(PackageId: Guid; ClearData: Boolean; ClearSchema: Boolean): Boolean
var
PublishedApplication: Record "Published Application";
ConfirmManagement: Codeunit "Confirm Management";
Dependents: Text;
begin
CheckPermissions();
PublishedApplication.SetRange("Package ID", PackageId);
PublishedApplication.SetRange("Tenant Visible", true);
if not PublishedApplication.FindFirst() then
exit(false);
if not IsInstalledByPackageId(PackageId) then begin
Message(StrSubstNo(AlreadyUninstalledMsg, PublishedApplication.Name));
exit(false);
end;
Dependents := GetDependentForExtensionToUninstall(PackageId);
Dependents := GetNonExcludedApps(Dependents);
if StrLen(Dependents) <> 0 then
if not ConfirmManagement.GetResponse(
StrSubstNo(DependentsFoundQst, PublishedApplication.Name, Dependents), false)
then
exit(false);
if ClearSchema then
if not ConfirmManagement.GetResponse(
StrSubstNo(ClearExtensionSchemaMsg, PublishedApplication.Name, Dependents), false)
then
exit(false);
UninstallExtensionSilently(PackageId, ClearData, ClearSchema);
// If successfully uninstalled, message users to restart activity for menusuites
if not IsInstalledByPackageId(PackageId) then
Message(StrSubstNo(RestartActivityUninstallMsg, PublishedApplication.Name))
else
exit(false);
exit(true);
end;
procedure GetClearExtensionSchemaConfirmation(PackageId: Guid; var ClearSchema: Boolean)
var
PublishedApplication: Record "Published Application";
ConfirmManagement: Codeunit "Confirm Management";
begin
if not ClearSchema then
exit;
PublishedApplication.SetRange("Package ID", PackageId);
PublishedApplication.SetRange("Tenant Visible", true);
if not PublishedApplication.FindFirst() then
ClearSchema := false
else
ClearSchema := ConfirmManagement.GetResponse(
StrSubstNo(ClearExtensionSchemaQst, PublishedApplication.Name), false);
end;
procedure GetNonExcludedApps(Dependents: Text): Text
var
newDependentsText: Text;
auxString: Text;
pos: Integer;
begin
newDependentsText := '';
auxString := '';
pos := StrPos(Dependents, ';');
if pos = 0 then
if StrPos(Dependents, '_Exclude_') > 0 then
exit('')
else
exit(Dependents);
while pos > 0 do begin
auxString := CopyStr(Dependents, 1, pos);
if StrPos(auxString, '_Exclude_') = 0 then
newDependentsText := newDependentsText + auxString;
Dependents := CopyStr(Dependents, pos + 1);
pos := StrPos(Dependents, ';');
end;
exit(newDependentsText);
end;
procedure GetVersionDisplayString(PublishedApplication: Record "Published Application"): Text
begin
if PublishedApplication."Version Build" <= -1 then
exit(StrSubstNo(NoBuildVersionStringTxt, PublishedApplication."Version Major", PublishedApplication."Version Minor"));
if PublishedApplication."Version Revision" <= -1 then
exit(StrSubstNo(NoRevisionVersionStringTxt, PublishedApplication."Version Major", PublishedApplication."Version Minor", PublishedApplication."Version Build"));
exit(StrSubstNo(FullVersionStringTxt, PublishedApplication."Version Major",
PublishedApplication."Version Minor", PublishedApplication."Version Build", PublishedApplication."Version Revision"));
end;
procedure GetInstallationBestPracticesURL(): Text;
begin
exit(InstallationBestPracticesUrlLbl);
end;
procedure GetDisclaimerURL(): Text;
begin
exit(DisclaimerUrlLbl);
end;
procedure GetPrivacyAndCookeisURL(): Text;
begin
exit(PrivacyPolicyUrlLbl);
end;
procedure RunExtensionInstallation(PublishedApplication: Record "Published Application"): Boolean
var
ExtensionDetails: Page "Extension Details";
begin
ExtensionDetails.SetRecord(PublishedApplication);
ExtensionDetails.Run();
exit(ExtensionDetails.Editable());
end;
procedure AllowsDebug(ResourceProtectionPolicy: Integer): Boolean
begin
exit(HasPolicy(ResourceProtectionPolicy, 1));
end;
procedure AllowsDownloadSource(ResourceProtectionPolicy: Integer): Boolean
begin
exit(HasPolicy(ResourceProtectionPolicy, 2));
end;
procedure AllowsDownloadSourceInSymbols(ResourceProtectionPolicy: Integer): Boolean
begin
exit(HasPolicy(ResourceProtectionPolicy, 4));
end;
local procedure HasPolicy(ResourceProtectionPolicy: Integer; PolicyToTestFor: Integer): Boolean
var
Remaining: Integer;
Tester: Integer;
Result: Integer;
NextFlag: Integer;
begin
Result := 0;
Remaining := ResourceProtectionPolicy;
Tester := PolicyToTestFor;
NextFlag := 1;
while (Remaining > 0) and (Tester > 0) do begin
if ((Remaining mod 2) = 1) and ((Tester mod 2) = 1) then
Result += NextFlag;
Remaining := Remaining div 2;
Tester := Tester div 2;
NextFlag := NextFlag * 2;
end;
exit(Result = PolicyToTestFor);
end;
}
Procedures, 27
| Name | Parameters | Returns | Access | Obsolete |
|---|---|---|---|---|
| IsInstalledByPackageId | (Guid) | Boolean | public | - |
| IsInstalledByAppId | (Guid) | Boolean | public | - |
| InstallExtension | (Guid, Integer, Boolean) | Boolean | public | - |
| InstallExtensionSilently | (Guid, Integer) | Boolean | public | - |
| InstallExtensionWithConfirmDialog | (Guid, Integer) | Boolean | public | - |
| GetExtensionInstalledDisplayString | (Guid) | Text | public | - |
| GetDependenciesForExtensionToInstall | (Guid) | Text | public | - |
| GetDependentForExtensionToUninstall | (Guid) | Text | public | - |
| CanManageExtensions | () | Boolean | public | - |
| UninstallExtension | (Guid, Boolean) | Boolean | public | - |
| UninstallExtensionWithConfirmDialog | (Guid, Boolean, Boolean) | Boolean | public | - |
| GetClearExtensionSchemaConfirmation | (Guid, var Boolean) | public | - | |
| GetNonExcludedApps | (Text) | Text | public | - |
| GetVersionDisplayString | (Record Published Application) | Text | public | - |
| GetInstallationBestPracticesURL | () | Text | public | - |
| RunExtensionInstallation | (Record Published Application) | Boolean | public | - |
| GetExtensionInstalledDisplayString | (Boolean) | Text | public | - |
| UninstallExtension | (Guid, Boolean, Boolean) | Boolean | public | - |
| GetDisclaimerURL | () | Text | public | - |
| GetPrivacyAndCookeisURL | () | Text | public | - |
| RunExtensionSetup | (Guid) | public | - | |
| CheckPermissions | () | public | - | |
| DeleteOrphanData | (Guid, Text) | Boolean | public | - |
| RunOrphanDeletion | (Record Extension Database Snapshot) | Boolean | public | - |
| AllowsDebug | (Integer) | Boolean | public | - |
| AllowsDownloadSource | (Integer) | Boolean | public | - |
| AllowsDownloadSourceInSymbols | (Integer) | Boolean | public | - |