1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2007, 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) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 */ 27 28 /** 29 * Creates an attachment dialog. 30 * @class 31 * This class represents an attachment dialog. 32 * 33 * @param {DwtControl} shell the parent 34 * @param {String} className the class name 35 * 36 * @extends DwtDialog 37 */ 38 ZmAttachDialog = function(shell, className) { 39 40 className = className || "ZmAttachDialog"; 41 DwtDialog.call(this, {parent:shell, className:className, title:ZmMsg.attachFile}); 42 43 // Initialize 44 this._createBaseHtml(); 45 46 // Ok and Cancel Actions 47 this._defaultCancelCallback = new AjxCallback(this, this._defaultCancelListener); 48 this._cancelListener = null; 49 50 this._defaultOkCallback = new AjxCallback(this, this._defaultOkListener); 51 this._okListener = null; 52 53 this.setButtonListener(DwtDialog.CANCEL_BUTTON, new AjxListener(this, function() { 54 this._cancelButtonListener(); 55 })); 56 57 this.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, function() { 58 this._okButtonListener(); 59 })); 60 61 62 var okButton = this.getButton(DwtDialog.OK_BUTTON); 63 okButton.setText(ZmMsg.attach); 64 65 }; 66 67 ZmAttachDialog.prototype = new DwtDialog; 68 ZmAttachDialog.prototype.constructor = ZmAttachDialog; 69 70 /** 71 * Defines the "briefcase" tab key. 72 */ 73 ZmAttachDialog.TABKEY_BRIEFCASE = "BRIEFCASE"; 74 75 //Listeners 76 77 /** 78 * Adds a cancel button listener. 79 * 80 * @param {constant} tabKey the tab key (see <code>TABKEY_</code> constants) 81 * @param {AjxListener|AjxCallback} cancelCallbackOrListener the listener 82 */ 83 ZmAttachDialog.prototype.setCancelListener = 84 function(cancelCallbackOrListener) { 85 if (cancelCallbackOrListener && 86 (cancelCallbackOrListener instanceof AjxListener || 87 cancelCallbackOrListener instanceof AjxCallback)) 88 { 89 this._cancelListener = cancelCallbackOrListener; 90 } 91 }; 92 93 94 ZmAttachDialog.prototype._defaultCancelListener = 95 function() { 96 this.popdown(); 97 }; 98 99 ZmAttachDialog.prototype._cancelButtonListener = 100 function() { 101 if (this._cancelListener) { 102 this._cancelListener.run(); 103 } else { 104 this._defaultCancelCallback.run(); 105 } 106 }; 107 108 /** 109 * Adds a OK button listener. 110 * 111 * @param {constant} tabKey the tab key (see <code>TABKEY_</code> constants) 112 * @param {AjxListener|AjxCallback} cancelCallbackOrListener the listener 113 */ 114 ZmAttachDialog.prototype.setOkListener = 115 function(okCallbackOrListener) { 116 if (okCallbackOrListener && 117 (okCallbackOrListener instanceof AjxListener || 118 okCallbackOrListener instanceof AjxCallback)) 119 { 120 this._okListener = okCallbackOrListener; 121 } 122 }; 123 124 ZmAttachDialog.prototype._defaultOkListener = 125 function() { 126 this.popdown(); 127 }; 128 129 ZmAttachDialog.prototype._okButtonListener = 130 function() { 131 132 var okButton = this.getButton(DwtDialog.OK_BUTTON); 133 okButton.setEnabled(false); 134 135 if (this._okListener) { 136 this._okListener.run(this); 137 } else { 138 this._defaultOkCallback.run(); 139 } 140 141 okButton.setEnabled(true); 142 }; 143 144 // Create HTML Container 145 ZmAttachDialog.prototype._createBaseHtml = 146 function() { 147 this._baseContainerView = new DwtComposite({parent:this, className:"ZmAttachDialog-container"}); 148 this._initializeTabView(this._baseContainerView); 149 this.setView(this._baseContainerView); 150 }; 151 152 ZmAttachDialog.prototype._initializeTabView = 153 function(view) { 154 this._setAttachmentSizeSection(view); 155 this._setInlineOptionSection(view); 156 this._setMsgSection(view); 157 this._setFooterSection(view); 158 }; 159 160 /** 161 * @private 162 */ 163 ZmAttachDialog.prototype.stateChangeListener = 164 function(ev) { 165 // Reset Inline Options Here 166 this._resetInlineOption(); 167 }; 168 169 170 ZmAttachDialog.prototype._setAttachmentSizeSection = 171 function(view) { 172 var div = document.createElement("div"); 173 div.className = "ZmAttachDialog-note"; 174 var attSize = AjxUtil.formatSize(appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT) || 0, true) 175 div.innerHTML = AjxMessageFormat.format(ZmMsg.attachmentLimitMsg, attSize); 176 view.getHtmlElement().appendChild(div); 177 }; 178 179 ZmAttachDialog.prototype._setMsgSection = 180 function(view) { 181 var div = document.createElement("div"); 182 div.className = "ZmAttachDialog-footer"; 183 div.id = Dwt.getNextId(); 184 view.getHtmlElement().appendChild(div); 185 this._msgDiv = document.getElementById(div.id); 186 }; 187 188 ZmAttachDialog.prototype._setFooterSection = 189 function(view) { 190 var div = document.createElement("div"); 191 div.className = "ZmAttachDialog-footer"; 192 div.id = Dwt.getNextId(); 193 view.getHtmlElement().appendChild(div); 194 195 this._footer = document.getElementById(div.id); 196 }; 197 198 /** 199 * Sets the footer content. 200 * 201 * @param {String} html the HTML footer content 202 */ 203 ZmAttachDialog.prototype.setFooter = 204 function(html) { 205 if (typeof html == "string") { 206 this._footer.innerHTML = html; 207 } else { 208 this._footer.appendChild(html); 209 } 210 }; 211 212 //Called when AjxEnv.supportsHTML5File is false 213 214 ZmAttachDialog.prototype.submitAttachmentFile = 215 function(view) { 216 this.upload(this._uploadCallback, view.uploadForm); 217 }; 218 219 ZmAttachDialog.prototype.cancelUploadFiles = 220 function() { 221 // Fix this, as this needs feature request like AjxPost.getRequestId() 222 // We need to cancel actual request, but for now just close the window 223 this._cancelUpload = true; 224 this._defaultCancelListener(); 225 }; 226 227 ZmAttachDialog.prototype.setUploadCallback = 228 function(callback) { 229 this._uploadCallback = callback; 230 }; 231 232 ZmAttachDialog.prototype.getUploadCallback = 233 function() { 234 return this._uploadCallback; 235 }; 236 237 /** 238 * Uploads the attachments. 239 * 240 * @param {AjxCallback} callback the callback 241 * @param {Object} uploadForm the upload form 242 */ 243 ZmAttachDialog.prototype.upload = 244 function(callback, uploadForm) { 245 if (!callback) { 246 callback = false; 247 } 248 this.setButtonEnabled(DwtDialog.OK_BUTTON, false); 249 this.setButtonEnabled(DwtDialog.CANCEL_BUTTON, true); 250 this.setFooter(ZmMsg.attachingFiles); 251 this._cancelUpload = false; 252 this._processUpload(callback, uploadForm); 253 }; 254 255 ZmAttachDialog.prototype._processUpload = 256 function(callback, uploadForm) { 257 var ajxCallback = new AjxCallback(this, this._uploadDoneCallback, [callback]); 258 var um = appCtxt.getUploadManager(); 259 window._uploadManager = um; 260 261 try { 262 um.execute(ajxCallback, uploadForm); 263 } catch (ex) { 264 ajxCallback.run(); 265 } 266 }; 267 268 ZmAttachDialog.prototype._uploadDoneCallback = 269 function(callback, status, attId) { 270 if (this._cancelUpload) { return; } 271 272 this.setButtonEnabled(DwtDialog.CANCEL_BUTTON, true); 273 274 if (status == AjxPost.SC_OK) { 275 this.setFooter(ZmMsg.attachingFilesDone); 276 if (callback) { 277 callback.run(status, attId); 278 } 279 } else if (status == AjxPost.SC_UNAUTHORIZED) { 280 // auth failed during att upload - let user relogin, continue with compose action 281 var ex = new AjxException("401 response during attachment upload", ZmCsfeException.SVC_AUTH_EXPIRED); 282 appCtxt.getAppController()._handleException(ex, {continueCallback:callback}); 283 } else { 284 // bug fix #2131 - handle errors during attachment upload. 285 appCtxt.getAppController().popupUploadErrorDialog(ZmItem.MSG, status); 286 this.setFooter(ZmMsg.attachingFilesError); 287 } 288 289 this.setButtonEnabled(DwtDialog.OK_BUTTON, true); 290 }; 291 292 ZmAttachDialog.prototype.removePrevAttDialogContent = 293 function(contentDiv) { 294 var elementNode = contentDiv && contentDiv.firstChild; 295 if (elementNode && elementNode.className == "DwtComposite" ){ 296 contentDiv.removeChild(elementNode); 297 } 298 }; 299 300 301 ZmAttachDialog.prototype.getBriefcaseView = 302 function(){ 303 304 this.removePrevAttDialogContent(this._getContentDiv().firstChild); 305 this.setTitle(ZmMsg.attachFile); 306 307 if (!this._briefcaseView) { 308 AjxDispatcher.require(["BriefcaseCore", "Briefcase"]); 309 this._briefcaseView = new ZmBriefcaseTabView(this); 310 } 311 312 this._briefcaseView.reparentHtmlElement(this._getContentDiv().childNodes[0], 0); 313 var okCallback = new AjxCallback(this._briefcaseView, this._briefcaseView.uploadFiles); 314 this.setOkListener(okCallback); 315 this.setCancelListener((new AjxCallback(this,this.cancelUploadFiles))); 316 317 318 return this._briefcaseView; 319 }; 320 321 // Inline Option for attachment Dialog. 322 ZmAttachDialog.prototype._setInlineOptionSection = 323 function(view){ 324 var div = document.createElement("div"); 325 div.className = "ZmAttachDialog-inline"; 326 div.id = Dwt.getNextId(); 327 view.getHtmlElement().appendChild(div); 328 329 this._inlineOption = document.getElementById(div.id); 330 }; 331 332 ZmAttachDialog.prototype.enableInlineOption = 333 function(enable) { 334 if (enable) { 335 var inlineCheckboxId = this._htmlElId + "_inlineCheckbox"; 336 this._inlineOption.setAttribute("option", "inline"); 337 this._inlineOption.innerHTML = [ 338 "<input type='checkbox' name='inlineimages' id='", 339 inlineCheckboxId, 340 "'> <label for='", 341 inlineCheckboxId, 342 "'>", 343 ZmMsg.inlineAttachmentOption, 344 "</label>" 345 ].join(""); 346 this._tabGroup.addMember(this._inlineOption.getElementsByTagName('input')[0],0); 347 } else { 348 this._inlineOption.innerHTML = ""; 349 } 350 }; 351 352 ZmAttachDialog.prototype._resetInlineOption = 353 function() { 354 var inlineOption = document.getElementById(this._htmlElId+"_inlineCheckbox"); 355 if (inlineOption) { 356 inlineOption.checked = false; 357 } 358 }; 359 360 ZmAttachDialog.prototype.isInline = 361 function() { 362 var inlineOption = document.getElementById(this._htmlElId+"_inlineCheckbox"); 363 return (inlineOption && inlineOption.checked); 364 }; 365 366 ZmAttachDialog.prototype.setInline = 367 function(checked) { 368 var inlineOption = document.getElementById(this._htmlElId+"_inlineCheckbox"); 369 370 if (inlineOption) 371 inlineOption.checked = checked; 372 }; 373 374 375 /** 376 * Attachment Upload View 377 * 378 * @param parent 379 * @param className 380 * @param posStyle 381 * 382 * @class 383 * @private 384 */ 385 ZmAttachDialog.prototype.getMyComputerView = 386 function(){ 387 var newElm = false; 388 this.removePrevAttDialogContent(this._getContentDiv().firstChild); 389 this.setTitle(ZmMsg.attachFile); 390 391 if (!this._myComputerView) { 392 this._myComputerView = new ZmMyComputerTabViewPage(this); 393 newElm = true; 394 } 395 396 this._myComputerView.reparentHtmlElement(this._getContentDiv().childNodes[0], 0); 397 398 if (!newElm) { 399 this._myComputerView.resetAttachments() 400 } 401 402 var okCallback = new AjxCallback(this, this.submitAttachmentFile,[this._myComputerView]); 403 this.setOkListener(okCallback); 404 this.setCancelListener((new AjxCallback(this,this.cancelUploadFiles))); 405 406 return this._myComputerView; 407 }; 408 409 410 ZmMyComputerTabViewPage = function(parent, className, posStyle) { 411 if (arguments.length == 0) { return; } 412 413 DwtComposite.call(this, parent, className, Dwt.STATIC_STYLE); 414 this._createHtml(); 415 this.showMe(); 416 this.setScrollStyle(Dwt.SCROLL); 417 }; 418 419 ZmMyComputerTabViewPage.prototype = new DwtComposite; 420 ZmMyComputerTabViewPage.prototype.constructor = ZmMyComputerTabViewPage; 421 422 ZmMyComputerTabViewPage.SHOW_NO_ATTACHMENTS = 5; 423 ZmMyComputerTabViewPage.MAX_NO_ATTACHMENTS = 10; 424 ZmMyComputerTabViewPage.UPLOAD_FIELD_NAME = "_attFile_"; 425 426 427 ZmMyComputerTabViewPage.prototype.showMe = 428 function() { 429 this.resetAttachments(); 430 this.setSize(Dwt.DEFAULT, "240"); 431 this._focusAttEl(); 432 }; 433 434 ZmMyComputerTabViewPage.prototype.hideMe = 435 function() { 436 DwtTabViewPage.prototype.hideMe.call(this); 437 }; 438 439 // Create UI for MyComputer 440 ZmMyComputerTabViewPage.prototype._createHtml = 441 function() { 442 443 var subs = { 444 id: this._htmlElId, 445 uri: (appCtxt.get(ZmSetting.CSFE_ATTACHMENT_UPLOAD_URI) + "?fmt=extended") 446 }; 447 this.setContent(AjxTemplate.expand("share.Dialogs#ZmAttachDialog-MyComputerTab", subs)); 448 449 this.attachmentTable = document.getElementById(this._htmlElId+"_attachmentTable"); 450 this.uploadForm = document.getElementById(this._htmlElId+"_uploadForm"); 451 this.attachmentButtonTable = document.getElementById(this._htmlElId+"_attachmentButtonTable"); 452 453 this._addAttachmentFieldButton(); 454 this._attachCount = 0; 455 }; 456 457 // Attachments 458 ZmMyComputerTabViewPage.prototype._addAttachmentField = 459 function() { 460 if (this._attachCount >= ZmMyComputerTabViewPage.MAX_NO_ATTACHMENTS) { return; } 461 462 this._attachCount++; 463 464 var row = this.attachmentTable.insertRow(-1); 465 var cell = row.insertCell(-1); 466 var fieldId = Dwt.getNextId(); 467 468 var subs = { 469 id: fieldId, 470 uploadName: ZmMyComputerTabViewPage.UPLOAD_FIELD_NAME 471 }; 472 cell.innerHTML = AjxTemplate.expand("share.Dialogs#ZmAttachDialog-MyComputerTab-AddAttachment", subs); 473 474 var removeEl = document.getElementById(fieldId+"_remove"); 475 removeEl.onclick = AjxCallback.simpleClosure(this._removeAttachmentField, this, row); 476 477 var inputId = fieldId+"_input"; 478 if (this._focusElId == -1) { 479 this._focusElId = inputId; 480 } 481 var inputEl = document.getElementById(inputId); 482 var sizeEl = document.getElementById(fieldId+"_size"); 483 484 //HTML5 485 if(AjxEnv.supportsHTML5File){ 486 Dwt.setHandler(inputEl, "onchange", AjxCallback.simpleClosure(this._handleFileSize, this, inputEl, sizeEl)); 487 } 488 489 // trap key presses in IE for input field so we can ignore ENTER key (bug 961) 490 if (AjxEnv.isIE) { 491 inputEl.onkeydown = AjxCallback.simpleClosure(this._handleKeys, this); 492 } 493 }; 494 495 ZmMyComputerTabViewPage.prototype._handleFileSize = 496 function(inputEl, sizeEl){ 497 498 var files = inputEl.files; 499 if(!files) return; 500 501 var sizeStr = [], className, totalSize =0; 502 for(var i=0; i<files.length;i++){ 503 var file = files[i]; 504 var size = file.size || file.fileSize /*Safari*/ || 0; 505 if ((-1 /* means unlimited */ != appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT)) && 506 (size > appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT))) { 507 className = "RedC"; 508 } 509 totalSize += size; 510 } 511 512 if(sizeEl) { 513 sizeEl.innerHTML = " ("+AjxUtil.formatSize(totalSize, true)+")"; 514 if(className) 515 Dwt.addClass(sizeEl, "RedC"); 516 else 517 Dwt.delClass(sizeEl, "RedC"); 518 } 519 }; 520 521 522 523 ZmMyComputerTabViewPage.prototype._removeAttachmentField = 524 function(row) { 525 this.attachmentTable.deleteRow(row.rowIndex); 526 this._attachCount--; 527 528 if (this._attachCount == 0) { 529 this._addAttachmentField(); 530 } 531 }; 532 533 ZmMyComputerTabViewPage.prototype._addAttachmentFieldButton = 534 function() { 535 var row = this.attachmentButtonTable.insertRow(-1); 536 var cell = row.insertCell(-1); 537 538 var button = new DwtButton({parent:this, parentElement:cell}); 539 button.setText(ZmMsg.addMoreAttachments); 540 button.addSelectionListener(new AjxListener(this, this._addAttachmentField)); 541 }; 542 543 ZmMyComputerTabViewPage.prototype.gotAttachments = 544 function() { 545 var atts = document.getElementsByName(ZmMyComputerTabViewPage.UPLOAD_FIELD_NAME); 546 547 for (var i = 0; i < atts.length; i++) 548 if (atts[i].value.length) { 549 return true; 550 } 551 return false; 552 }; 553 554 ZmMyComputerTabViewPage.prototype.resetAttachments = 555 function() { 556 // CleanUp 557 this._cleanTable(this.attachmentTable); 558 this._attachCount = 0; 559 if (ZmMyComputerTabViewPage.SHOW_NO_ATTACHMENTS > ZmMyComputerTabViewPage.MAX_NO_ATTACHMENTS) { 560 ZmMyComputerTabViewPage.SHOW_NO_ATTACHMENTS = ZmMyComputerTabViewPage.MAX_NO_ATTACHMENTS; 561 } 562 563 // Re-initialize UI 564 this._focusElId = -1; 565 var row = this.attachmentTable.insertRow(-1); 566 var cell = row.insertCell(-1); 567 cell.appendChild(document.createElement("br")); 568 cell.appendChild(document.createElement("br")); 569 570 for (var i = 0; i < ZmMyComputerTabViewPage.SHOW_NO_ATTACHMENTS; i++) { 571 this._addAttachmentField(); 572 } 573 delete i; 574 }; 575 576 ZmMyComputerTabViewPage.prototype._focusAttEl = 577 function() { 578 var el = document.getElementById(this._focusElId); 579 if (el) el.focus(); 580 }; 581 582 // Utilities 583 ZmMyComputerTabViewPage.prototype._cleanTable = 584 function(table) { 585 if (!table || !table.rows) { return; } 586 while (table.rows.length > 0) { 587 table.deleteRow(0); 588 } 589 }; 590 591 ZmMyComputerTabViewPage.prototype._handleKeys = 592 function(ev) { 593 var key = DwtKeyEvent.getCharCode(ev); 594 return !DwtKeyEvent.IS_RETURN[key]; 595 }; 596 597 ZmMyComputerTabViewPage.prototype._validateFileSize = 598 function(){ 599 600 var atts = document.getElementsByName(ZmMyComputerTabViewPage.UPLOAD_FIELD_NAME); 601 var file, size; 602 for (var i = 0; i < atts.length; i++){ 603 file = atts[i].files; 604 if(!file || file.length == 0) continue; 605 for(var j=0; j<file.length;j++){ 606 var f = file[j]; 607 size = f.size || f.fileSize /*Safari*/; 608 if ((-1 /* means unlimited */ != appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT)) && 609 (size > appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT))) { 610 return false; 611 } 612 } 613 } 614 return true; 615 }; 616 617 ZmMyComputerTabViewPage.prototype.validate = 618 function(){ 619 var status, errorMsg; 620 if(AjxEnv.supportsHTML5File){ 621 status = this._validateFileSize(); 622 errorMsg = AjxMessageFormat.format(ZmMsg.attachmentSizeError, AjxUtil.formatSize(appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT))); 623 }else{ 624 status = true; 625 } 626 627 return {status: status, error:errorMsg}; 628 }; 629