我在“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无关?
非常感谢!
答案 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;