OfficeDev / office-js

A repo and NPM package for Office.js, corresponding to a copy of what gets published to the official "evergreen" Office.js CDN, at https://appsforoffice.microsoft.com/lib/1/hosted/office.js.
https://learn.microsoft.com/javascript/api/overview
Other
670 stars 95 forks source link

displayDialogAsync in ItemSend callback doesn't work when OnSendAddinsEnabled is true #4858

Open nivgoldsteiniron opened 1 week ago

nivgoldsteiniron commented 1 week ago

Provide required information needed to triage your issue

In the callback for ItemSend I run displayDialogAsync to display a dialog to the user to notify them there is suspicious content in the email and ask them if they want to send the email. (In reality, it accesses a backend. But in the example I provided it will always show the dialog) We can see the dialog when OnSendAddinsEnabled is false, but when its true, the dialog can't be seen.

Your Environment

Expected behavior

The dialog popup should also to be shown when OnSendAddinsEnabled is true. Currently, I only see it dialog when OnSendAddinsEnabled is false.

Steps to reproduce

  1. install addin.xml
  2. Set OnSendAddinsEnabled to true
  3. Try to send an email. You won't see a dialog asking you whether you want to send the email
  4. If OnSendAddinsEnabled is false, you should see a dialog (just make sure popups are allowed on your browser)

addin.xml

<OfficeApp
        xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
        xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:type="MailApp">
    <Id>332980cc-1e81-40e8-9e69-7cd1b4db3756</Id>
    <Version>1.0.0</Version>
    <ProviderName>ASDF</ProviderName>
    <DefaultLocale>en-US</DefaultLocale>
    <DisplayName DefaultValue="Accidental Data Exposure"/>
    <Description DefaultValue="Accidental Data Exposure"/>
    <Hosts>
        <Host Name="Mailbox"/>
    </Hosts>
    <Requirements>
        <Sets>
            <Set Name="MailBox" MinVersion="1.1"/>
        </Sets>
    </Requirements>
    <FormSettings>
        <Form xsi:type="ItemRead">
            <DesktopSettings>
                <SourceLocation DefaultValue="https://ade-plugin-staging.s3.amazonaws.com/ade/functions.html" />
                <RequestedHeight>100</RequestedHeight>
            </DesktopSettings>
        </Form>
    </FormSettings>
    <Permissions>ReadWriteMailbox</Permissions>
    <Rule xsi:type="RuleCollection" Mode="Or">
        <Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" IncludeSubClasses="true"/>
    </Rule>
    <DisableEntityHighlighting>false</DisableEntityHighlighting>
    <VersionOverrides
            xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
            xsi:type="VersionOverridesV1_0">
        <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
                          xsi:type="VersionOverridesV1_1">
            <Hosts>
                <Host xsi:type="MailHost">
                    <DesktopFormFactor>
                        <SupportsSharedFolders>true</SupportsSharedFolders>
                        <FunctionFile resid="function-file-url"/>
                        <ExtensionPoint xsi:type="Events">
                            <Event Type="ItemSend" FunctionExecution="synchronous"
                FunctionName="handleItemSend" />
                        </ExtensionPoint>
                    </DesktopFormFactor>
                </Host>
            </Hosts>
            <Resources>
                <bt:Images>
                </bt:Images>
                <bt:Urls>
          <bt:Url id="function-file-url"
            DefaultValue="https://ade-plugin-staging.s3.amazonaws.com/ade2/functions.html" />
                </bt:Urls>
                <bt:ShortStrings>
                    <bt:String id="add-in-label" DefaultValue="Accidental Data Exposure"/>
                </bt:ShortStrings>
            </Resources>
        </VersionOverrides>
    </VersionOverrides>
</OfficeApp>

functions file simplified to not interact with BE to focus on the issue at hand: https://ade-plugin-staging.s3.amazonaws.com/ade2/functions.html

<html lang="en">

