SharePoint:commonModalDialogClose不会关闭跨域对话框

时间:2011-08-18 11:18:15

标签: sharepoint sharepoint-2010

我在“virtualcasa1”域中托管了一个页面,打开了一个模态对话框:

var options = {
    title: "Repro",
    width: 400,
    height: 600,
    url: http://domain2:999/sites/blank/_layouts/XDomainTest/XDomainTestTarget.aspx //[1]
    //url: http://virtualcasa1/sites/blank/_layouts/XDomainTest/XDomainTestTarget.aspx [2]
};
SP.UI.ModalDialog.showModalDialog(options);

我有这个代码来关闭它:

alert(document.domain);
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancelled clicked'); 

如果两者都在同一个域中(上面的情况[2]),则对话框关闭,没有问题。

但是 - 如果目标页面在对话框中托管(上面的案例[1]),对话框不会关闭: - (

上面的document.domain显示了页面存在的正确域名。

我怀疑我在这里遇到跨域问题(duh),但是如何修复它?或者我错了,问题与XDomain无关?

非常感谢!

3 个答案:

答案 0 :(得分:3)

HTML5的postMessage就是您的答案。

https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage

启动对话框的父窗口必须包含以下javascript:

function listener(event) {
  //alert(event.data);
  if (event.data == 'Cancel') {
    SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancel clicked');
  }
  else {
    SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, event.data);
  }
}

if (window.addEventListener) {
  addEventListener("message", listener, false)
} else {
  attachEvent("onmessage", listener)
}

弹出窗口中的确定和取消按钮的Javascript:

<input type="button" value="OK" onclick="parent.postMessage('Message to be displayed by the parent', '*');" class="ms-ButtonHeightWidth" />
<input type="button" value="Cancel" onclick="parent.postMessage('Cancel', '*');" class="ms-ButtonHeightWidth" />

答案 1 :(得分:0)

我有完全相同的问题 - 当从同一个网络应用程序/域上的网站集打开时,打开项目视图页面的对话框工作正常,但是当从网站打开相同的项目时,关闭按钮无法工作集合托管在单独的Web应用程序中。我假设它是一个跨域的东西,所以我已经改变了解决方案以适应这个限制,但是,我并不是百分之百满意,因为它确实使整个解决方案从用户使用有点尴尬 - 透视。由于项目的时间表,我现在把问题放在一边,但我仍然很好奇为什么。我能想到的唯一的事情是导致它的整个跨域事情,也许它可以通过设计来防止XSS安全漏洞。

答案 2 :(得分:0)

Ajay在2014年8月1日的答案很好,但需要更多解释。无法关闭对话框的原因很简单。现代浏览器的跨站点脚本安全功能不允许做一些事情,其中​​之一就是在框架窗口中使用window.frameElement。这是窗口对象上的只读属性,它设置为null(或者使用IE,它实际上在您尝试访问它时会引发异常)。模态对话框中的普通Cancel事件处理程序以对window.frameElement.cancelPopup()的调用结束。这当然会失败。 Save在服务器端工作的普通Save处理程序导致SharePoint发回一行作为替换文档,这是一个调用window.frameElement.commitPopup()的scriptlet。这也是行不通的,因为页面已经重新加载并且没有可用于处理任何内容的脚本,所以要克服它真的很痛苦。 XSS不允许我们从呼叫页面访问带框架的DOM。

为了使跨域托管表单无缝地工作,您需要将脚本添加到打开对话框和框架页面的页面。在打开对话框的页面中,按照Ajay的建议设置消息监听器。在框架表单页面中,您需要以下内容:

