Skip to content

Commit

Permalink
[25.x] [Copilot] Backport privacy check support (#2680)
Browse files Browse the repository at this point in the history
#### Summary
Backport the support to check privacy notices for Copilot capabilities.
Start the geo expansion of chat.

#### Work Item(s)
Fixes
[AB#502565](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/502565)

---------

Co-authored-by: Edward Lynch-Milner <47575833+edwardUL99@users.noreply.github.com>
Co-authored-by: Eddy Lynch <eddylynch@microsoft.com>
  • Loading branch information
3 people authored Jan 8, 2025
1 parent 3b25ec7 commit de3d313
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,15 @@ codeunit 7773 "Copilot Capability"
begin
exit(CopilotCapabilityImpl.IsCapabilityActive(CopilotCapability, AppId));
end;

/// <summary>
/// Event that is raised to specify if a Copilot capability depends on any additional privacy notices.
/// </summary>
/// <param name="CopilotCapability">The capability.</param>
/// <param name="AppId">The app id associated with the capability.</param>
/// <param name="RequiredPrivacyNotices">The list of required privacy notices for the capability to be enabled.</param>
[IntegrationEvent(false, false)]
procedure OnGetRequiredPrivacyNotices(CopilotCapability: Enum "Copilot Capability"; AppId: Guid; var RequiredPrivacyNotices: List of [Code[50]])
begin
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace System.AI;

using System.Environment;
using System;

codeunit 7760 "Copilot Capability Install"
{
Expand All @@ -24,11 +25,13 @@ codeunit 7760 "Copilot Capability Install"
internal procedure RegisterCapabilities()
var
EnvironmentInformation: Codeunit "Environment Information";
WithinEUDB: Boolean;
ApplicationFamily: Text;
begin
ApplicationFamily := EnvironmentInformation.GetApplicationFamily();
if TryGetIsWithinEUDB(WithinEUDB) then;

if ApplicationFamily = 'US' then
if ApplicationFamily in ['US', 'MX'] or WithinEUDB then
RegisterSaaSCapability(Enum::"Copilot Capability"::Chat, Enum::"Copilot Availability"::Preview, ChatLearnMoreLbl);

RegisterSaaSCapability(Enum::"Copilot Capability"::"Analyze List", Enum::"Copilot Availability"::Preview, AnalyzeListLearnMoreLbl);
Expand All @@ -44,6 +47,14 @@ codeunit 7760 "Copilot Capability Install"
CopilotCapability.RegisterCapability(Capability, Availability, LearnMoreUrl);
end;

[TryFunction]
local procedure TryGetIsWithinEUDB(var WithinEUDB: Boolean)
var
ALCopilotFunctions: DotNet ALCopilotFunctions;
begin
WithinEUDB := ALCopilotFunctions.IsWithinEUDB();
end;

[EventSubscriber(ObjectType::Page, Page::"Copilot AI Capabilities", 'OnRegisterCopilotCapability', '', false, false)]
local procedure OnRegisterCopilotCapability()
begin
Expand Down
34 changes: 34 additions & 0 deletions src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// ------------------------------------------------------------------------------------------------
namespace System.AI;

using System.Privacy;

/// <summary>
/// Table to keep track of each Copilot Capability settings.
/// </summary>
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

namespace System.Privacy;

/// <summary>
/// This codeunit registers platform level privacy notices and provides procedures to consistently reference the privacy notices.
/// </summary>
codeunit 1566 "System Privacy Notice Reg."
{
Access = Internal;
InherentEntitlements = X;
InherentPermissions = X;

Expand All @@ -28,16 +30,28 @@ codeunit 1566 "System Privacy Notice Reg."
if not TempPrivacyNotice.Insert() then;
end;

/// <summary>
/// Gets the Microsoft Teams privacy notice identifier.
/// </summary>
/// <returns>The privacy notice id for Microsoft Teams.</returns>
procedure GetTeamsPrivacyNoticeId(): Code[50]
begin
exit(MicrosoftTeamsTxt);
end;

/// <summary>
/// Gets the Power Automate privacy notice identifier.
/// </summary>
/// <returns>The privacy notice id for Power Automate.</returns>
procedure GetPowerAutomatePrivacyNoticeId(): Code[50]
begin
exit(PowerAutomateIdTxt);
end;

/// <summary>
/// Gets the Power Automate privacy notice name.
/// </summary>
/// <returns>The privacy notice name for Power Automate.</returns>
procedure GetPowerAutomatePrivacyNoticeName(): Code[250]
begin
exit(PowerAutomateLabelTxt);
Expand Down

0 comments on commit de3d313

Please sign in to comment.