<head>
    <title>No UI</title>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
    <script>
        START_TIME = (new Date()).getTime()

        const DIALOG_HEIGHT = 60;
        const DIALOG_WIDTH = 35;

        function getPathUrl(path) {
            const url = new URL(window.location.href);
            url.pathname = url.pathname.replace("functions.html", path);
            url.search = "";
            url.hash = "";

            return url.href
        }

        function timedLog(s) {
            let total_ms = ((new Date()).getTime() - START_TIME);
            console.log(`Timed log (${total_ms}ms) ${s}`);
        }

        timedLog("Start log");

        function handleItemSend(e) {

            timedLog("start handleItemSend");
            let eventArgs = e;

            function processDialogResult(editConfirmed, eventArgs) {
                return () => {
                    eventArgs.completed({allowEvent: !editConfirmed});
                }
            }

            const approveFn = processDialogResult(
                false,
                eventArgs,
            );
            const cancelFn = processDialogResult(
                true,
                eventArgs,
            );

            function eventHandler(arg, approveFn) {
                if (arg.error) {
                    approveFn();
                }
            }

            function processMessage(arg, approveFn, cancelFn, adeMessage) {
                const messageFromDialog = JSON.parse(arg.message);
                if (messageFromDialog.result === "isReady") {
                    dialog.messageChild(
                        JSON.stringify({adeMessage})
                    );
                } else {
                    if (messageFromDialog.result === "yes") {
                        approveFn();
                    } else {
                        cancelFn();
                    }
                    dialog.close();
                    Office.context.ui.closeContainer();
                }
            }

            const adeMessage = {
                title: "Accidental Data Exposure",
                body: "This email contains sensitive information. Are you sure you want to send it?"
            };
            const dialogUrl = getPathUrl('dialog.html');
            timedLog(`Dialog URL ${dialogUrl}`);

            Office.context.ui.displayDialogAsync(
                dialogUrl,
                {
                    height: DIALOG_HEIGHT,
                    width: DIALOG_WIDTH,
                    promptBeforeOpen: false,
                },
                function (asyncResult) {
                    timedLog(`asyncResult start ${asyncResult.status}`);

                    // In case if dialog not opened by error (blocked popup) we are approving sending
                    if (asyncResult.status === Office.AsyncResultStatus.Failed) {
                        eventArgs.completed({allowEvent: true});
                    }

                    dialog = asyncResult.value;
                    dialog.addEventHandler(Office.EventType.DialogMessageReceived, (arg) => processMessage(arg, approveFn, cancelFn, adeMessage));
                    dialog.addEventHandler(Office.EventType.DialogEventReceived, (arg) => eventHandler(arg, approveFn));
                    timedLog(`asyncResult end`);

                }
            );
        }

        Office.initialize = function () {
        }
        timedLog("Finish loading definitions");
    </script>
</head>

<body>
This page is left blank intentionally.
</body>

</html>

https://ade-plugin-staging.s3.amazonaws.com/ade2/dialog.html

<html>
<body>
<!--StartFragment-->
  | <!DOCTYPE html>
