Page 2500 Extension Management in 27
- App
- System Application
- Namespace
- System.Apps
- Source table
- 2000000206
Versions171819202122232425262728latest
Source in 27
src/System Application/App/Extension Management/src/ExtensionManagement.Page.al495 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.Agents;
using System.Environment;
using System.Environment.Configuration;
using System.Integration;
/// <summary>
/// Lists the available extensions, and provides features for managing them.
/// </summary>
page 2500 "Extension Management"
{
Caption = 'Extension Management';
AdditionalSearchTerms = 'app,add-in,customize,plug-in,appsource';
ApplicationArea = All;
DeleteAllowed = false;
Editable = false;
InsertAllowed = false;
ModifyAllowed = false;
PageType = List;
RefreshOnActivate = true;
SourceTable = "Published Application";
SourceTableView = sorting(Name)
order(ascending)
where(Name = filter(<> '_Exclude_*'),
"Package Type" = filter(= Extension | Designer),
"Tenant Visible" = const(true));
UsageCategory = Administration;
ContextSensitiveHelpPage = 'ui-extensions';
Permissions = tabledata "Published Application" = r;
layout
{
area(Content)
{
repeater(Group)
{
Editable = false;
field(Logo; Rec.Logo)
{
Caption = 'Logo';
ToolTip = 'Specifies the logo of the extension, such as the logo of the service provider.';
}
field(Name; Rec.Name)
{
ToolTip = 'Specifies the name of the extension.';
}
field(Publisher; Rec.Publisher)
{
ToolTip = 'Specifies the publisher of the extension.';
}
field(Version; VersionDisplay)
{
Caption = 'Version';
ToolTip = 'Specifies the version of the extension.';
}
field("Is Installed"; IsInstalled)
{
Caption = 'Is Installed';
Style = Favorable;
StyleExpr = InfoStyle;
ToolTip = 'Specifies whether the extension is installed.';
}
field("Source"; AllowsDownloadSource)
{
Caption = 'Source';
StyleExpr = AllowsDownloadSourceStyleExpr;
ToolTip = 'Specifies whether the extension allows the source to be downloaded.';
OptionCaption = ' ,Yes,No';
}
field("Published As"; Rec."Published As")
{
Caption = 'Published As';
ToolTip = 'Specifies whether the extension is published as a per-tenant, development, or a global extension.';
}
label(Control18)
{
Enabled = IsSaaS;
HideValue = true;
ShowCaption = false;
Caption = '';
Style = Favorable;
StyleExpr = true;
ToolTip = 'Specifies a spacer for ''Brick'' view mode.';
Visible = not IsOnPremDisplay;
}
}
}
}
actions
{
area(Processing)
{
group(ActionGroup13)
{
Caption = 'Process';
action(View)
{
Caption = 'View';
Enabled = ActionsEnabled;
Image = View;
ShortcutKey = 'Return';
ToolTip = 'View extension details.';
RunObject = page "Extension Settings";
RunPageLink = "App ID" = field(ID);
Scope = Repeater;
}
action(Install)
{
Caption = 'Install';
Enabled = ActionsEnabled and (not IsInstalled);
Image = NewRow;
Scope = Repeater;
ToolTip = 'Install the extension for the current tenant.';
trigger OnAction()
begin
if ExtensionInstallationImpl.RunExtensionInstallation(Rec) then
CurrPage.Update(false);
end;
}
action(Uninstall)
{
Caption = 'Uninstall';
Enabled = ActionsEnabled and IsInstalled;
Image = RemoveLine;
Scope = Repeater;
ToolTip = 'Remove the extension from the current tenant.';
trigger OnAction()
begin
if ExtensionInstallationImpl.RunExtensionInstallation(Rec) then
CurrPage.Update(false);
end;
}
action(Unpublish)
{
Caption = 'Unpublish';
Enabled = ActionsEnabled and IsTenantExtension and (not IsInstalled);
Image = RemoveLine;
Scope = Repeater;
ToolTip = 'Unpublish the extension from the tenant.';
trigger OnAction()
begin
if ExtensionInstallationImpl.IsInstalledByPackageId(Rec."Package ID") then begin
Message(CannotUnpublishIfInstalledMsg, Rec.Name);
exit;
end;
ExtensionOperationImpl.UnpublishUninstalledPerTenantExtension(Rec."Package ID");
end;
}
action(SetupApp)
{
Caption = 'Set up';
Image = SetupList;
Enabled = ActionsEnabled and IsInstalled;
Scope = Repeater;
ToolTip = 'Runs the setup page that has been marked as primary for the selected app.';
trigger OnAction()
var
PublishedApplication: Record "Published Application";
begin
CurrPage.SetSelectionFilter(PublishedApplication);
if PublishedApplication.Count > 1 then
Error(MultiSelectNotSupportedErr);
ExtensionInstallationImpl.RunExtensionSetup(Rec.ID);
end;
}
action("Download Source")
{
Caption = 'Download Source';
Enabled = IsTenantExtension;
Image = ExportFile;
Scope = Repeater;
ToolTip = 'Download the source code for the extension.';
trigger OnAction()
begin
ExtensionOperationImpl.DownloadExtensionSource(Rec."Package ID");
end;
}
action("Learn More")
{
Caption = 'Learn More';
Visible = HelpActionVisible;
Enabled = ActionsEnabled;
Image = Info;
Scope = Repeater;
ToolTip = 'View information from the extension provider.';
trigger OnAction()
begin
HyperLink(Rec.Help);
end;
}
action(Refresh)
{
Caption = 'Refresh';
Image = Refresh;
ToolTip = 'Refresh the list of extensions.';
trigger OnAction()
begin
ActionsEnabled := false;
CurrPage.Update(false);
end;
}
#if not CLEAN25
action("Extension Marketplace")
{
Caption = 'Extension Marketplace';
Enabled = IsSaaS;
Image = NewItem;
ToolTip = 'Browse the extension marketplace for new extensions to install.';
Visible = false;
ObsoleteState = Pending;
ObsoleteReason = 'This action will be obsoleted. Microsoft AppSource apps feature will replace the Extension Marketplace.';
ObsoleteTag = '25.0';
trigger OnAction()
begin
Hyperlink('https://aka.ms/bcappsource');
end;
}
#endif
action("Upload Extension")
{
Caption = 'Upload Extension';
Image = Import;
RunObject = page "Upload And Deploy Extension";
ToolTip = 'Upload an extension to your application.';
Ellipsis = true;
Visible = IsSaaS;
}
action("Deployment Status")
{
Caption = 'Installation Status';
Image = Status;
RunObject = page "Extension Deployment Status";
ToolTip = 'Check status for upload process for extensions.';
Visible = IsSaaS;
}
action("Delete Orphaned Extension Data")
{
Caption = 'Delete Orphaned Extension Data';
Image = Delete;
RunObject = page "Delete Orphaned Extension Data";
ToolTip = 'Delete the data of orphaned extensions.';
}
}
group("Develop in VS Code")
{
Caption = 'Develop in VS Code';
ToolTip = 'Set of actions to configure your local AL project in Visual Studio Code for extension development.';
action("Open Source in VS Code")
{
Caption = 'Open source from Git';
Enabled = IsSourceSpecificationAvailable;
Image = Open;
Scope = Repeater;
ToolTip = 'Open the source code for the extension based on the source control information.';
trigger OnAction()
begin
VsCodeIntegration.OpenExtensionSourceInVSCode(Rec);
end;
}
action("Update configurations")
{
AccessByPermission = System "Tools, Zoom" = X;
Caption = 'Generate launch configurations';
Image = Setup;
ToolTip = 'Generates the launch configurations in your local AL project in Visual Studio Code for extension development in this environment.';
trigger OnAction()
begin
VSCodeIntegration.UpdateConfigurationsInVSCode();
end;
}
group("Get as dependencies")
{
Caption = 'Get selected as dependencies';
ToolTip = 'Set of actions to add the selected extensions as dependencies to your local project in Visual Studio Code.';
action("Download dependencies")
{
AccessByPermission = System "Tools, Zoom" = X;
Caption = 'Download in VS Code';
Image = Download;
ToolTip = 'Adds the selected extensions to your local project''s dependencies in Visual Studio Code, and downloads the symbols for them.';
trigger OnAction()
var
PublishedApplication: Record "Published Application";
begin
CurrPage.SetSelectionFilter(PublishedApplication);
VSCodeIntegration.UpdateDependenciesInVSCode(PublishedApplication);
end;
}
action("Show dependencies")
{
AccessByPermission = System "Tools, Zoom" = X;
ApplicationArea = All;
Caption = 'Show and copy';
Image = Copy;
ToolTip = 'Formats the selected dependencies as a json array and displays them in a dialog window.';
trigger OnAction()
var
PublishedApplication: Record "Published Application";
begin
CurrPage.SetSelectionFilter(PublishedApplication);
Message(VSCodeIntegration.GetDependenciesAsJson(PublishedApplication));
end;
}
}
}
}
area(Promoted)
{
group(Category_Category5)
{
Caption = 'Manage', Comment = 'Generated from the PromotedActionCategories property index 4.';
#if not CLEAN25
#pragma warning disable AL0432
actionref("Extension Marketplace_Promoted"; "Extension Marketplace")
#pragma warning restore AL0432
{
ObsoleteState = Pending;
ObsoleteReason = 'This action will be obsoleted. Microsoft AppSource apps feature will replace the Extension Marketplace.';
ObsoleteTag = '25.0';
Visible = false;
}
#endif
actionref("Upload Extension_Promoted"; "Upload Extension") { }
actionref("Deployment Status_Promoted"; "Deployment Status") { }
actionref(View_Promoted; View) { }
actionref(Install_Promoted; Install) { }
actionref(Uninstall_Promoted; Uninstall) { }
actionref(Unpublish_Promoted; Unpublish) { }
actionref(SetupApp_Promoted; SetupApp) { }
actionref("Download Source_Promoted"; "Download Source") { }
actionref("Learn More_Promoted"; "Learn More") { }
actionref(Refresh_Promoted; Refresh) { }
}
group("Develop in VS Code_Promoted")
{
Caption = 'Develop in VS Code';
actionref("Open Source in VS Code_Promoted"; "Open Source in VS Code") { }
actionref("Update configurations_Promoted"; "Update configurations") { }
group("Get as dependencies_Promoted")
{
Caption = 'Get selected as dependencies';
actionref("Download dependencies_Promoted"; "Download dependencies") { }
actionref("Show dependencies_Promoted"; "Show dependencies") { }
}
}
}
}
trigger OnAfterGetRecord()
begin
// Reenable page actions when record has been loaded/selected
ActionsEnabled := true;
DetermineExtensionConfigurations();
VersionDisplay := ExtensionInstallationImpl.GetVersionDisplayString(Rec);
SetInfoStyleForIsInstalled();
end;
trigger OnAfterGetCurrRecord()
begin
HelpActionVisible := StrLen(Rec.Help) > 0;
end;
trigger OnOpenPage()
var
AgentUtilities: Codeunit "Agent Utilities";
begin
AgentUtilities.BlockPageFromBeingOpenedByAgent();
DetermineEnvironmentConfigurations();
SetExtensionManagementFilter();
if not IsInstallAllowed then
CurrPage.Caption(SaaSCaptionTxt);
// Temporary disable the page actions until extension is loaded/selected (OnAfterGetRecord)
ActionsEnabled := false;
HelpActionVisible := false;
end;
var
ExtensionInstallationImpl: Codeunit "Extension Installation Impl";
ExtensionOperationImpl: Codeunit "Extension Operation Impl";
VsCodeIntegration: Codeunit "VS Code Integration";
AllowsDownloadSource: Option " ","Yes","No";
VersionDisplay: Text;
AllowsDownloadSourceStyleExpr: Text;
ActionsEnabled: Boolean;
IsSaaS: Boolean;
SaaSCaptionTxt: Label 'Installed Extensions', Comment = 'The caption to display when on SaaS';
IsTenantExtension: Boolean;
CannotUnpublishIfInstalledMsg: Label 'The extension %1 cannot be unpublished because it is installed.', Comment = '%1 = name of extension';
MultiSelectNotSupportedErr: Label 'Multi-select is not supported on this action';
IsMarketplaceEnabled: Boolean;
IsOnPremDisplay: Boolean;
IsInstalled: Boolean;
IsInstallAllowed: Boolean;
InfoStyle: Boolean;
HelpActionVisible: Boolean;
IsSourceSpecificationAvailable: Boolean;
protected procedure IsSaasEnvironment(): boolean
begin
exit(IsSaaS)
end;
protected procedure IsOnPremDisplayTarget(): boolean
begin
exit(IsOnPremDisplay)
end;
local procedure SetExtensionManagementFilter()
begin
// Set installed filter if we are not displaying like on-prem
Rec.FilterGroup(2);
if not IsInstallAllowed then
Rec.SetRange("PerTenant Or Installed", true);
Rec.FilterGroup(0);
end;
local procedure DetermineEnvironmentConfigurations()
var
EnvironmentInformation: Codeunit "Environment Information";
ExtensionMarketplace: Codeunit "Extension Marketplace";
ServerSetting: Codeunit "Server Setting";
IsSaaSInstallAllowed: Boolean;
begin
IsSaaS := EnvironmentInformation.IsSaaS();
IsSaaSInstallAllowed := ServerSetting.GetEnableSaaSExtensionInstallSetting();
IsMarketplaceEnabled := ExtensionMarketplace.IsMarketplaceEnabled();
// Composed configurations for the simplicity of representation
IsOnPremDisplay := not IsMarketplaceEnabled or not IsSaaS;
IsInstallAllowed := IsOnPremDisplay or IsSaaSInstallAllowed;
end;
local procedure DetermineExtensionConfigurations()
begin
// Determining Record and Styling Configurations
IsInstalled := ExtensionInstallationImpl.IsInstalledByPackageId(Rec."Package ID");
IsTenantExtension := Rec."Published As" <> Rec."Published As"::Global;
AllowsDownloadSource := AllowsDownloadSource::" ";
if IsTenantExtension then
if ExtensionInstallationImpl.AllowsDownloadSource(Rec."Resource Exposure Policy") then begin
AllowsDownloadSource := AllowsDownloadSource::Yes;
AllowsDownloadSourceStyleExpr := Format(PageStyle::Favorable);
end else begin
AllowsDownloadSource := AllowsDownloadSource::No;
AllowsDownloadSourceStyleExpr := Format(PageStyle::Unfavorable);
end;
IsSourceSpecificationAvailable := StrLen(Rec."Source Repository Url") > 0;
end;
local procedure SetInfoStyleForIsInstalled()
begin
InfoStyle := IsInstalled and IsInstallAllowed;
end;
}Procedures, 2
| Name | Parameters | Returns | Access | Obsolete |
|---|---|---|---|---|
| IsSaasEnvironment | () | Boolean | protected | - |
| IsOnPremDisplayTarget | () | Boolean | protected | - |