Page 9515 Azure AD User Update Wizard, source in 29

Source242526272829

src/System Application/App/Azure AD User Management/src/User sync/AzureADUserUpdateWizard.Page.al500 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.Azure.Identity;

using System.Environment.Configuration;
using System.Security.User;
using System.Utilities;

/// <summary>
/// Administrators can use this page to synchronize information about users from Microsoft 365 to Business Central.
/// </summary>
page 9515 "Azure AD User Update Wizard"
{
    Caption = 'Update users from Microsoft 365';
    PageType = NavigatePage;
    ApplicationArea = All;
    DeleteAllowed = false;
    InsertAllowed = false;
    UsageCategory = Administration;
    SourceTable = "Azure AD User Update Buffer";
    SourceTableTemporary = true;
    Extensible = false;

    layout
    {
        area(Content)
        {
            group(Welcome)
            {
                Visible = WelcomeVisible;
                Caption = 'Welcome to user updates';
                group(HeaderText)
                {
                    ShowCaption = false;
                    Caption = 'Description';
                    InstructionalText = 'Bring changes to user information from your Microsoft 365 organization to Business Central. Update license assignments, name changes, email addresses, preferred languages, and user access.';
                }
                group(NoteGroup)
                {
                    Caption = 'Note:';
                    InstructionalText = 'It can take up to 72 hours for a change in Microsoft 365 to become available to Business Central.';
                }

                group(LinkToLicenseConfigurationGroup)
                {
                    Caption = 'Before you get started';
                    InstructionalText = 'You might want to configure custom permissions for each license type to speed up how you configure users.';

                    field(LinkToLicenseConfiguration; LinkToLicenseConfigurationTxt)
                    {
                        ApplicationArea = All;
                        Editable = false;
                        ShowCaption = false;
                        Caption = ' ';
                        ToolTip = 'Configure permissions per license so you won''t have to manually configure every user.';

                        trigger OnDrillDown()
                        begin
                            Page.Run(Page::"Plan Configuration List");
                        end;
                    }
                }
            }
            group("No Updates")
            {
                Visible = NoAvailableUpdatesVisible;
                Caption = 'No updates';
                InstructionalText = 'There are no updates from Microsoft 365. You can exit this guide.';
                ShowCaption = false;
            }
            group("Total Updates To Confirm")
            {
                Visible = TotalUpdatesToConfirmVisible;
                Caption = 'License updates to confirm';

                label(TotalUpdatesToConfirmLbl)
                {
                    ApplicationArea = All;
                    CaptionClass = TotalUpdatesToConfirm;
                    ShowCaption = false;
                }
            }
            group("Total Updates Ready To Apply")
            {
                Visible = TotalUpdatesReadyToApplyVisible;
                Caption = 'Available updates';

                label(TotalUpdatesReadyToApplyLbl)
                {
                    ApplicationArea = All;
                    CaptionClass = TotalUpdatesReadyToApply;
                    ShowCaption = false;
                }
            }
            group(ConfirmPermissionChanges)
            {
                Visible = ConfirmPermissionChangesVisible;
                Caption = 'Confirm permission changes';
                InstructionalText = 'For each update with Select in the Action column, you must either choose Keep current to disregard the updated permissions, or Append to update the user permissions.';
                group(PermissionsGroup)
                {
                    ShowCaption = false;
                    repeater(Permissions)
                    {
                        field(DisplayName; Rec."Display Name")
                        {
                            ToolTip = 'Specifies the display name';
                            ApplicationArea = All;
                        }
                        field(CurrentLicense; Rec."Current Value")
                        {
                            Caption = 'Current plan';
                            ToolTip = 'Specifies the current of user entity';
                            Editable = false;
                            ApplicationArea = All;
                        }
                        field(NewLicense; Rec."New Value")
                        {
                            Caption = 'New plan';
                            ToolTip = 'Specifies the new value of user entity';
                            Editable = false;
                            ApplicationArea = All;
                        }
                        field(PermissionAction; Rec."Permission Change Action")
                        {
                            Caption = 'Action';
                            ToolTip = 'Specifies how this license change should be handled';
                            ApplicationArea = All;
                            Enabled = Rec."Update Type" = Rec."Update Type"::Change;

                            trigger OnValidate()
                            begin
                                SetDoneSelectingPermissionsButtonEnabled();
                            end;
                        }
                    }
                }
            }
            group(ListOfChanges)
            {
                Visible = ListOfChangesVisible;
                InstructionalText = 'To apply the changes, choose Finish.';
                part(Changes; "Azure AD User Updates Part")
                {
                    ApplicationArea = All;
                }
            }
            group(Finished)
            {
                Visible = FinishedVisible;
                Caption = 'Good work!';
                group(NumberOfUpdatesAppliedGroup)
                {
                    Caption = 'Summary';
                    label(NumberOfUpdatesApplied)
                    {
                        ApplicationArea = All;
                        CaptionClass = NumberOfUpdatesApplied;
                        ShowCaption = false;
                    }
                }
                group(FailedUpdatesGroup)
                {
                    Visible = HasFailures;
                    Caption = 'Failed updates';
                    InstructionalText = 'The following changes could not be applied. Resolve the issues and run this guide again to retry.';
                    repeater(Failures)
                    {
                        Editable = false;
                        field(FailedDisplayName; Rec."Display Name")
                        {
                            Caption = 'User';
                            ToolTip = 'Specifies the display name of the user that failed to update.';
                            ApplicationArea = All;
                        }
                        field(FailedEntity; Rec."Update Entity")
                        {
                            Caption = 'Information';
                            ToolTip = 'Specifies the user information that failed to update.';
                            ApplicationArea = All;
                        }
                        field(FailedError; Rec."Error Message")
                        {
                            Caption = 'Error';
                            ToolTip = 'Specifies the error returned when applying the update.';
                            ApplicationArea = All;
                        }
                    }
                }
            }
        }
    }

    actions
    {
        area(Processing)
        {
            action(Cancel)
            {
                ApplicationArea = All;
                Caption = 'Cancel';
                ToolTip = 'Cancel the user updates';
                Image = Cancel;
                Visible = CancelButtonVisible;
                InFooterBar = true;

                trigger OnAction()
                var
                    ConfirmManagement: Codeunit "Confirm Management";
                begin
                    if ConfirmManagement.GetResponse(ConfirmCancelQst, false) then
                        CurrPage.Close();
                end;
            }
            action(Back)
            {
                ApplicationArea = All;
                Caption = 'Back';
                ToolTip = 'See overview of all the changes';
                Image = PreviousRecord;
                Visible = BackButtonVisible;
                InFooterBar = true;

                trigger OnAction()
                begin
                    ShowOverview();
                end;
            }
            action(DoneSelectingPermissions)
            {
                ApplicationArea = All;
                Caption = 'Next';
                ToolTip = 'Proceed to applying the user updates';
                Image = NextRecord;
                Visible = DoneSelectingPermissionsButtonVisible;
                Enabled = DoneSelectingPermissionsButtonEnabled;
                InFooterBar = true;

                trigger OnAction()
                begin
                    ShowOverview();
                end;
            }
            action(Next)
            {
                ApplicationArea = All;
                Caption = 'Next';
                ToolTip = 'See the available updates';
                Image = NextRecord;
                Visible = NextButtonVisible;
                InFooterBar = true;

                trigger OnAction()
                var
                    AzureADUserSyncImpl: Codeunit "Azure AD User Sync Impl.";
                begin
                    AzureADUserSyncImpl.FetchUpdatesFromAzureGraph(Rec);
                    CurrPage.Changes.Page.SetUpdates(Rec);

                    ShowOverview();
                end;
            }
            action(ManagePermissionUpdates)
            {
                ApplicationArea = All;
                Caption = 'Next';
                ToolTip = 'Confirm the permission updates to be applied';
                Visible = ManagePermissionUpdatesButtonVisible;
                Image = Questionaire;
                InFooterBar = true;

                trigger OnAction()
                begin
                    MakeAllGroupsInvisible();
                    SetVisiblityOnActions();
                    BackButtonVisible := false;
                    DoneSelectingPermissionsButtonVisible := true;
                    DoneSelectingPermissionsButtonEnabled := false;
                    ManagePermissionUpdatesButtonVisible := false;
                    ConfirmPermissionChangesVisible := true;
                    Rec.SetRange("Update Entity", Rec."Update Entity"::Plan);
                end;
            }
            action(ViewChanges)
            {
                ApplicationArea = All;
                Caption = 'View changes';
                ToolTip = 'View a list of changes that will be applied';
                Visible = ViewChangesButtonVisible;
                Image = Change;
                InFooterBar = true;

                trigger OnAction()
                begin
                    MakeAllGroupsInvisible();
                    ListOfChangesVisible := true;
                    SetVisiblityOnActions();
                    BackButtonVisible := true;
                    ViewChangesButtonVisible := false;
                    Rec.SetRange("Needs User Review", false);
                end;
            }
            action(ApplyUpdates)
            {
                ApplicationArea = All;
                Caption = 'Finish';
                ToolTip = 'Apply the user updates';
                Visible = ApplyUpdatesButtonVisible;
                Image = Approve;
                InFooterBar = true;

                trigger OnAction()
                var
                    AzureADUserSyncImpl: Codeunit "Azure AD User Sync Impl.";
                    GuidedExperience: Codeunit "Guided Experience";
                    SuccessCount: Integer;
                    TotalCount: Integer;
                    UpdateUsersfromMicrosoft365RunLbl: Label 'Update users from Microsoft 365 wizard has been run by the UserSecurityId %1.', Locked = true;
                begin
                    Rec.Reset();
                    TotalCount := Rec.Count();
                    SuccessCount := AzureADUserSyncImpl.ApplyUpdatesFromAzureGraph(Rec);
                    HasFailures := SuccessCount < TotalCount;
                    if HasFailures then
                        NumberOfUpdatesApplied := StrSubstNo(SomeUpdatesFailedTxt, SuccessCount, TotalCount)
                    else
                        NumberOfUpdatesApplied := StrSubstNo(NumberOfUpdatesAppliedTxt, SuccessCount, TotalCount);

                    // Keep failed records so the user can see them; drop the successful ones.
                    Rec.SetRange("Error Message", '');
                    Rec.DeleteAll();
                    Rec.SetRange("Error Message");

                    GuidedExperience.CompleteAssistedSetup(ObjectType::Page, Page::"Azure AD User Update Wizard");
                    Session.LogAuditMessage(StrSubstNo(UpdateUsersfromMicrosoft365RunLbl, UserSecurityId()), SecurityOperationResult::Success, AuditCategory::ApplicationManagement, 2, 0);

                    MakeAllGroupsInvisible();
                    FinishedVisible := true;
                    SetVisiblityOnActions();
                    ApplyUpdatesButtonVisible := false;
                    CancelButtonVisible := false;
                    CloseButtonVisible := true;
                end;
            }
            action(Close)
            {
                ApplicationArea = All;
                Caption = 'Close';
                ToolTip = 'Closes this window';
                Image = Close;
                Visible = CloseButtonVisible;
                InFooterBar = true;

                trigger OnAction()
                begin
                    CurrPage.Close();
                end;
            }
        }
    }

    var
        WelcomeVisible: Boolean;
        ConfirmPermissionChangesVisible: Boolean;
        ListOfChangesVisible: Boolean;
        FinishedVisible: Boolean;

        CancelButtonVisible: Boolean;
        BackButtonVisible: Boolean;
        NextButtonVisible: Boolean;
        ManagePermissionUpdatesButtonVisible: Boolean;
        ViewChangesButtonVisible: Boolean;
        ApplyUpdatesButtonVisible: Boolean;
        CloseButtonVisible: Boolean;
        DoneSelectingPermissionsButtonVisible: Boolean;
        DoneSelectingPermissionsButtonEnabled: Boolean;

        CountOfManagedPermissionUpdates: Integer;
        CountOfApplicableUpdates: Integer;

        HasFailures: Boolean;

        ConfirmCancelQst: Label 'Are you sure you wish to cancel the updates?';
        NumberOfUpdatesApplied: Text;
        NumberOfUpdatesAppliedTxt: Label '%1 out of %2 updates have been applied in Business Central. You can close this guide.', Comment = '%1 = An integer count of total updates applied; %2 = total count of updates';
        SomeUpdatesFailedTxt: Label '%1 out of %2 updates have been applied in Business Central. Review the details of the failed updates below and try again.', Comment = '%1 = An integer count of total updates applied; %2 = total count of updates';

        NoAvailableUpdatesVisible: Boolean;

        TotalUpdatesToConfirm: Text;
        TotalUpdatesToConfirmVisible: Boolean;
        TotalUpdatesToConfirmSingularTxt: Label 'We found %1 license update for a user who has customized permissions. Before continuing, you must either keep the current permissions or add the permissions associated with the new license for the user.', Comment = '%1 = An integer count of total updates to get confirmation on';
        TotalUpdatesToConfirmPluralTxt: Label 'We found %1 license updates for users who have customized permissions. Before continuing, you must either keep the current permissions or add the permissions associated with the new license for those users.', Comment = '%1 = An integer count of total updates to get confirmation on';

        TotalUpdatesReadyToApply: Text;
        TotalUpdatesReadyToApplyVisible: Boolean;
        TotalUpdatesReadyToApplyTxt: Label 'Number of updates ready to be applied: %1. These can be name, email address, preferred language, and user access changes. Choose View changes to see the list.', Comment = '%1 = An integer count of total updates ready to apply';

        LinkToLicenseConfigurationTxt: Label 'Configure permissions per license';
        CannotUpdateUsersFromOfficeErr: Label 'Your user account does not give you permission to fetch users from Microsoft 365. Please contact your administrator.';

    trigger OnOpenPage()
    var
        UserPermissions: Codeunit "User Permissions";
    begin
        if not (UserPermissions.CanManageUsersOnTenant(UserSecurityId()) and UserPermissions.IsSuper(UserSecurityId())) then
            Error(CannotUpdateUsersFromOfficeErr);

        MakeAllGroupsInvisible();
        WelcomeVisible := true;

        SetVisiblityOnActions();
        NextButtonVisible := true;
    end;

    trigger OnAfterGetCurrRecord()
    begin
        if DoneSelectingPermissionsButtonVisible then
            SetDoneSelectingPermissionsButtonEnabled();
    end;

    local procedure SetDoneSelectingPermissionsButtonEnabled()
    var
        TempAzureADUserUpdateBuffer: Record "Azure AD User Update Buffer" temporary;
    begin
        if Rec."Permission Change Action" = Rec."Permission Change Action"::Select then begin
            DoneSelectingPermissionsButtonEnabled := false;
            exit;
        end;

        TempAzureADUserUpdateBuffer.Copy(Rec, true); // share the same table
        TempAzureADUserUpdateBuffer.SetRange("Permission Change Action", TempAzureADUserUpdateBuffer."Permission Change Action"::Select);
        // exclude the current record
        TempAzureADUserUpdateBuffer.SetFilter("Authentication Object ID", '<>%1', Rec."Authentication Object ID");

        // if all of the update actions are chosen, enable the "Next" button.
        if TempAzureADUserUpdateBuffer.IsEmpty() then
            DoneSelectingPermissionsButtonEnabled := true;
    end;

    local procedure MakeAllGroupsInvisible()
    begin
        WelcomeVisible := false;
        TotalUpdatesToConfirmVisible := false;
        ConfirmPermissionChangesVisible := false;
        TotalUpdatesReadyToApplyVisible := false;
        NoAvailableUpdatesVisible := false;
        ListOfChangesVisible := false;
        FinishedVisible := false;
    end;

    local procedure ShowOverview()
    var
        GuidedExperience: Codeunit "Guided Experience";
    begin
        MakeAllGroupsInvisible();
        SetVisiblityOnActions();
        TotalUpdatesToConfirmVisible := CountOfManagedPermissionUpdates > 0;
        if CountOfManagedPermissionUpdates = 1 then
            TotalUpdatesToConfirm := StrSubstNo(TotalUpdatesToConfirmSingularTxt, CountOfManagedPermissionUpdates)
        else
            TotalUpdatesToConfirm := StrSubstNo(TotalUpdatesToConfirmPluralTxt, CountOfManagedPermissionUpdates);
        TotalUpdatesReadyToApplyVisible := (CountOfApplicableUpdates > 0) and (not TotalUpdatesToConfirmVisible);
        TotalUpdatesReadyToApply := StrSubstNo(TotalUpdatesReadyToApplyTxt, CountOfApplicableUpdates);
        if (not TotalUpdatesReadyToApplyVisible) and (not TotalUpdatesToConfirmVisible) then begin
            NoAvailableUpdatesVisible := true;
            GuidedExperience.CompleteAssistedSetup(ObjectType::Page, Page::"Azure AD User Update Wizard");
        end;
    end;

    local procedure SetVisiblityOnActions()
    var
        TotalNumberOfUpdates: Integer;
    begin
        Rec.Reset();
        CancelButtonVisible := true;
        BackButtonVisible := false;
        NextButtonVisible := false;
        CloseButtonVisible := false;
        DoneSelectingPermissionsButtonVisible := false;
        TotalNumberOfUpdates := Rec.Count();

        Rec.SetRange("Needs User Review", true);
        CountOfManagedPermissionUpdates := Rec.Count();
        CountOfApplicableUpdates := TotalNumberOfUpdates - CountOfManagedPermissionUpdates;

        ApplyUpdatesButtonVisible := (CountOfManagedPermissionUpdates = 0) and (CountOfApplicableUpdates > 0);
        ManagePermissionUpdatesButtonVisible := CountOfManagedPermissionUpdates > 0;
        ViewChangesButtonVisible := (CountOfApplicableUpdates > 0) and (not ManagePermissionUpdatesButtonVisible);
        Rec.SetRange("Needs User Review");
        if CountOfApplicableUpdates + CountOfManagedPermissionUpdates = 0 then begin
            CloseButtonVisible := true;
            CancelButtonVisible := false;
        end;
    end;
}