Codeunit 5323 Exchange Add-in Setup, source in 29

Source29

src/Layers/W1/BaseApp/System/Exchange/ExchangeAddinSetup.Codeunit.al258 lines, Copyright (c) Microsoft Corporation. MIT

#if not CLEAN29
namespace System.Integration;

using Microsoft.CRm.BusinessRelation;
using Microsoft.CRM.Contact;
using Microsoft.CRM.Outlook;
using Microsoft.Foundation.Company;
using Microsoft.Utilities;
using System;
using System.Azure.Identity;
using System.Environment;
using System.Security.AccessControl;
using System.Security.User;

codeunit 5323 "Exchange Add-in Setup"
{
    ObsoleteReason = 'Exchange is depracated. Please deploy add-in manually.';
    ObsoleteState = Pending;
    ObsoleteTag = '29.0';

    trigger OnRun()
    begin
    end;

    var
        ExchangeWebServicesServer: Codeunit "Exchange Web Services Server";
        AddinManifestManagement: Codeunit "Add-in Manifest Management";
        Initialized: Boolean;
        InvalidCredentialsErr: Label 'The provided email address and password are not valid Office 365 or Exchange credentials.';
        NoMailboxErr: Label 'An Office 365 or Exchange mailbox could not be found for this account.';
        AutodiscoverMsg: Label 'Searching for your mailbox.';
        WelcomeSubjectTxt: Label 'Welcome to %1 in Outlook', Comment = '%1 - Application name';
        WelcomeEmailFromNameTxt: Label '%1 Admin', Comment = '%1 - Application Name';
        SalesEmailAddrTxt: Label 'admin@contoso.com', Locked = true;
        ExchangeTelemetryCategoryTxt: Label 'AL Exchange', Locked = true;
        TryInitializeWithEmailTxt: Label 'Trying to initialize the service with email.', Locked = true;
        TryInitializeWithCredentialsTxt: Label 'Trying to initialize the service with credentials.', Locked = true;
        InitializedWithOAuthTokenTxt: Label 'The service is initialized with OAuth token.', Locked = true;
        InitializedWithCertificateTxt: Label 'The service is initialized with certificate.', Locked = true;
        InitializedWithCredentialsTxt: Label 'The service is initialized with credentials.', Locked = true;
        NotInitializedWithCredentialsTxt: Label 'The service is not initialized with credentials.', Locked = true;
        TryAutodiscoverEndpointTxt: Label 'Try to autodiscover endpoint.', Locked = true;
        ImpersonateUserTxt: Label 'Impersonate user.', Locked = true;
        DeployAddInTxt: Label 'Deploy add-in.', Locked = true;
        DeploySampleEmailsTxt: Label 'Deploy sample emails.', Locked = true;
        SampleEmailsDeployedTxt: Label 'sample emails are deployed.', Locked = true;
        ValidCredentialsTxt: Label 'The provided email address and password are valid Office 365 or Exchange credentials.', Locked = true;
        InvalidCredentialsTxt: Label 'The provided email address and password are not valid Office 365 or Exchange credentials.', Locked = true;
        NoMailboxTxt: Label 'An Office 365 or Exchange mailbox could not be found for this account.', Locked = true;
        ToSpecifiedEmailTxt: Label 'Recipient email is the specified email.', Locked = true;
        ToUserEmailTxt: Label 'Recipient email is the user email.', Locked = true;
        FromContactEmailTxt: Label 'Sender email is the contact email.', Locked = true;
        FromSalesEmailTxt: Label 'Sender email is the sales email.', Locked = true;
        DemoCompanyEmailTxt: Label 'trey.research@contoso.com', Locked = true;

    [TryFunction]
    local procedure Initialize(AuthenticationEmail: Text[80])
    var
        ExchangeServiceSetup: Record "Exchange Service Setup";
        AzureADMgt: Codeunit "Azure AD Mgt.";
        AccessToken: SecretText;
    begin
        Session.LogMessage('0000BX8', TryInitializeWithEmailTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);

        AccessToken := AzureADMgt.GetAccessTokenAsSecretText(AzureADMgt.GetO365Resource(), AzureADMgt.GetO365ResourceName(), false);

        if not AccessToken.IsEmpty() then begin
            ExchangeWebServicesServer.InitializeWithOAuthToken(AccessToken, ExchangeWebServicesServer.GetEndpoint());
            if ValidateCredentials() then begin
                Session.LogMessage('0000BX9', InitializedWithOAuthTokenTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
                exit;
            end;
        end;

        ExchangeServiceSetup.Get();
        ExchangeWebServicesServer.InitializeWithCertificate(ExchangeServiceSetup."Azure AD App. ID", ExchangeServiceSetup."Azure AD App. Cert. Thumbprint",
              ExchangeServiceSetup."Azure AD Auth. Endpoint", ExchangeServiceSetup."Exchange Service Endpoint", ExchangeServiceSetup."Exchange Resource Uri");

        ExchangeWebServicesServer.SetImpersonatedIdentity(AuthenticationEmail);
        Initialized := true;

        Session.LogMessage('0000BYR', InitializedWithCertificateTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
    end;

    [TryFunction]
    [Scope('OnPrem')]
    procedure InitializeServiceWithCredentials(Email: Text[80]; Password: Text)
    var
        WebCredentials: DotNet WebCredentials;
        ProgressWindow: Dialog;
        ErrorText: Text;
    begin
        Session.LogMessage('0000BXA', TryInitializeWithCredentialsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);

        WebCredentials := WebCredentials.WebCredentials(Email, Password);

        ProgressWindow.Open('#1');
        ProgressWindow.Update(1, AutodiscoverMsg);

        // Production O365 endpoint
        Initialized := ExchangeWebServicesServer.Initialize(Email, ExchangeWebServicesServer.GetEndpoint(), WebCredentials, false) and
          ValidateCredentials();
        ErrorText := GetLastErrorText;

        // Autodiscover endpoint (can be slow)
        if not Initialized then begin
            Session.LogMessage('0000BXB', TryAutodiscoverEndpointTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
            Initialized := ExchangeWebServicesServer.Initialize(Email, '', WebCredentials, true) and ValidateCredentials();
            ErrorText := GetLastErrorText;
        end;

        ProgressWindow.Close();

        if not Initialized then begin
            Session.LogMessage('0000BXC', NotInitializedWithCredentialsTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
            Error(ErrorText);
        end;

        Session.LogMessage('0000BXD', InitializedWithCredentialsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
    end;

    [Scope('OnPrem')]
    procedure CredentialsRequired(AuthenticationEmail: Text[80]) Required: Boolean
    begin
        Required := not Initialize(AuthenticationEmail);
    end;

    [Scope('OnPrem')]
    procedure ImpersonateUser(Email: Text[80])
    begin
        Session.LogMessage('0000BXI', ImpersonateUserTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);

        if not Initialized then
            Initialize(Email);

        ExchangeWebServicesServer.SetImpersonatedIdentity(Email);
    end;

    procedure SampleEmailsAvailable(): Boolean
    var
        CompanyInformationMgt: Codeunit "Company Information Mgt.";
        EnvironmentInfo: Codeunit "Environment Information";
    begin
        exit(CompanyInformationMgt.IsDemoCompany() and EnvironmentInfo.IsSaaS());
    end;

    [Scope('OnPrem')]
    procedure DeployAddin(OfficeAddin: Record "Office Add-in")
    var
        UserPreference: Record "User Preference";
        InstructionMgt: Codeunit "Instruction Mgt.";
        Stream: DotNet MemoryStream;
        Encoding: DotNet UTF8Encoding;
        ManifestText: Text;
    begin
        Session.LogMessage('0000BXJ', DeployAddInTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);

        AddinManifestManagement.GenerateManifest(OfficeAddin, ManifestText);
        Encoding := Encoding.UTF8Encoding();
        Stream := Stream.MemoryStream(Encoding.GetBytes(ManifestText));
        ExchangeWebServicesServer.InstallApp(Stream);

        UserPreference.SetRange("Instruction Code", InstructionMgt.OfficeUpdateNotificationCode());
        UserPreference.SetRange("User ID", UserId);
        UserPreference.DeleteAll();
    end;

    [Scope('OnPrem')]
    procedure DeployAddins(var OfficeAddin: Record "Office Add-in")
    begin
        if OfficeAddin.GetAddins() then
            repeat
                DeployAddin(OfficeAddin);
            until OfficeAddin.Next() = 0;
    end;

    [TryFunction]
    [Scope('OnPrem')]
    procedure TryDeployAddins(var OfficeAddin: Record "Office Add-in")
    begin
        DeployAddins(OfficeAddin);
    end;

    [Scope('OnPrem')]
    procedure DeploySampleEmails(EmailAddress: Text)
    var
        User: Record User;
        OfficeAddinSampleEmails: Codeunit "Office Add-In Sample Emails";
        RecipientEmail: Text;
        FromEmail: Text;
        HTMlBody: Text;
    begin
        Session.LogMessage('0000BXK', DeploySampleEmailsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);

        if EmailAddress <> '' then begin
            Session.LogMessage('0000BXL', ToSpecifiedEmailTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
            RecipientEmail := EmailAddress
        end else begin
            User.SetRange("User Name", UserId);
            if User.FindFirst() then begin
                Session.LogMessage('0000BXM', ToUserEmailTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
                RecipientEmail := User."Authentication Email";
            end;
        end;

        FromEmail := GetFromEmail();
        if FromEmail <> '' then
            Session.LogMessage('0000BXN', FromContactEmailTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt)
        else begin
            // Fallback to SalesEmailAddrTxt
            FromEmail := SalesEmailAddrTxt;
            Session.LogMessage('0000BYS', FromSalesEmailTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
        end;

        HTMlBody := OfficeAddinSampleEmails.GetHTMLSampleMsg();
        ExchangeWebServicesServer.SaveHTMLEmailToInbox(StrSubstNo(WelcomeSubjectTxt, PRODUCTNAME.Marketing()), HTMlBody,
          FromEmail, StrSubstNo(WelcomeEmailFromNameTxt, PRODUCTNAME.Full()), RecipientEmail);

        Session.LogMessage('0000BXO', SampleEmailsDeployedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
    end;

    [TryFunction]
    local procedure ValidateCredentials()
    begin
        if not ExchangeWebServicesServer.ValidCredentials() then begin
            if StrPos(GetLastErrorText, '401') > 0 then begin
                Session.LogMessage('0000BXP', InvalidCredentialsTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
                Error(InvalidCredentialsErr);
            end;
            Session.LogMessage('0000BXQ', NoMailboxTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
            Error(NoMailboxErr);
        end;
        Session.LogMessage('0000BXR', ValidCredentialsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', ExchangeTelemetryCategoryTxt);
    end;

    local procedure GetFromEmail(): Text
    var
        ContactBusinessRelation: Record "Contact Business Relation";
        Contact: Record Contact;
        FirstContactEmail: Text;
    begin
        ContactBusinessRelation.SetRange("Link to Table", ContactBusinessRelation."Link to Table"::Customer);
        if ContactBusinessRelation.FindSet() then
            repeat
                if Contact.Get(ContactBusinessRelation."Contact No.") then begin
                    if (FirstContactEmail = '') and (Contact."E-Mail" <> '') then
                        FirstContactEmail := Contact."E-Mail";
                    if Contact."E-Mail" = DemoCompanyEmailTxt then
                        exit(Contact."E-Mail");
                end;
            until ContactBusinessRelation.Next() = 0;

        if FirstContactEmail <> '' then
            exit(FirstContactEmail);
    end;
}
#endif