diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al b/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al
index 4d3c8eadd0..f322325fb9 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al
@@ -35,7 +35,7 @@ page 7770 "Copilot Cap. Early Preview"
Editable = false;
Width = 30;
}
- field(Status; Rec.Status)
+ field(Status; Rec.EvaluateStatus())
{
ApplicationArea = All;
Caption = 'Status';
@@ -90,6 +90,8 @@ page 7770 "Copilot Cap. Early Preview"
trigger OnAction()
begin
if Dialog.Confirm(ActivateEarlyPreviewTxt, false) then begin
+ if not Rec.EnsurePrivacyNoticesApproved() then
+ exit;
Rec.Status := Rec.Status::Active;
Rec.Modify(true);
@@ -169,17 +171,19 @@ page 7770 "Copilot Cap. Early Preview"
local procedure SetStatusStyle()
begin
- if (Rec.Status = Rec.Status::Active) then
+ if (Rec.EvaluateStatus() = Rec.Status::Active) then
StatusStyleExpr := 'Favorable'
else
StatusStyleExpr := '';
end;
local procedure SetActionsEnabled()
+ var
+ CopilotCapability: Codeunit "Copilot Capability";
begin
if CopilotCapabilityImpl.IsAdmin() then begin
ActionsEnabled := (Rec.Capability.AsInteger() <> 0) and DataMovementEnabled;
- CapabilityEnabled := Rec.Status = Rec.Status::Active;
+ CapabilityEnabled := CopilotCapability.IsCapabilityActive(Rec.Capability, Rec."App Id");
end
else begin
ActionsEnabled := false;
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al
index c0fbbc521a..b76b8e4350 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al
@@ -35,7 +35,7 @@ page 7774 "Copilot Capabilities GA"
Editable = false;
Width = 30;
}
- field(Status; Rec.Status)
+ field(Status; Rec.EvaluateStatus())
{
ApplicationArea = All;
Caption = 'Status';
@@ -89,6 +89,9 @@ page 7774 "Copilot Capabilities GA"
trigger OnAction()
begin
+ if not Rec.EnsurePrivacyNoticesApproved() then
+ exit;
+
Rec.Status := Rec.Status::Active;
Rec.Modify(true);
@@ -179,17 +182,19 @@ page 7774 "Copilot Capabilities GA"
local procedure SetStatusStyle()
begin
- if (Rec.Status = Rec.Status::Active) then
+ if (Rec.EvaluateStatus() = Rec.Status::Active) then
StatusStyleExpr := 'Favorable'
else
StatusStyleExpr := '';
end;
local procedure SetActionsEnabled()
+ var
+ CopilotCapability: Codeunit "Copilot Capability";
begin
if CopilotCapabilityImpl.IsAdmin() then begin
ActionsEnabled := (Rec.Capability.AsInteger() <> 0) and DataMovementEnabled;
- CapabilityEnabled := Rec.Status = Rec.Status::Active;
+ CapabilityEnabled := CopilotCapability.IsCapabilityActive(Rec.Capability, Rec."App Id");
end
else begin
ActionsEnabled := false;
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al
index dc3da9694c..38e90454e1 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al
@@ -35,7 +35,7 @@ page 7773 "Copilot Capabilities Preview"
Editable = false;
Width = 30;
}
- field(Status; Rec.Status)
+ field(Status; Rec.EvaluateStatus())
{
ApplicationArea = All;
Caption = 'Status';
@@ -89,6 +89,9 @@ page 7773 "Copilot Capabilities Preview"
trigger OnAction()
begin
+ if not Rec.EnsurePrivacyNoticesApproved() then
+ exit;
+
Rec.Status := Rec.Status::Active;
Rec.Modify(true);
@@ -166,17 +169,19 @@ page 7773 "Copilot Capabilities Preview"
local procedure SetStatusStyle()
begin
- if (Rec.Status = Rec.Status::Active) then
+ if (Rec.EvaluateStatus() = Rec.Status::Active) then
StatusStyleExpr := 'Favorable'
else
StatusStyleExpr := '';
end;
local procedure SetActionsEnabled()
+ var
+ CopilotCapability: Codeunit "Copilot Capability";
begin
if CopilotCapabilityImpl.IsAdmin() then begin
ActionsEnabled := (Rec.Capability.AsInteger() <> 0) and DataMovementEnabled;
- CapabilityEnabled := Rec.Status = Rec.Status::Active;
+ CapabilityEnabled := CopilotCapability.IsCapabilityActive(Rec.Capability, Rec."App Id");
end
else begin
ActionsEnabled := false;
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapability.Codeunit.al b/src/System Application/App/AI/src/Copilot/CopilotCapability.Codeunit.al
index 5f4b18bec3..0f6215c940 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapability.Codeunit.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapability.Codeunit.al
@@ -118,4 +118,15 @@ codeunit 7773 "Copilot Capability"
begin
exit(CopilotCapabilityImpl.IsCapabilityActive(CopilotCapability, AppId));
end;
+
+ ///
+ /// Event that is raised to specify if a Copilot capability depends on any additional privacy notices.
+ ///
+ /// The capability.
+ /// The app id associated with the capability.
+ /// The list of required privacy notices for the capability to be enabled.
+ [IntegrationEvent(false, false)]
+ procedure OnGetRequiredPrivacyNotices(CopilotCapability: Enum "Copilot Capability"; AppId: Guid; var RequiredPrivacyNotices: List of [Code[50]])
+ begin
+ end;
}
\ No newline at end of file
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al
index 20f2117a1e..30504e7049 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al
@@ -133,13 +133,27 @@ codeunit 7774 "Copilot Capability Impl"
procedure IsCapabilityActive(CopilotCapability: Enum "Copilot Capability"; AppId: Guid): Boolean
var
CopilotSettings: Record "Copilot Settings";
+ CopilotCapabilityCU: Codeunit "Copilot Capability";
+ PrivacyNotice: Codeunit "Privacy Notice";
+ RequiredPrivacyNotices: List of [Code[50]];
+ RequiredPrivacyNotice: Code[50];
begin
CopilotSettings.ReadIsolation(IsolationLevel::ReadCommitted);
CopilotSettings.SetLoadFields(Status);
if not CopilotSettings.Get(CopilotCapability, AppId) then
exit(false);
- exit(CopilotSettings.Status = Enum::"Copilot Status"::Active);
+ CopilotCapabilityCU.OnGetRequiredPrivacyNotices(CopilotCapability, AppId, RequiredPrivacyNotices);
+
+ if (CopilotSettings.Status <> Enum::"Copilot Status"::Active) or (RequiredPrivacyNotices.Count() <= 0) then
+ exit(CopilotSettings.Status = Enum::"Copilot Status"::Active);
+
+ // check privacy notices
+ foreach RequiredPrivacyNotice in RequiredPrivacyNotices do
+ if (PrivacyNotice.GetPrivacyNoticeApprovalState(RequiredPrivacyNotice, true) <> Enum::"Privacy Notice Approval State"::Agreed) then
+ exit(false);
+
+ exit(true);
end;
procedure SendActivateTelemetry(CopilotCapability: Enum "Copilot Capability"; AppId: Guid)
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilityInstall.Codeunit.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilityInstall.Codeunit.al
index 3e236a2e6e..3e8832d3c4 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilityInstall.Codeunit.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilityInstall.Codeunit.al
@@ -5,6 +5,7 @@
namespace System.AI;
using System.Environment;
+using System.Privacy;
codeunit 7760 "Copilot Capability Install"
{
@@ -53,4 +54,22 @@ codeunit 7760 "Copilot Capability Install"
begin
RegisterCapabilities();
end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Copilot Capability", 'OnGetRequiredPrivacyNotices', '', false, false)]
+ local procedure OnGetRequiredPrivacyNotices(CopilotCapability: Enum "Copilot Capability"; AppId: Guid; var RequiredPrivacyNotices: List of [Code[50]])
+ var
+ SystemPrivacyNoticeReg: Codeunit "System Privacy Notice Reg.";
+ ModuleInfo: ModuleInfo;
+ begin
+ NavApp.GetCurrentModuleInfo(ModuleInfo);
+
+ if AppId <> ModuleInfo.Id then
+ exit;
+
+ if CopilotCapability <> Enum::"Copilot Capability"::Chat then
+ exit;
+
+ if not RequiredPrivacyNotices.Contains(SystemPrivacyNoticeReg.GetMicrosoftLearnID()) then
+ RequiredPrivacyNotices.Add(SystemPrivacyNoticeReg.GetMicrosoftLearnID());
+ end;
}
\ No newline at end of file
diff --git a/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al b/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
index 2f2644e30b..9ee6c06bfb 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
@@ -4,6 +4,8 @@
// ------------------------------------------------------------------------------------------------
namespace System.AI;
+using System.Privacy;
+
///
/// Table to keep track of each Copilot Capability settings.
///
@@ -51,4 +53,36 @@ table 7775 "Copilot Settings"
Clustered = true;
}
}
+
+ procedure EvaluateStatus(): Enum "Copilot Status"
+ var
+ CopilotCapability: Codeunit "Copilot Capability";
+ begin
+ if Rec.Status <> Rec.Status::Active then
+ exit(Rec.Status);
+
+ if CopilotCapability.IsCapabilityActive(Rec.Capability, Rec."App Id") then
+ exit(Rec.Status::Active)
+ else
+ exit(Rec.Status::Inactive);
+ end;
+
+ procedure EnsurePrivacyNoticesApproved(): Boolean
+ var
+ CopilotCapability: Codeunit "Copilot Capability";
+ PrivacyNotice: Codeunit "Privacy Notice";
+ RequiredPrivacyNotices: List of [Code[50]];
+ RequiredPrivacyNotice: Code[50];
+ begin
+ CopilotCapability.OnGetRequiredPrivacyNotices(Rec.Capability, Rec."App Id", RequiredPrivacyNotices);
+
+ if RequiredPrivacyNotices.Count() <= 0 then
+ exit(true);
+
+ foreach RequiredPrivacyNotice in RequiredPrivacyNotices do
+ if not PrivacyNotice.ConfirmPrivacyNoticeApproval(RequiredPrivacyNotice, true) then
+ exit(false);
+
+ exit(true);
+ end;
}
\ No newline at end of file
diff --git a/src/System Application/App/Privacy Notice/src/PrivacyNotices.Page.al b/src/System Application/App/Privacy Notice/src/PrivacyNotices.Page.al
index a6ded490cf..c951c4e682 100644
--- a/src/System Application/App/Privacy Notice/src/PrivacyNotices.Page.al
+++ b/src/System Application/App/Privacy Notice/src/PrivacyNotices.Page.al
@@ -159,6 +159,7 @@ page 1565 "Privacy Notices"
trigger OnAfterGetRecord()
begin
+ Rec.CalcFields(Rec.Enabled, Rec.Disabled);
Accepted := Rec.Enabled;
Rejected := Rec.Disabled;
UserDecides := not (Accepted or Rejected);
diff --git a/src/System Application/App/Privacy Notice/src/SystemPrivacyNoticeReg.Codeunit.al b/src/System Application/App/Privacy Notice/src/SystemPrivacyNoticeReg.Codeunit.al
index 71d654afe8..7445673304 100644
--- a/src/System Application/App/Privacy Notice/src/SystemPrivacyNoticeReg.Codeunit.al
+++ b/src/System Application/App/Privacy Notice/src/SystemPrivacyNoticeReg.Codeunit.al
@@ -5,9 +5,11 @@
namespace System.Privacy;
+///
+/// This codeunit registers platform level privacy notices and provides procedures to consistently reference the privacy notices.
+///
codeunit 1566 "System Privacy Notice Reg."
{
- Access = Internal;
InherentEntitlements = X;
InherentPermissions = X;
@@ -17,11 +19,6 @@ codeunit 1566 "System Privacy Notice Reg."
PowerAutomateLabelTxt: Label 'Microsoft Power Automate', Locked = true; // Product names are not translated and it's important this entry exists.
MicrosoftLearnTxt: Label 'Microsoft Learn', Locked = true; // Product names are not translated and it's important this entry exists.
- procedure GetMicrosoftLearnID(): Text
- begin
- exit(MicrosoftLearnTxt);
- end;
-
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Privacy Notice", OnRegisterPrivacyNotices, '', false, false)]
local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary)
begin
@@ -37,16 +34,37 @@ codeunit 1566 "System Privacy Notice Reg."
if not TempPrivacyNotice.Insert() then;
end;
+ ///
+ /// Gets the Microsoft Learn privacy notice identifier.
+ ///
+ /// The privacy notice id for Microsoft Learn.
+ procedure GetMicrosoftLearnID(): Code[50]
+ begin
+ exit(MicrosoftLearnTxt);
+ end;
+
+ ///
+ /// Gets the Microsoft Teams privacy notice identifier.
+ ///
+ /// The privacy notice id for Microsoft Teams.
procedure GetTeamsPrivacyNoticeId(): Code[50]
begin
exit(MicrosoftTeamsTxt);
end;
+ ///
+ /// Gets the Power Automate privacy notice identifier.
+ ///
+ /// The privacy notice id for Power Automate.
procedure GetPowerAutomatePrivacyNoticeId(): Code[50]
begin
exit(PowerAutomateIdTxt);
end;
+ ///
+ /// Gets the Power Automate privacy notice name.
+ ///
+ /// The privacy notice name for Power Automate.
procedure GetPowerAutomatePrivacyNoticeName(): Code[250]
begin
exit(PowerAutomateLabelTxt);