-- | --
  | <html lang="en">
  |  
  | <head>
  | 
  | <meta http-equiv="X-UA-Compatible" content="IE=edge">
  | <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
  | <meta name="viewport" content="width=device-width, initial-scale=1.0">
  | <link href="https://fonts.cdnfonts.com/css/open-sans" rel="stylesheet">
  | <title>Ironscales ADE</title>
  | </head>
  | <style>
  | @import url('https://fonts.cdnfonts.com/css/open-sans');
  |  
  | * {
  | box-sizing: border-box;
  | }
  |  
  | body {
  | font-family: 'Open Sans', sans-serif;
  | height: 100vh;
  | margin: 0;
  | display: grid;
  | }
  |  
  | * ::-webkit-scrollbar {
  | width: 4px;
  | }
  |  
  | * ::-webkit-scrollbar-thumb {
  | -webkit-border-radius: 4px;
  | border-radius: 4px;
  | background: #e4e4e4;
  | }
  |  
  | .hr {
  | width: 100%;
  | border-bottom: 1px solid #E6E9EE;
  | margin: 0;
  | height: 1px;
  | }
  |  
  | .footer_img {
  | width: 61px;
  | margin: 0 5px;
  | }
  |  
  | .footer {
  | padding: 5px;
  | flex: 1;
  | display: flex;
  | flex-direction: column;
  | justify-content: flex-end;
  | gap: 16px;
  | }
  |  
  | .container {
  | padding: 16px 40px;
  | font-size: 14px;
  | background: #fff;
  | display: flex;
  | gap: 16px;
  | justify-content: center;
  | flex-direction: column;
  | }
  |  
  | .title {
  | display: flex;
  | gap: 9px;
  | font-weight: 600;
  | }
  |  
  | .h1 {
  | font-size: 20px;
  | color: #2F354A;
  | }
  |  
  | .buttons {
  | display: flex;
  | justify-content: space-between;
  | width: 100%;
  | gap: 16px;
  | }
  |  
  | .buttons .actions {
  | display: flex;
  | justify-content: center;
  | align-items: center;
  | gap: 16px;
  | }
  |  
  | .buttons .ironscales_logo {
  | display: flex;
  | justify-content: center;
  | align-items: center;
  | }
  |  
  | @media (max-width: 480px) {
  | .buttons {
  | flex-direction: column-reverse;
  | }
  |  
  | .buttons .actions {
  | justify-content: space-between;
  | }
  | }
  |  
  | .btn {
  | border-radius: 4px;
  | padding: 8px 12px;
  | font-size: 14px;
  | font-weight: 600;
  | color: #2E41EA;
  | text-decoration: none;
  | border: 1px solid #2E41EA;
  | background-color: transparent;
  | cursor: pointer;
  | }
  |  
  | .btn:active {
  | transform: translate(0px, 2px);
  | -webkit-transform: translate(0px, 2px);
  | }
  |  
  | .btn:hover {
  | background-color: #E6E9EE;
  | }
  |  
  | .blue {
  | color: #fff;
  | background-color: #2E41EA;
  | }
  |  
  | .blue:hover {
  | background-color: #1D2CB2;
  | }
  |  
  | .list {
  | list-style-type: none;
  | padding: 0;
  | margin-top: 16px;
  | margin-bottom: 22px;
  | }
  |  
  | .list li {
  | margin-bottom: 5px;
  | }
  |  
  | .content {
  | display: flex;
  | height: 100%;
  | line-height: 22px;
  | }
  | </style>
  |  
  | <body>
  | <div class="container">
  | <div class="title">
  | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none">
  | <path
  | d="M21.601 18.707L12.601 1.60698C12.4485 1.31653 12.2194 1.0733 11.9387 0.903575C11.6579 0.733849 11.3361 0.644084 11.008 0.643982C10.68 0.644084 10.3582 0.733849 10.0774 0.903575C9.79663 1.0733 9.56759 1.31653 9.41504 1.60698L0.41504 18.707C0.265186 18.9853 0.191281 19.2982 0.200761 19.6142C0.21024 19.9302 0.30277 20.2381 0.46904 20.507C0.633051 20.766 0.860507 20.9789 1.12984 21.1253C1.39918 21.2718 1.70146 21.3471 2.00804 21.344H20.008C20.3179 21.3439 20.6224 21.2638 20.8922 21.1115C21.162 20.9592 21.388 20.7399 21.5482 20.4747C21.7084 20.2095 21.7974 19.9075 21.8067 19.5978C21.8159 19.2881 21.7451 18.9813 21.601 18.707ZM10.108 7.84398C10.108 7.60529 10.2029 7.37637 10.3716 7.20759C10.5404 7.0388 10.7693 6.94398 11.008 6.94398C11.2467 6.94398 11.4757 7.0388 11.6444 7.20759C11.8132 7.37637 11.908 7.60529 11.908 7.84398V13.244C11.908 13.4827 11.8132 13.7116 11.6444 13.8804C11.4757 14.0492 11.2467 14.144 11.008 14.144C10.7693 14.144 10.5404 14.0492 10.3716 13.8804C10.2029 13.7116 10.108 13.4827 10.108 13.244V7.84398ZM11.053 18.194C10.6986 18.1919 10.3585 18.0533 10.1037 17.8068C9.8489 17.5604 9.69894 17.2252 9.68504 16.871C9.67902 16.6931 9.70873 16.5157 9.7724 16.3495C9.83608 16.1833 9.93243 16.0315 10.0558 15.9031C10.1791 15.7747 10.3269 15.6724 10.4905 15.6021C10.654 15.5319 10.83 15.4951 11.008 15.494C11.3625 15.496 11.7026 15.6347 11.9574 15.8811C12.2122 16.1276 12.3621 16.4628 12.376 16.817C12.3821 16.9949 12.3524 17.1722 12.2887 17.3385C12.225 17.5047 12.1286 17.6565 12.0053 17.7849C11.882 17.9132 11.7342 18.0156 11.5706 18.0858C11.407 18.1561 11.2311 18.1929 11.053 18.194Z"
  | fill="#FFA800" />
  | </svg>
  | <span class="h1" id="adeMessageTitle">Accidental data exposure</span>
  | </div>
  | <div class="content">
  | <div style="flex: 1;" id="adeMessageBody"></div>
  | </div>
  |  
  | <div class="footer">
  | <div class="hr"></div>
  | <div class="buttons">
  | <div class="ironscales_logo">
  | <div style="display: flex; align-items:center;">
  | <span style="font-size: 10px;">Powered by</span>
  | <img src="https://res.cloudinary.com/dwai4phgu/image/upload/v1656859772/Shape_rtfsjn.png"
  | class="footer_img" />
  | </div>
  | </div>
  | <div class="actions">
  | <button id="sendBtn" class="btn">Send anyway</button>
  | <button id="editBtn" class="btn blue">Do not send</button>
  | </div>
  | </div>
  | </div>
  | </div>
  |  
  | <script>
  | Office.initialize = function () {
  | Office.context.ui.addHandlerAsync(Office.EventType.DialogParentMessageReceived, onMessageFromParent)
  | sendMessage('isReady')
  | }
  |  
  | function onMessageFromParent(arg) {
  | const messageFromParent = JSON.parse(arg.message);
  |  
  | const adeMessageTitle = messageFromParent?.adeMessage?.title
  | const adeMessageTitleContainer = document.getElementById('adeMessageTitle')
  |  
  | if (adeMessageTitle && adeMessageTitleContainer) {
  | adeMessageTitleContainer.innerHTML = adeMessageTitle
  | }
  |  
  | const adeMessageBody = messageFromParent?.adeMessage?.body
  | const adeMessageBodyContainer = document.getElementById('adeMessageBody')
  |  
  | if (adeMessageBody && adeMessageBodyContainer) {
  | adeMessageBodyContainer.innerHTML = adeMessageBody
  | }
  | }
  |  
  | function sendMessage(message) {
  | const parsedMessage = {
  | result: message
  | }
  | Office.context.ui.messageParent(JSON.stringify(parsedMessage), { targetOrigin: "*" })
  | }
  |  
  |  
  | function pressedYes() {
  | sendMessage('yes')
  | }
  |  
  | function pressedNo() {
  | sendMessage('no')
  | }
  |  
  | const sendButton = document.getElementById("sendBtn")
  | sendButton.addEventListener('click', pressedYes)
  | const editButton = document.getElementById("editBtn")
  | editButton.addEventListener('click', pressedNo)
  |  
  | </script>
  |  
  | </body>
  |  
  | </html>

<!--EndFragment-->
</body>
</html>

Context

This breaks the feature, customers can't see the dialog

rkpathak commented 3 days ago

@nivgoldsteiniron Please host the minimum addin code that we can run to repo in a private repo and share it with @exextoc. This will help us run and root cause the issue.

nivgoldsteiniron commented 2 days ago

@nivgoldsteiniron Please host the minimum addin code that we can run to repo in a private repo and share it with @exextoc. This will help us run and root cause the issue.

How should I share it? By private message?

nivgoldsteiniron commented 2 days ago

@nivgoldsteiniron Please host the minimum addin code that we can run to repo in a private repo and share it with @exextoc. This will help us run and root cause the issue.

@exextoc I'm publicly hosting the files on an S3 bucket. I'll make sure that they will stay there for the near future. Let me know if you need any other assistance to reproduce. https://ade-plugin-staging.s3.amazonaws.com/ade2/functions.html https://ade-plugin-staging.s3.amazonaws.com/ade2/dialog.html The addin manifest XML should be copy pasted and saved into a local XML file, and be installed manually.