Skip to content

Commit

Permalink
Add approval by default to privacy notices (#2368)
Browse files Browse the repository at this point in the history
<!-- Thank you for submitting a Pull Request. If you're new to
contributing to BCApps please read our pull request guideline below
* https://github.com/microsoft/BCApps/Contributing.md
-->
#### Summary <!-- Provide a general summary of your changes -->
Quick outline of new learn api requirements form Ayrton and Jacob:
1. Implement a privacy notice which is “enabled” by default for the
learn integration.
2. Implement an experience for when the privacy notice is declined by
either an admin or individual user. This involves showing a UI component
on the help pane notifying the user that the experience may be limited.
a. If admin, the text will have a link for them to open the privacy
notices page to enable it
b. If user and the notice is not yet set by them or an admin, the text
will have a link to open the wizard for approval
c. If user and the notice is disabled by admin, the card will simply
tell them to reach out to an administrator
For on-prem, this isn’t supported, so no privacy notice or search box
necessary.

To implement 2, the current Privacy notice table does not allow us to
differentiate if it is enabled by default. It simply calculates if it is
approved/disabled for all users or the user decides. So I am adding a
new way to allow an integration to be approved by default if not created yet or on creation.


#### Work Item(s) <!-- Add the issue number here after the #. The issue
needs to be open and approved. Submitting PRs with no linked issues or
unapproved issues is highly discouraged. -->
Fixes
[AB#556896](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/556896)

---------

Co-authored-by: Eddy Lynch <[email protected]>
  • Loading branch information
edwardUL99 and eddylynch authored Nov 26, 2024
1 parent 351ab0e commit 073ef9a
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
7 changes: 7 additions & 0 deletions src/System Application/App/Privacy Notice/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,10 @@ begin
end;
```

### How to have a privacy notice approved by default
In `PrivacyNoticeImpl` codeunit, there is a procedure called `ShouldApproveByDefault`. Return true from this method if the integration ID matches
the ID of the scenario you want to approve by default. In the following scenarios where the procedure returns true, it will have a default approval for the entire organization if:
1. `PrivacyNotice.GetPrivacyNoticeApprovalState` is called for an integration with no privacy notice record created or,
2. A privacy notice for the scenario is created and no approvals exist for it yet.

An admin can later agree/disagree to this approval on the Privacy Notices page.
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,19 @@ codeunit 1563 "Privacy Notice"
var
PrivacyNoticeImpl: Codeunit "Privacy Notice Impl.";
begin
exit(PrivacyNoticeImpl.CheckPrivacyNoticeApprovalState(Id) = "Privacy Notice Approval State"::Disagreed);
exit(PrivacyNoticeImpl.IsApprovalStateDisagreed(Id));
end;

/// <summary>
/// Determines whether the admin or user has disagreed with the Privacy Notice.
/// </summary>
/// <param name="State">The approval state.</param>
/// <returns>Whether the Privacy Notice was disagreed to.</returns>
procedure IsApprovalStateDisagreed(State: Enum "Privacy Notice Approval State"): Boolean
var
PrivacyNoticeImpl: Codeunit "Privacy Notice Impl.";
begin
exit(PrivacyNoticeImpl.IsApprovalStateDisagreed(State));
end;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ codeunit 1565 "Privacy Notice Impl."

if not PrivacyNotice.Get(PrivacyNoticeId) then begin
Session.LogMessage('0000GN7', StrSubstNo(PrivacyNoticeDoesNotExistTelemetryTxt, PrivacyNoticeId), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTxt);
if SkipCheckInEval and Company.Get(CompanyName()) and Company."Evaluation Company" then
exit("Privacy Notice Approval State"::Agreed); // Auto-agree for evaluation companies if admin has not explicitly disagreed
if ShouldApproveByDefault(PrivacyNoticeId) or (SkipCheckInEval and Company.Get(CompanyName()) and Company."Evaluation Company") then
exit("Privacy Notice Approval State"::Agreed); // Auto-agree for evaluation companies if admin has not explicitly disagreed or approve by default
exit("Privacy Notice Approval State"::"Not set"); // If there are no Privacy Notice then it is by default "Not set".
end;

Expand Down Expand Up @@ -176,8 +176,8 @@ codeunit 1565 "Privacy Notice Impl."
if CanCurrentUserApproveForOrganization() then
PrivacyNoticeApproval.SetApprovalState(PrivacyNoticeId, EmptyGuid, PrivacyNoticeApprovalState)
else
if PrivacyNoticeApprovalState <> "Privacy Notice Approval State"::Disagreed then // We do not store rejected user approvals
PrivacyNoticeApproval.SetApprovalState(PrivacyNoticeId, UserSecurityId(), PrivacyNoticeApprovalState);
if not IsApprovalStateDisagreed(PrivacyNoticeApprovalState) then // We do not store rejected user approvals
PrivacyNoticeApproval.SetApprovalState(PrivacyNoticeId, UserSecurityId(), PrivacyNoticeApprovalState)
end;

procedure ShowOneTimePrivacyNotice(IntegrationName: Text[250]): Enum "Privacy Notice Approval State"
Expand Down Expand Up @@ -222,11 +222,27 @@ codeunit 1565 "Privacy Notice Impl."
if PrivacyNotice.Link = '' then
PrivacyNotice.Link := MicrosoftPrivacyLinkTxt;
if not PrivacyNotice.Insert() then
Session.LogMessage('0000GMF', PrivacyNoticeNotCreatedTelemetryErr, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTxt);
Session.LogMessage('0000GMF', PrivacyNoticeNotCreatedTelemetryErr, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.TelemetryCategoryTxt)
else
TryCreateDefaultApproval(PrivacyNotice);
end;
until TempPrivacyNotice.Next() = 0;
end;

/// <summary>
/// Creates a default approval for the given privacy notice if it can be approved by default and there is not already an approval record for it.
/// </summary>
/// <param name="PrivacyNotice">The notice to save approval under.</param>
local procedure TryCreateDefaultApproval(PrivacyNotice: Record "Privacy Notice")
var
PrivacyNoticeApproval: Codeunit "Privacy Notice Approval";
begin
if ShouldApproveByDefault(PrivacyNotice.ID) then begin
PrivacyNoticeApproval.SetApprovalState(PrivacyNotice.ID, EmptyGuid, "Privacy Notice Approval State"::Agreed);
PrivacyNotice.CalcFields(Enabled);
end;
end;

[TryFunction]
local procedure TryGetAllPrivacyNotices(var PrivacyNotice: Record "Privacy Notice" temporary)
var
Expand All @@ -245,7 +261,14 @@ codeunit 1565 "Privacy Notice Impl."
PrivacyNotice.Id := Id;
PrivacyNotice."Integration Service Name" := IntegrationName;
PrivacyNotice.Link := Link;
exit(PrivacyNotice.Insert());

if PrivacyNotice.Insert() then begin
TryCreateDefaultApproval(PrivacyNotice);

exit(true);
end;

exit(false);
end;

local procedure ShowPrivacyNotice(PrivacyNotice: Record "Privacy Notice"): Boolean
Expand Down Expand Up @@ -303,6 +326,55 @@ codeunit 1565 "Privacy Notice Impl."
IsApproved := false;
end;

/// <summary>
/// Checks if the IDs are equal.
/// </summary>
/// <param name="ID">The first ID.</param>
/// <param name="IDToCheck">The ID to check against the first ID parameter.</param>
/// <returns>true if equal; otherwise false.</returns>
local procedure CheckIntegrationIDEquality(ID: Text; IDToCheck: Text): Boolean
begin
exit(CopyStr(UpperCase(ID), 1, 50) = CopyStr(UpperCase(IDToCheck), 1, 50));
end;

/// <summary>
/// Indicates if the integration should be enabled by default.
/// </summary>
/// <param name="IntegrationID">The integration ID/</param>
/// <returns>true if it should be approved by default; otherwise false.</returns>
local procedure ShouldApproveByDefault(IntegrationID: Text): Boolean
var
SystemPrivacyNoticeReg: Codeunit "System Privacy Notice Reg.";
begin
if CheckIntegrationIDEquality(SystemPrivacyNoticeReg.GetMicrosoftLearnID(), IntegrationID) then
exit(true);

exit(false);
end;

/// <summary>
/// Determines whether the admin or user has disagreed with the Privacy Notice.
/// </summary>
/// <param name="Id">Identification of an existing privacy notice.</param>
/// <returns>Whether the Privacy Notice was disagreed to.</returns>
procedure IsApprovalStateDisagreed(Id: Code[50]): Boolean
var
State: Enum "Privacy Notice Approval State";
begin
State := CheckPrivacyNoticeApprovalState(Id);
exit(IsApprovalStateDisagreed(State));
end;

/// <summary>
/// Determines whether the admin or user has disagreed with the Privacy Notice.
/// </summary>
/// <param name="State">The approval state.</param>
/// <returns>Whether the Privacy Notice was disagreed to.</returns>
procedure IsApprovalStateDisagreed(State: Enum "Privacy Notice Approval State"): Boolean
begin
exit(State = "Privacy Notice Approval State"::Disagreed);
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"System Action Triggers", GetPrivacyNoticeApprovalState, '', true, true)]
local procedure GetPrivacyNoticeApprovalState(PrivacyNoticeIntegrationName: Text; var PrivacyNoticeApprovalState: Integer)
var
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ page 1565 "Privacy Notices"
field(Accepted; Accepted)
{
Caption = 'Agree for Everyone';
ToolTip = 'Specifies whether an administrator has accepted the integration''s privacy notice on behalf of all users.';
ToolTip = 'Specifies whether an administrator (or the system by default) has accepted the integration''s privacy notice on behalf of all users.';
ApplicationArea = All;

trigger OnValidate()
Expand Down Expand Up @@ -92,6 +92,7 @@ page 1565 "Privacy Notices"
SetRecordApprovalState();
end;
}

#pragma warning disable AA0218
field(Accepted2; Rec.Enabled)
{
Expand Down Expand Up @@ -175,6 +176,8 @@ page 1565 "Privacy Notices"
else
PrivacyNotice.SetApprovalState(Rec.ID, "Privacy Notice Approval State"::"Not set");
end;

CurrPage.Update();
end;

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@ codeunit 1566 "System Privacy Notice Reg."
MicrosoftTeamsTxt: Label 'Microsoft Teams', Locked = true; // Product names are not translated and it's important this entry exists.
PowerAutomateIdTxt: Label 'Power Automate', Locked = true; // Product names are not translated and it's important this entry exists.
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
TempPrivacyNotice.Init();
TempPrivacyNotice."ID" := MicrosoftLearnTxt;
TempPrivacyNotice."Integration Service Name" := MicrosoftLearnTxt;
if not TempPrivacyNotice.Insert() then;
TempPrivacyNotice.ID := MicrosoftTeamsTxt;
TempPrivacyNotice."Integration Service Name" := MicrosoftTeamsTxt;
if not TempPrivacyNotice.Insert() then;
Expand Down

0 comments on commit 073ef9a

Please sign in to comment.