(function() {
    $(document).ready(function() {
        var frameElement = null;
        // Try/catch to overcome IE Access Denied exception on window.frameElement
        try {
            frameElement = window.frameElement;
        } catch (Exception) {}

        // Determine that the page is hosted in a dialog from a different domain
        if (window.parent && !frameElement) {
            // Set the correct height for #s4-workspace
            var frameHeight = $(window).height();
            var ribbonHeight = $('#s4-ribbonrow').height();
            $('#s4-workspace').height(frameHeight - ribbonHeight);

            // Finds the Save and Cancel buttons and hijacks the onclick
            function applyClickHandlers(theDocument) {
                $(theDocument).find('input[value="Cancel"]').removeAttr('onclick').on('click', doTheClose);
                $(theDocument).find('a[id="Ribbon.ListForm.Edit.Commit.Cancel-Large"]').removeAttr('onclick').removeAttr('href').on('click', doTheClose);
                $(theDocument).find('input[value="Save"]').removeAttr('onclick').on('click', doTheCommit);
                $(theDocument).find('a[id="Ribbon.ListForm.Edit.Commit.Publish-Large"]').removeAttr('onclick').removeAttr('href').on('click', doTheCommit);
            }

            // Function to perform onclick for Cancel
            function doTheClose(evt) {
                evt.preventDefault();
                parent.postMessage('Cancel', '*');
            }

            // Function to perform onclick for Save
            function doTheCommit(evt) {
                evt.preventDefault();

                if (!PreSaveItem()) return false;
                var targetName = $('input[value="Save"]').attr('name');
                var oldOnSubmit = WebForm_OnSubmit;
                WebForm_OnSubmit = function() {
                    var retVal = oldOnSubmit.call(this);
                    if (retVal) {
                        var theForm = $('#aspnetForm');
                        // not sure whether following line is needed,
                        // but doesn't hurt
                        $('#__EVENTTARGET').val(targetName);
                        var formData = new FormData(theForm[0]);
                        $.ajax(
                        {
                            url: theForm.attr('action'),
                            data: formData,
                            cache: false,
                            contentType: false,
                            processData: false,
                            method: 'POST',
                            type: 'POST', // For jQuery < 1.9
                            success: function(data, status, transport) {
                                console.log(arguments);
                                // hijack the response if it's just script to
                                // commit the popup (which will break)
                                if (data.startsWith('<script') &&
                                    data.indexOf('.commitPopup()') > -1)
                                {
                                    parent.postMessage('OK', '*');
                                    return;
                                }

                                // popup not being committed, so actually
                                // submit the form and replace the page.
                                theForm.submit();
                            }
                        }).fail(function() {
                            console.log('Ajax post failed.');
                            console.log(arguments);
                        });
                    }

                    return false;
                }
                WebForm_DoPostBackWithOptions(
                    new WebForm_PostBackOptions(targetName,
                                                "",
                                                true,
                                                "",
                                                "",
                                                false,
                                                true)
                );
                WebForm_OnSubmit = oldOnSubmit;
            }

            applyClickHandlers(document);
        }
    });
})();

该解决方案利用了我们组织广泛使用的jQuery库。这是我们的首选框架(由我选择)。我确信有一个非常聪明的人可以在没有这种依赖的情况下重写它,但这是一个很好的起点。我希望有人发现它有用,因为它代表了一个很好的两天工作。有些事情需要注意:

SharePoint会对页面上的各种事件进行回发,包括将页面置于编辑模式。因此,在表单和功能区中捕获特定按钮单击更有意义,而不是批量重新定义(例如,全局WebForm_OnSubmit函数)。我们在保存点击上暂时覆盖它,然后将其重新设置。

在任何Save click事件中,我们都会失败表单的正常发布,并使用AJAX将其替换为相同的POST请求。这允许我们在成功发布表单时丢弃返回的scriptlet。当表单提交不成功时,可能是因为需要空白的值,我们只需正确发布表单以允许页面更新。这很好,因为表单不会被处理。此解决方案的早期版本采用生成的HTML文档并替换了所有页面内容,但Internet Explorer并不喜欢这样。

FormData api允许我们post the form as multipart-mime。这个api至少在所有现代浏览器中都有基本支持,并且有旧版本的解决方法。

跨域托管对话框中似乎失败的另一件事是滚动内容窗口。无论出于何种原因,在id为s4-workspace的div上未正确设置高度,因此我们也在解决方案中设置了这个高度。

编辑: 差点忘了。您可能还需要将此控件添加到框架ASPX页面,这可以使用SharePoint Designer完成:

&lt; WebPartPages:AllowFraming runat =&#34; server&#34; /&gt;