1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file defines the import/export controller. 27 * 28 */ 29 30 /** 31 * Creates an import/export controller. 32 * @class 33 * This class represents an import/export controller. 34 * 35 * @extends ZmController 36 */ 37 ZmImportExportController = function() { 38 ZmController.call(this, null); 39 }; 40 41 ZmImportExportController.prototype = new ZmController; 42 ZmImportExportController.prototype.constructor = ZmImportExportController; 43 44 /** 45 * Returns a string representation of the object. 46 * 47 * @return {String} a string representation of the object 48 */ 49 ZmImportExportController.prototype.toString = function() { 50 return "ZmImportExportController"; 51 }; 52 53 // 54 // Constants 55 // 56 57 ZmImportExportController.IMPORT_TIMEOUT = 300; 58 59 /** 60 * Defines the "CSV" type. 61 * @type {String} 62 */ 63 ZmImportExportController.TYPE_CSV = "csv"; 64 /** 65 * Defines the "ICS" type. 66 * @type {String} 67 */ 68 ZmImportExportController.TYPE_ICS = "ics"; 69 /** 70 * Defines the "TGZ" type. 71 * @type {String} 72 */ 73 ZmImportExportController.TYPE_TGZ = "tgz"; 74 75 /** 76 * Defines the default type. 77 * 78 * @see ZmImportExportController.TYPE_TGZ 79 */ 80 ZmImportExportController.TYPE_DEFAULT = ZmImportExportController.TYPE_TGZ; 81 82 /** 83 * Defines the sub-type default array 84 */ 85 ZmImportExportController.SUBTYPE_DEFAULT = {}; 86 ZmImportExportController.SUBTYPE_DEFAULT[ZmImportExportController.TYPE_TGZ] = ZmImportExportController.SUBTYPE_ZIMBRA_TGZ; 87 ZmImportExportController.SUBTYPE_DEFAULT[ZmImportExportController.TYPE_CSV] = ZmImportExportController.SUBTYPE_ZIMBRA_CSV; 88 ZmImportExportController.SUBTYPE_DEFAULT[ZmImportExportController.TYPE_ICS] = ZmImportExportController.SUBTYPE_ZIMBRA_ICS; 89 90 ZmImportExportController.TYPE_EXTS = {}; 91 ZmImportExportController.TYPE_EXTS[ZmImportExportController.TYPE_CSV] = [ "csv", "vcf" ]; 92 ZmImportExportController.TYPE_EXTS[ZmImportExportController.TYPE_ICS] = [ "ics" ]; 93 ZmImportExportController.TYPE_EXTS[ZmImportExportController.TYPE_TGZ] = [ "tgz", "zip", "tar" ]; 94 95 ZmImportExportController.EXTS_TYPE = {}; 96 AjxUtil.foreach(ZmImportExportController.TYPE_EXTS, function(exts, p) { 97 for (var i = 0; i < exts.length; i++) { 98 ZmImportExportController.EXTS_TYPE[exts[i]] = p; 99 } 100 }); 101 102 ZmImportExportController.__FAULT_ARGS_MAPPING = { 103 "formatter.INVALID_FORMAT": [ "filename" ], 104 "formatter.INVALID_TYPE": [ "view", "path" ], 105 "formatter.MISMATCHED_META": [ "path" ], 106 "formatter.MISMATCHED_SIZE": [ "path" ], 107 "formatter.MISMATCHED_TYPE": [ "path" ], 108 "formatter.MISSING_BLOB": [ "path" ], 109 "formatter.MISSING_META": [ "path" ], 110 "formatter.MISSING_VCARD_FIELDS": [ "path" ], 111 "formatter.UNKNOWN_ERROR": [ "path", "message" ], 112 "mail.EXPORT_PERIOD_TOO_LONG": [ "limit" ] 113 }; 114 115 116 ZmImportExportController.CSRF_TOKEN_HIDDEN_INPUT_ID = "ZmImportExportCsrfToken"; 117 // 118 // Public methods 119 // 120 121 /** 122 * Imports user data as specified in the <code>params</code> object. 123 * 124 * @param {Hash} params a hash of parameters 125 * @param {Element} params.form the form containing file input field 126 * @param {String} params.folderId the folder id for import. If not specified, assumes import to root folder. 127 * @param {String} params.type the type (defaults to {@link TYPE_TGZ}) 128 * @param {String} params.subType the sub-type (defaults to <code>SUBTYPE_DEFAULT[type]</code>) 129 * @param {String} params.resolve resolve duplicates: "" (ignore), "modify", "replace", "reset" (defaults to ignore). 130 * @param {String} params.views a comma-separated list of views 131 * @param {AjxCallback} callback the callback for success 132 * @param {AjxCallback} errorCallback the callback for errors 133 * 134 * @return {Boolean} <code>true</code> if the import is successful 135 */ 136 ZmImportExportController.prototype.importData = 137 function(params) { 138 // error checking 139 params = params || {}; 140 var folderId = params.folderId || -1; 141 if (folderId == -1) { 142 var params = { 143 msg: ZmMsg.importErrorMissingFolder, 144 level: ZmStatusView.LEVEL_CRITICAL 145 }; 146 appCtxt.setStatusMsg(params); 147 return false; 148 } 149 150 params.filename = params.form && params.form.elements["file"].value; 151 if (!params.filename) { 152 var params = { 153 msg: ZmMsg.importErrorMissingFile, 154 level: ZmStatusView.LEVEL_CRITICAL 155 }; 156 appCtxt.setStatusMsg(params); 157 return false; 158 } 159 160 params.ext = params.filename.replace(/^.*\./,"").toLowerCase(); 161 if (!ZmImportExportController.EXTS_TYPE[params.ext]) { 162 var params = { 163 msg: AjxMessageFormat.format(ZmMsg.importErrorTypeNotSupported, params.ext), 164 level: ZmStatusView.LEVEL_CRITICAL 165 }; 166 appCtxt.setStatusMsg(params); 167 return false; 168 } 169 params.defaultType = params.type || ZmImportExportController.EXTS_TYPE[params.ext] || ZmImportExportController.TYPE_DEFAULT; 170 var isZimbra = ZmImportExportController.EXTS_TYPE[params.defaultType] == ZmImportExportController.TYPE_TGZ; 171 var folder = appCtxt.getById(folderId); 172 if (!isZimbra && folder && folder.nId == ZmOrganizer.ID_ROOT) { 173 var params = { 174 msg: ZmMsg.importErrorRootNotAllowed, 175 level: ZmStatusView.LEVEL_CRITICAL 176 }; 177 appCtxt.setStatusMsg(params); 178 return false; 179 } 180 181 if (params.resolve == "reset") { 182 var dialog = appCtxt.getOkCancelMsgDialog(); 183 dialog.registerCallback(DwtDialog.OK_BUTTON, this._confirmImportReset, this, [params]); 184 dialog.registerCallback(DwtDialog.CANCEL_BUTTON, this._cancelImportReset, this); 185 var msg = ZmMsg.importResetWarning; 186 var style = DwtMessageDialog.WARNING_STYLE; 187 dialog.setMessage(msg, style); 188 dialog.popup(); 189 return false; 190 } 191 192 // import 193 return this._doImportData(params); 194 }; 195 196 /** 197 * Exports user data as specified in the <code>params</code> object. 198 * 199 * @param {Hash} params a hash of parameters 200 * @param {String} params.folderId the folder id for export. If not specified, all folders want to be exported. 201 * @param {String} params.type the type (defaults to {@link TYPE_TGZ}) 202 * @param {String} params.subType the sub-type (defaults to <code>SUBTYPE_DEFAULT[type]</code>) 203 * @param {String} params.views a comma-separated list of views 204 * @param {String} params.filename the filename for exported file 205 * @param {String} params.searchFilter the search filter 206 * @param {Boolean} params.skipMeta if <code>true</code>, skip export of meta-data 207 * @param {AjxCallback} callback the callback for success 208 * @param {AjxCallback} errorCallback the callback for errors 209 * 210 * @return {Boolean} <code>true</code> if the export is successful 211 */ 212 ZmImportExportController.prototype.exportData = function(params) { 213 // error checking 214 params = params || {}; 215 var folderId = params.folderId || -1; 216 if (folderId == -1) { 217 var params = { 218 msg: ZmMsg.exportErrorMissingFolder, 219 level: ZmStatusView.LEVEL_CRITICAL 220 }; 221 appCtxt.setStatusMsg(params); 222 return false; 223 } 224 225 var type = params.type = params.type || ZmImportExportController.TYPE_DEFAULT; 226 var isZimbra = ZmImportExportController.EXTS_TYPE[type] == ZmImportExportController.TYPE_TGZ; 227 var folder = appCtxt.getById(folderId); 228 if (!isZimbra && folder && folder.nId == ZmOrganizer.ID_ROOT) { 229 var params = { 230 msg: ZmMsg.exportErrorRootNotAllowed, 231 level: ZmStatusView.LEVEL_CRITICAL 232 }; 233 appCtxt.setStatusMsg(params); 234 return false; 235 } 236 237 // export 238 return this._doExportData(params); 239 }; 240 241 // 242 // Protected methods 243 // 244 245 /** 246 * @private 247 */ 248 ZmImportExportController.prototype._doImportData = 249 function(params) { 250 if (params.folderId == -1 && params.defaultType != ZmImportExportController.TYPE_TGZ) { 251 return this._doImportSelectFolder(params); 252 } 253 return this._doImport(params); 254 }; 255 256 /** 257 * @private 258 */ 259 ZmImportExportController.prototype._doImportSelectFolder = 260 function(params) { 261 var dialog = appCtxt.getChooseFolderDialog(); 262 dialog.reset(); 263 dialog.setTitle(ZmMsg._import); 264 dialog.registerCallback(DwtDialog.OK_BUTTON, this._doImportSelectFolderDone, this, [params]); 265 266 var overviewId = dialog.getOverviewId(ZmSetting.IMPORT); 267 var omit = {}; 268 omit[ZmFolder.ID_TRASH] = true; 269 var type = params.defaultType; 270 if (type == ZmImportExportController.TYPE_CSV) { 271 AjxDispatcher.require(["ContactsCore", "Contacts"]); 272 var noNew = !appCtxt.get(ZmSetting.NEW_ADDR_BOOK_ENABLED); 273 dialog.popup({ 274 treeIds: [ZmOrganizer.ADDRBOOK], 275 title: ZmMsg.chooseAddrBook, 276 overviewId: overviewId, 277 description: ZmMsg.chooseAddrBookToImport, 278 skipReadOnly: true, 279 hideNewButton: noNew, 280 omit: omit, 281 appName: ZmApp.CONTACTS 282 }); 283 } 284 else if (type == ZmImportExportController.TYPE_ICS) { 285 AjxDispatcher.require(["MailCore", "CalendarCore", "Calendar"]); 286 dialog.popup({ 287 treeIds: [ZmOrganizer.CALENDAR], 288 title: ZmMsg.chooseCalendar, 289 overviewId: overviewId, 290 description: ZmMsg.chooseCalendarToImport, 291 skipReadOnly: true, 292 omit:omit, 293 appName:ZmApp.CALENDAR 294 }); 295 } 296 }; 297 298 /** 299 * @private 300 */ 301 ZmImportExportController.prototype._doImportSelectFolderDone = 302 function(params, organizer) { 303 params.folderId = organizer.id; 304 this._doImport(params); 305 }; 306 307 /** 308 * @private 309 */ 310 ZmImportExportController.prototype._doImport = 311 function(params) { 312 // create custom callback function for this import request 313 var funcName = "ZmImportExportController__callback__"+Dwt.getNextId("import"); 314 window[funcName] = AjxCallback.simpleClosure(this._handleImportResponse, this, funcName, params); 315 316 // generate request url 317 var folder = params.folderId && appCtxt.getById(params.folderId); 318 if (folder && folder.nId == ZmOrganizer.ID_ROOT) folder = null; 319 var path = folder ? folder.getPath(null, null, null, true, true) : ""; 320 var type = params.type || params.ext; 321 var url = [ 322 "/home/", 323 encodeURIComponent(appCtxt.get(ZmSetting.USERNAME)), 324 "/", 325 encodeURIComponent(path), 326 "?", 327 type ? "fmt="+encodeURIComponent(type) : "", 328 params.views ? "&types="+encodeURIComponent(params.views) : "", 329 params.resolve ? "&resolve="+encodeURIComponent(params.resolve) : "", 330 "&callback="+funcName, 331 "&charset="+appCtxt.getCharset() 332 ].join(""); 333 334 // initialize form 335 var form = params.form; 336 form.action = url; 337 form.method = "POST"; 338 form.enctype = "multipart/form-data"; 339 340 this._setCsrfTokenInput(form); 341 342 // destination iframe 343 var onload = null; 344 var onerror = AjxCallback.simpleClosure(this._importError, this, params.errorCallback); 345 params.iframe = ZmImportExportController.__createIframe(form, onload, onerror); 346 347 // import 348 form.submit(); 349 return true; 350 }; 351 352 /** 353 * lazily generate the hidden csrf token input field for the form. 354 */ 355 ZmImportExportController.prototype._setCsrfTokenInput = 356 function(form) { 357 var csrfTokenInput = document.getElementById(ZmImportExportController.CSRF_TOKEN_HIDDEN_INPUT_ID); 358 if (csrfTokenInput) { 359 return; 360 } 361 csrfTokenInput = document.createElement("input"); 362 csrfTokenInput.type = "hidden"; 363 csrfTokenInput.name = "csrfToken"; 364 csrfTokenInput.id = ZmImportExportController.CSRF_TOKEN_HIDDEN_INPUT_ID; 365 csrfTokenInput.value = window.csrfToken; 366 367 var firstChildEl = form.firstChild; 368 form.insertBefore(csrfTokenInput, firstChildEl); 369 }; 370 371 /** 372 * @private 373 */ 374 ZmImportExportController.prototype._handleImportResponse = 375 function(funcName, params, type, fault1 /* , ... , faultN */) { 376 // gather error/warning messages 377 var messages = []; 378 if (fault1) { 379 for (var j = 3; j < arguments.length; j++) { 380 var fault = arguments[j]; 381 var code = fault.Detail.Error.Code; 382 var message = fault.Reason.Text; 383 var args = ZmImportExportController.__faultArgs(fault.Detail.Error.a); 384 if (code == "formatter.UNKNOWN_ERROR") { 385 args.path = args.path || ["(",ZmMsg.unknown,")"].join(""); 386 args.message = message; 387 } 388 var mappings = ZmImportExportController.__FAULT_ARGS_MAPPING[code]; 389 var formatArgs = new Array(mappings ? mappings.length : 0); 390 for (var i = 0; i < formatArgs.length; i++) { 391 formatArgs[i] = args[mappings[i]]; 392 } 393 var errorMsg = ZMsg[code] ? AjxMessageFormat.format(ZMsg[code], formatArgs) : ""; 394 // be a little more verbose if there was a failure 395 if (type == "fail") { 396 errorMsg = message ? errorMsg + '<br><br>' + AjxStringUtil.htmlEncode(message) : errorMsg; 397 } 398 else if (type == "warn") { 399 errorMsg = errorMsg || message; 400 } 401 messages.push(errorMsg); 402 } 403 } 404 // show success or failure 405 if (type == "fail") { 406 this._importError(params.errorCallback, messages[0]); 407 } 408 else if (type == "warn") { 409 this._importWarnings(params.callback, messages); 410 } 411 else { 412 this._importSuccess(params.callback); 413 appCtxt.getAppController().sendNoOp(); //send no-op to refresh 414 } 415 416 // cleanup 417 try { 418 delete window[funcName]; // IE fails on this one (bug #57952) 419 } catch (e) { 420 if (window[funcName]) { 421 window[funcName] = undefined; 422 } 423 } 424 var iframe = params.iframe; 425 setTimeout(function() { // Right now we are actually in the iframe's onload handler, so we defer killing the iframe until we're out of it 426 iframe.parentNode.removeChild(iframe); 427 }, 0); 428 }; 429 430 /** 431 * @private 432 */ 433 ZmImportExportController.__faultArgs = 434 function(array) { 435 var args = {}; 436 for (var i = 0; array && i < array.length; i++) { 437 args[array[i].n] = array[i]._content; 438 } 439 return args; 440 }; 441 442 /** 443 * @private 444 */ 445 ZmImportExportController.prototype._confirmImportReset = 446 function(params) { 447 this._cancelImportReset(); 448 this._doImportData(params); 449 }; 450 451 /** 452 * @private 453 */ 454 ZmImportExportController.prototype._cancelImportReset = 455 function() { 456 var dialog = appCtxt.getOkCancelMsgDialog(); 457 dialog.reset(); 458 dialog.popdown(); 459 }; 460 461 /** 462 * @private 463 */ 464 ZmImportExportController.prototype._doExportData = 465 function(params) { 466 // create custom error callback function for this export request 467 var funcName = "exportErrorCallback__" + Dwt.getNextId("export"); 468 ZmImportExportController[funcName] = this._handleExportErrorResponse.bind(this, funcName, params); 469 470 var type = params.type; 471 var isTGZ = type == ZmImportExportController.TYPE_TGZ; 472 var isCSV = type == ZmImportExportController.TYPE_CSV; 473 var subType = params.subType || ZmImportExportController.SUBTYPE_DEFAULT[type]; 474 475 var folder = params.folderId && appCtxt.getById(params.folderId); 476 if (folder && folder.nId == ZmOrganizer.ID_ROOT) folder = null; 477 var path = folder ? folder.getPath(null, null, null, true, true) : ""; 478 479 // generate request URL 480 var url = [ 481 "/home/", 482 encodeURIComponent(appCtxt.get(ZmSetting.USERNAME)), 483 "/", 484 encodeURIComponent(path) 485 ].join(""); 486 487 var formParams = { "fmt" : type }; 488 if (isCSV) { 489 formParams[type+"fmt"] = subType; 490 } 491 var startDate = params.start ? AjxDateUtil.simpleParseDateStr(params.start) : null; 492 var endDate = params.end ? AjxDateUtil.simpleParseDateStr(params.end) : null; 493 if (isTGZ && params.views) { formParams["types"] = params.views; } 494 if (type == ZmImportExportController.TYPE_ICS) { 495 formParams["icalAttach"] = "inline"; 496 } 497 if(startDate) { 498 formParams["start"] = startDate.getTime(); 499 } 500 if(endDate) { 501 endDate = AjxDateUtil.roll(endDate, AjxDateUtil.DAY, 1); 502 formParams["end"] = endDate.getTime(); 503 } 504 if (isTGZ && params.searchFilter) { formParams["query"] = params.searchFilter; } 505 if (params.skipMeta) { formParams["meta"] = "0"; } 506 if (params.filename) { formParams["filename"] = params.filename; } 507 formParams.emptyname = ZmMsg.exportEmptyName; 508 formParams["charset"] = (subType === "windows-live-mail-csv" || subType === "thunderbird-csv") ? "UTF-8" : appCtxt.getCharset(); 509 formParams["callback"] = "ZmImportExportController." + funcName; 510 511 // initialize form 512 var form = ZmImportExportController.__createForm(url, formParams); 513 514 // destination form 515 var onload = null; 516 var onerror = this._exportError.bind(this, params.errorCallback); 517 params.iframe = ZmImportExportController.__createIframe(form, onload, onerror); 518 params.form = form; 519 // export 520 form.submit(); 521 }; 522 523 /** 524 * @private 525 */ 526 ZmImportExportController.prototype._importSuccess = 527 function(callback) { 528 if (callback) { 529 callback.run(true); 530 } 531 ZmImportExportController.__showMessage(ZmMsg.importSuccess, DwtMessageDialog.INFO_STYLE); 532 return true; 533 }; 534 535 /** 536 * @private 537 */ 538 ZmImportExportController.prototype._importWarnings = 539 function(callback, messages) { 540 if (callback) { 541 callback.run(false); 542 } 543 // remove duplicates 544 var msgmap = {}; 545 for (var i = 0; i < messages.length; i++) { 546 msgmap[messages[i]] = true; 547 } 548 messages = AjxUtil.map(AjxUtil.keys(msgmap), AjxStringUtil.htmlEncode); 549 if (messages.length > 5) { 550 var count = messages.length - 5; 551 messages.splice(5, count, AjxMessageFormat.format(ZmMsg.importAdditionalWarnings, [count])); 552 } 553 // show warnings 554 var msglist = []; 555 for (var i = 0; i < messages.length; i++) { 556 msglist.push("<li>", messages[i]); 557 } 558 var msg = AjxMessageFormat.format(ZmMsg.importSuccessWithWarnings, [ messages.length, msglist.join("") ]); 559 ZmImportExportController.__showMessage(msg, DwtMessageDialog.WARNING_STYLE); 560 return true; 561 }; 562 563 /** 564 * @private 565 */ 566 ZmImportExportController.prototype._importError = 567 function(errorCallback, message) { 568 if (errorCallback) { 569 errorCallback.run(false); 570 } 571 var msg = message || ZmMsg.importFailed; 572 ZmImportExportController.__showMessage(msg, DwtMessageDialog.CRITICAL_STYLE); 573 return true; 574 }; 575 576 /** 577 * @private 578 */ 579 ZmImportExportController.prototype._handleExportErrorResponse = 580 function(funcName, params, type, fault1 /* , ... , faultN */) { 581 // gather error messages 582 var messages = []; 583 if (fault1) { 584 for (var j = 3; j < arguments.length; j++) { 585 var fault = arguments[j]; 586 var code = fault.Detail.Error.Code; 587 var message = fault.Reason.Text; 588 var args = ZmImportExportController.__faultArgs(fault.Detail.Error.a); 589 if (code == "mail.EXPORT_PERIOD_NOT_SPECIFIED" || code == "mail.EXPORT_PERIOD_TOO_LONG") { 590 message = ""; 591 } else if (code == "formatter.UNKNOWN_ERROR") { 592 args.path = args.path || ["(",ZmMsg.unknown,")"].join(""); 593 args.message = message; 594 } 595 var mappings = ZmImportExportController.__FAULT_ARGS_MAPPING[code]; 596 var formatArgs = new Array(mappings ? mappings.length : 0); 597 for (var i = 0; i < formatArgs.length; i++) { 598 formatArgs[i] = args[mappings[i]]; 599 } 600 var errorMsg = ZMsg[code] ? AjxMessageFormat.format(ZMsg[code], formatArgs) : ""; 601 if (type == "fail") { 602 errorMsg = message ? errorMsg + '<br><br>' + AjxStringUtil.htmlEncode(message) : errorMsg; 603 } 604 messages.push(errorMsg); 605 } 606 } 607 if (type == "fail") { 608 this._exportError(params.errorCallback, messages[0]); 609 } 610 // cleanup 611 try { 612 delete ZmImportExportController[funcName]; // IE fails on this one (bug #57952) 613 } catch (e) { 614 if (ZmImportExportController[funcName]) { 615 ZmImportExportController[funcName] = undefined; 616 } 617 } 618 var iframe = params.iframe; 619 var form = params.form; 620 setTimeout(function() { // Right now we are actually in the iframe's onload handler, so we defer killing the iframe until we're out of it 621 iframe.parentNode.removeChild(iframe); 622 form.parentNode.removeChild(form); 623 }, 0); 624 }; 625 626 /** 627 * @private 628 */ 629 ZmImportExportController.prototype._exportError = 630 function(errorCallback, message) { 631 if (errorCallback) { 632 errorCallback.run(false); 633 } 634 var msg = message || ZmMsg.exportFailed; 635 ZmImportExportController.__showMessage(msg, DwtMessageDialog.CRITICAL_STYLE); 636 return true; 637 }; 638 639 // 640 // Private methods 641 // 642 643 /** 644 * @private 645 */ 646 ZmImportExportController.__showMessage = 647 function(msg, level) { 648 var dialog = appCtxt.getErrorDialog(); 649 dialog.setMessage(msg, null, level); 650 dialog.popup(null, true); 651 }; 652 653 /** 654 * @private 655 */ 656 ZmImportExportController.__createForm = 657 function(action, params, method) { 658 var form = document.createElement("FORM"); 659 form.action = action; 660 form.method = method || "GET"; 661 for (var name in params) { 662 var value = params[name]; 663 if (!value) continue; 664 var input = document.createElement("INPUT"); 665 input.type = "hidden"; 666 input.name = name; 667 input.value = value; 668 form.appendChild(input); 669 } 670 form.style.display = "none"; 671 document.body.appendChild(form); 672 return form; 673 }; 674 675 /** 676 * @private 677 */ 678 ZmImportExportController.__createIframe = 679 function(form, onload, onerror) { 680 var id = Dwt.getNextId() + "_iframe"; 681 var iframe; 682 if (AjxEnv.isIE) { 683 try { 684 // NOTE: This has to be done because IE doesn't recognize the name 685 // attribute if set programmatically. And without that, the 686 // form target will cause it to return in a new window which 687 // breaks the callback. 688 var html = [ "<IFRAME id='",id,"' name='",id,"'>" ].join(""); 689 iframe = document.createElement(html); 690 } catch (e) { 691 // Unless its IE9+ in non-quirks mode, then the above throws an exception 692 iframe = document.createElement("IFRAME"); 693 iframe.name = iframe.id = id; 694 } 695 } 696 else { 697 iframe = document.createElement("IFRAME"); 698 iframe.name = iframe.id = id; 699 } 700 // NOTE: Event handlers won't be called when iframe hidden. 701 // iframe.style.display = "none"; 702 iframe.style.position = "absolute"; 703 iframe.style.width = 1; 704 iframe.style.height = 1; 705 iframe.style.top = 10; 706 iframe.style.left = -10; 707 document.body.appendChild(iframe); 708 form.target = iframe.name; 709 710 iframe.onload = onload; 711 iframe.onerror = onerror; 712 713 return iframe; 714 }; 715