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 * This file contains the briefcase application class. 27 */ 28 29 /** 30 * Creates and initializes the briefcase application. 31 * @class 32 * The briefcase application manages the creation and display of briefcase items. 33 * 34 * @param {DwtControl} container the container 35 * @param {ZmController} parentController the parent controller 36 * 37 * @author Conrad Damon 38 * 39 * @extends ZmApp 40 */ 41 ZmBriefcaseApp = function(container, parentController) { 42 ZmApp.call(this, ZmApp.BRIEFCASE, container, parentController); 43 }; 44 45 ZmBriefcaseApp.prototype = new ZmApp; 46 ZmBriefcaseApp.prototype.constructor = ZmBriefcaseApp; 47 48 ZmBriefcaseApp.prototype.isZmBriefcaseApp = true; 49 ZmBriefcaseApp.prototype.toString = function() { return "ZmBriefcaseApp"; }; 50 51 52 // Constants 53 54 // Organizer and item-related constants 55 ZmEvent.S_BRIEFCASE_ITEM = ZmId.ITEM_BRIEFCASE; 56 ZmItem.BRIEFCASE_ITEM = ZmEvent.S_BRIEFCASE_ITEM; 57 ZmItem.BRIEFCASE = ZmItem.BRIEFCASE_ITEM; // back-compatibility 58 59 ZmEvent.S_BRIEFCASE_REVISION_ITEM = ZmId.ITEM_BRIEFCASE_REV; 60 ZmItem.BRIEFCASE_REVISION_ITEM = ZmEvent.S_BRIEFCASE_REVISION_ITEM; 61 ZmItem.BRIEFCASE_REVISION = ZmItem.BRIEFCASE_REVISION_ITEM; // back-compatibility 62 63 /** 64 * Defines the "briefcase" organizer. 65 */ 66 ZmOrganizer.BRIEFCASE = ZmId.ORG_BRIEFCASE; 67 68 // App-related constants 69 /** 70 * Defines the "briefcase" application. 71 */ 72 ZmApp.BRIEFCASE = ZmId.APP_BRIEFCASE; 73 ZmApp.CLASS[ZmApp.BRIEFCASE] = "ZmBriefcaseApp"; 74 ZmApp.SETTING[ZmApp.BRIEFCASE] = ZmSetting.BRIEFCASE_ENABLED; 75 ZmApp.LOAD_SORT[ZmApp.BRIEFCASE] = 65; 76 ZmApp.QS_ARG[ZmApp.BRIEFCASE] = "briefcase"; 77 ZmApp.BUTTON_ID[ZmApp.BRIEFCASE] = ZmId.BRIEFCASE_APP; 78 79 80 ZmBriefcaseApp.prototype._defineAPI = 81 function() { 82 AjxDispatcher.setPackageLoadFunction("BriefcaseCore", new AjxCallback(this, this._postLoadCore)); 83 AjxDispatcher.setPackageLoadFunction("Briefcase", new AjxCallback(this, this._postLoad, ZmOrganizer.BRIEFCASE)); 84 AjxDispatcher.registerMethod("GetBriefcaseController", ["BriefcaseCore", "Briefcase"], new AjxCallback(this, this.getBriefcaseController)); 85 }; 86 87 ZmBriefcaseApp.prototype._registerOperations = 88 function() { 89 ZmOperation.registerOp(ZmId.OP_NEW_BRIEFCASE, {textKey:"newBriefcase", image:"NewFolder", tooltipKey:"newBriefcaseTooltip", shortcut:ZmKeyMap.NEW_BRIEFCASE}); 90 ZmOperation.registerOp(ZmId.OP_NEW_FILE, {textKey:"uploadNewFile", tooltipKey:"uploadNewFile", textPrecedence:70, showImageInToolbar:true, showTextInToolbar:true}); 91 ZmOperation.registerOp(ZmId.OP_NEW_DOC, {textKey:"newDocument", tooltipKey:"newDocument", image:"NewDoc", shortcut:ZmKeyMap.NEW_DOC, textPrecedence:12}); 92 ZmOperation.registerOp(ZmId.OP_SHARE_BRIEFCASE, {textKey:"shareFolder", image:"SharedMailFolder"}, ZmSetting.SHARING_ENABLED); 93 ZmOperation.registerOp(ZmId.OP_OPEN_FILE, {textKey:"openFile", tooltipKey:"openFileTooltip", image:"NewDoc"}); 94 ZmOperation.registerOp(ZmId.OP_SAVE_FILE, {textKey:"saveFile", tooltipKey:"saveFileTooltip", image:"DownArrow"}); 95 ZmOperation.registerOp(ZmId.OP_VIEW_FILE_AS_HTML, {textKey:"viewFileAsHtml", tooltipKey:"viewAsHtml", image:"HtmlDoc"}); 96 ZmOperation.registerOp(ZmId.OP_SEND_FILE, {textKey:"sendLink", tooltipKey:"sendLink", image:"Send"}); 97 ZmOperation.registerOp(ZmId.OP_SEND_FILE_AS_ATT, {textKey:"sendAsAttachment", tooltipKey:"sendAsAttachment", image:"Attachment"}); 98 ZmOperation.registerOp(ZmId.OP_SEND_FILE_MENU, {textKey:"send", image:"Send", textPrecedence:75}); 99 ZmOperation.registerOp(ZmId.OP_EDIT_FILE, {textKey: "edit", image:"Edit"}); 100 ZmOperation.registerOp(ZmId.OP_RENAME_FILE, {textKey: "rename", image:"FileRename"}); 101 ZmOperation.registerOp(ZmId.OP_CHECKIN, {textKey: "checkInFile", image:"Checkin"}); 102 ZmOperation.registerOp(ZmId.OP_CHECKOUT, {textKey: "checkOutFile", image:"Checkout"}); 103 ZmOperation.registerOp(ZmId.OP_DISCARD_CHECKOUT, {textKey: "checkOutFileDiscard", image:"DiscardCheckout"}); 104 ZmOperation.registerOp(ZmId.OP_VERSION_HISTORY, {textKey: "versionHistory", image:"VersionHistory"}); 105 ZmOperation.registerOp(ZmId.OP_RESTORE_VERSION, {textKey: "restoreCurrentVersion", image:"RestoreVersion"}); 106 ZmOperation.registerOp(ZmId.OP_DELETE_VERSION, {textKey: "deleteVersion", image:"Delete"}); 107 }; 108 109 ZmBriefcaseApp.prototype._registerSettings = 110 function(settings) { 111 settings = settings || appCtxt.getSettings(); 112 settings.registerSetting("DOCS_ENABLED", {name:"zimbraFeatureBriefcaseDocsEnabled", type:ZmSetting.T_COS, dataType: ZmSetting.D_BOOLEAN, defaultValue:true}); 113 settings.registerSetting("PREVIEW_ENABLED", { type:ZmSetting.T_COS, dataType: ZmSetting.D_BOOLEAN, defaultValue:false}); 114 settings.registerSetting("READING_PANE_LOCATION_BRIEFCASE", {name:"zimbraPrefBriefcaseReadingPaneLocation", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:ZmSetting.RP_BOTTOM, isImplicit:true}); 115 }; 116 117 ZmBriefcaseApp.prototype._registerItems = 118 function() { 119 ZmItem.registerItem(ZmItem.BRIEFCASE_ITEM, 120 {app: ZmApp.BRIEFCASE, 121 nameKey: "file", 122 icon: "GenericDoc", 123 soapCmd: "ItemAction", 124 itemClass: "ZmBriefcaseItem", 125 node: "doc", 126 organizer: ZmOrganizer.BRIEFCASE, 127 dropTargets: [ZmOrganizer.TAG, ZmOrganizer.BRIEFCASE], 128 searchType: "document", 129 resultsList: 130 AjxCallback.simpleClosure(function(search) { 131 AjxDispatcher.require("BriefcaseCore"); 132 return new ZmList(ZmItem.BRIEFCASE_ITEM, search); 133 }, this) 134 }); 135 }; 136 137 ZmBriefcaseApp.prototype._registerOrganizers = 138 function() { 139 ZmOrganizer.registerOrg(ZmOrganizer.BRIEFCASE, 140 {app : ZmApp.BRIEFCASE, 141 nameKey : "folder", 142 defaultFolder : ZmOrganizer.ID_BRIEFCASE, 143 soapCmd : "FolderAction", 144 firstUserId : 256, 145 orgClass : "ZmBriefcase", 146 orgPackage : "BriefcaseCore", 147 treeController : "ZmBriefcaseTreeController", 148 labelKey : "briefcaseFolders", 149 itemsKey : "files", 150 treeType : ZmOrganizer.FOLDER, 151 views : ["document"], 152 folderKey : "briefcase", 153 mountKey : "mountFolder", 154 createFunc : "ZmOrganizer.create", 155 compareFunc : "ZmFolder.sortCompareNonMail", 156 deferrable : true, 157 newOp : ZmOperation.NEW_BRIEFCASE, 158 displayOrder : 100, 159 hasColor : true, 160 defaultColor : ZmOrganizer.C_NONE, 161 childWindow : true 162 }); 163 }; 164 165 ZmBriefcaseApp.prototype._setupSearchToolbar = 166 function() { 167 //TODO:search for page alone 168 ZmSearchToolBar.addMenuItem(ZmItem.BRIEFCASE_ITEM, 169 {msgKey: "files", 170 tooltipKey: "searchForFiles", 171 icon: "Doc", 172 shareIcon: null, // the following doesn't work now, so keep the regular icon. doesn't really matter in my opinion --> "SharedBriefcase", 173 setting: ZmSetting.BRIEFCASE_ENABLED, 174 id: ZmId.getMenuItemId(ZmId.SEARCH, ZmId.ITEM_BRIEFCASE), 175 disableOffline:true 176 }); 177 }; 178 179 ZmBriefcaseApp.prototype._registerApp = 180 function() { 181 var newItemOps = {}; 182 newItemOps[ZmOperation.NEW_DOC] = "document"; 183 184 var newOrgOps = {}; 185 newOrgOps[ZmOperation.NEW_BRIEFCASE] = "briefcase"; 186 187 var actionCodes = {}; 188 actionCodes[ZmKeyMap.NEW_FILE] = ZmOperation.NEW_FILE; 189 actionCodes[ZmKeyMap.NEW_BRIEFCASE] = ZmOperation.NEW_BRIEFCASE; 190 actionCodes[ZmKeyMap.NEW_DOC] = ZmOperation.NEW_DOC; 191 192 ZmApp.registerApp(ZmApp.BRIEFCASE, 193 {mainPkg: "Briefcase", 194 nameKey: "briefcase", 195 icon: "Briefcase", 196 textPrecedence: 30, 197 chooserTooltipKey: "gotoBriefcase", 198 defaultSearch: ZmItem.BRIEFCASE_ITEM, 199 organizer: ZmOrganizer.BRIEFCASE, 200 overviewTrees: [ZmOrganizer.BRIEFCASE, ZmOrganizer.TAG], 201 searchTypes: [ZmItem.BRIEFCASE_ITEM], 202 newItemOps: newItemOps, 203 newOrgOps: newOrgOps, 204 actionCodes: actionCodes, 205 gotoActionCode: ZmKeyMap.GOTO_BRIEFCASE, 206 newActionCode: ZmKeyMap.NEW_DOC, 207 chooserSort: 70, 208 defaultSort: 60, 209 searchResultsTab: true 210 }); 211 }; 212 213 // App API 214 215 /** 216 * Checks for the creation of a briefcase or a mount point to one, or of an item 217 * 218 * @param {Hash} creates a hash of create notifications 219 * 220 * @private 221 */ 222 ZmBriefcaseApp.prototype.createNotify = 223 function(creates, force) { 224 225 if (!creates["folder"] && !creates["doc"] && !creates["link"]) { return; } 226 if (!force && !this._noDefer && this._deferNotifications("create", creates)) { return; } 227 228 for (var name in creates) { 229 var clist = creates[name]; 230 for (var i = 0; (clist != null) && i < clist.length; i++) { 231 var create = clist[i]; 232 if (appCtxt.cacheGet(create.id)) { continue; } 233 234 if (name == "folder") { 235 this._handleCreateFolder(create, ZmOrganizer.BRIEFCASE); 236 } else if (name == "link") { 237 this._handleCreateLink(create, ZmOrganizer.BRIEFCASE); 238 } else if (name == "doc") { 239 var bc = AjxDispatcher.run("GetBriefcaseController"); 240 bc.handleCreateNotify(create); 241 } 242 } 243 } 244 }; 245 246 ZmBriefcaseApp.prototype.modifyNotify = 247 function(modifies, force) { 248 if (!modifies["doc"]) { return; } 249 var bc = AjxDispatcher.run("GetBriefcaseController"); 250 bc.handleModifyNotify(modifies); 251 }; 252 253 ZmBriefcaseApp.prototype.handleOp = 254 function(op) { 255 256 switch (op) { 257 case ZmOperation.NEW_FILE: { 258 var loadCallback = new AjxCallback(this, this._handleNewItem); 259 AjxDispatcher.require(["BriefcaseCore", "Briefcase"], false, loadCallback, null, true); 260 break; 261 } 262 case ZmOperation.NEW_BRIEFCASE: { 263 var loadCallback = new AjxCallback(this, this._handleLoadNewBriefcase); 264 AjxDispatcher.require(["BriefcaseCore", "Briefcase"], false, loadCallback, null, true); 265 break; 266 } 267 268 case ZmOperation.NEW_DOC: { 269 var newDocCallback = new AjxCallback(this, this.newDoc, [ZmMimeTable.APP_ZIMBRA_DOC]); 270 AjxDispatcher.require(["BriefcaseCore", "Briefcase"], true, newDocCallback, null); 271 break; 272 } 273 } 274 }; 275 276 /** 277 * Creates a new document. 278 * 279 * @param {String} contentType the content type 280 * @param {String} new the document name 281 * @param {String} winName the name of the popup doc window 282 */ 283 ZmBriefcaseApp.prototype.newDoc = 284 function(contentType, name, winName) { 285 var overviewController = appCtxt.getOverviewController(); 286 var treeController = overviewController.getTreeController(ZmOrganizer.BRIEFCASE); 287 var folderId = ZmOrganizer.ID_BRIEFCASE; 288 if (treeController) { 289 var treeView = treeController.getTreeView(this.getOverviewId()); 290 var briefcase = treeView ? treeView.getSelected() : null; 291 folderId = briefcase ? briefcase.id : ZmOrganizer.ID_BRIEFCASE; 292 } 293 294 if (AjxDispatcher.run("GetBriefcaseController").chkFolderPermission(folderId)) { 295 if (contentType == ZmMimeTable.APP_ZIMBRA_DOC) { 296 var win = appCtxt.getNewWindow(false, null, null, winName); 297 if (win) { 298 win.command = "documentEdit"; 299 win.params = { name: name, folderId: folderId }; 300 } 301 } 302 } 303 }; 304 305 /** 306 * Gets the popup doc window features. 307 * 308 * @return {String} the window features 309 */ 310 ZmBriefcaseApp.getDocWindowFeatures = 311 function() { 312 return [ 313 "width=",(screen.width || 640),",", 314 "height=",(screen.height || 480),",", 315 "scrollbars=yes,", 316 "resizable=yes" 317 ].join(""); 318 }; 319 320 ZmBriefcaseApp.prototype._handleNewItem = 321 function() { 322 appCtxt.getAppViewMgr().popView(true, ZmId.VIEW_LOADING); // pop "Loading..." page 323 AjxDispatcher.run("GetBriefcaseController").__popupUploadDialog(ZmMsg.uploadFileToBriefcase); 324 }; 325 326 ZmBriefcaseApp.prototype._handleLoadNewBriefcase = 327 function() { 328 appCtxt.getAppViewMgr().popView(true, ZmId.VIEW_LOADING); // pop "Loading..." page 329 330 if (!this._newBriefcaseCb) { 331 this._newBriefcaseCb = this._newBriefcaseCallback.bind(this); 332 } 333 ZmController.showDialog(appCtxt.getNewBriefcaseDialog(), this._newBriefcaseCb); 334 }; 335 336 337 // Public methods 338 339 ZmBriefcaseApp.prototype.launch = 340 function(params, callback) { 341 this._setLaunchTime(this.toString(), new Date()); 342 var loadCallback = this._handleLoadLaunch.bind(this, callback); 343 AjxDispatcher.require(["BriefcaseCore","Briefcase"], true, loadCallback, null, true); 344 345 // In case of external sharing we replace drop down button options with New Document button 346 if (appCtxt.isExternalAccount()) { 347 var newButton = appCtxt.getAppController().getNewButton(); 348 newButton.removePullDownMenuOptions(); 349 } 350 }; 351 352 ZmBriefcaseApp.prototype._handleLoadLaunch = 353 function(callback) { 354 this.search(); 355 if (callback) { callback.run(); } 356 }; 357 358 ZmBriefcaseApp.prototype.getNewButtonProps = 359 function() { 360 return { 361 text: ZmMsg.newDocument, 362 tooltip: ZmMsg.newDocument, 363 icon: "NewDoc", 364 iconDis: "NewDocDis", 365 defaultId: ZmOperation.NEW_DOC, 366 disabled: !this.containsWritableFolder() 367 }; 368 }; 369 370 /** 371 * Performs a search. 372 * 373 * @param {Hash} params a hash of parameters 374 * @param {String} params.folderId the ID of briefcase folder to search in 375 * @param {String} [params.query] the query to send (overrides folderId) 376 * @param {AjxCallback} [params.callback] the callback 377 * @param {String} [params.accountName] the account name 378 * @param {Boolean} [params.noRender] if <code>true</code>, do not display results 379 */ 380 ZmBriefcaseApp.prototype.search = 381 function(params) { 382 383 params = params || {}; 384 var folderId = params.folderId || (appCtxt.isExternalAccount() ? this.getDefaultFolderId() : ZmOrganizer.ID_BRIEFCASE); 385 var folder = appCtxt.getById(folderId); 386 387 var searchParams = { 388 query: params.query || folder.createQuery(), 389 types: [ZmItem.BRIEFCASE_ITEM], 390 limit: this.getLimit(), 391 searchFor: ZmId.ITEM_BRIEFCASE, 392 callback: params.callback, 393 accountName: params.accountName, 394 noRender: params.noRender 395 }; 396 var sc = appCtxt.getSearchController(); 397 sc.searchAllAccounts = false; 398 sc.search(searchParams); 399 }; 400 401 /** 402 * Shows the search results. 403 * 404 * @param {Object} results the results 405 * @param {AjxCallback} callback the callback 406 */ 407 ZmBriefcaseApp.prototype.showSearchResults = 408 function(results, callback, searchResultsController) { 409 var loadCallback = this._handleLoadShowSearchResults.bind(this, results, callback, searchResultsController); 410 AjxDispatcher.require(["BriefcaseCore", "Briefcase"], false, loadCallback, null, true); 411 }; 412 413 ZmBriefcaseApp.prototype._handleLoadShowSearchResults = 414 function(results, callback, searchResultsController) { 415 var sessionId = searchResultsController ? searchResultsController.getCurrentViewId() : ZmApp.MAIN_SESSION; 416 var controller = AjxDispatcher.run("GetBriefcaseController", sessionId, searchResultsController); 417 controller.show(results); 418 this._setLoadedTime(this.toString(), new Date()); 419 if (callback) { 420 callback.run(controller); 421 } 422 }; 423 424 ZmBriefcaseApp.prototype.setActive = 425 function(active) { 426 }; 427 428 // return enough for us to get a scroll bar since we are pageless 429 /** 430 * Gets the limit for the search triggered by the application launch or an overview click. 431 * 432 * @param {Boolean} offset if <code>true</code> app has offset 433 * @return {int} the limit 434 */ 435 ZmBriefcaseApp.prototype.getLimit = 436 function(offset) { 437 var limit = appCtxt.get(ZmSetting.PAGE_SIZE); 438 return offset ? limit : 2 * limit; 439 }; 440 441 ZmBriefcaseApp.prototype._newBriefcaseCallback = 442 function(parent, name, color) { 443 appCtxt.getNewBriefcaseDialog().popdown(); 444 var oc = appCtxt.getOverviewController(); 445 oc.getTreeController(ZmOrganizer.BRIEFCASE)._doCreate(parent, name, color); 446 }; 447 448 ZmBriefcaseApp.prototype.getBriefcaseController = 449 function(sessionId, searchResultsController) { 450 return this.getSessionController({controllerClass: "ZmBriefcaseController", 451 sessionId: sessionId || ZmApp.MAIN_SESSION, 452 searchResultsController: searchResultsController}); 453 }; 454 455 ZmBriefcaseApp.prototype.createFromAttachment = 456 function(msgId, partId,name) { 457 var loadCallback = new AjxCallback(this, this._handleCreateFromAttachment, [msgId, partId, name]); 458 AjxDispatcher.require(["BriefcaseCore","Briefcase"], false, loadCallback); 459 }; 460 461 ZmBriefcaseApp.prototype._handleCreateFromAttachment = 462 function(msgId, partId, name) { 463 if (this._deferredFolders.length != 0) { 464 this._createDeferredFolders(ZmApp.BRIEFCASE); 465 } 466 AjxDispatcher.run("GetBriefcaseController").createFromAttachment(msgId, partId, name); 467 }; 468 469 470 //Make sure we load BriefcaseCore before calling _creatDeferredFolders() from child window. 471 ZmBriefcaseApp.prototype._createDeferredFolders = 472 function(type) { 473 AjxPackage.require("BriefcaseCore"); 474 ZmApp.prototype._createDeferredFolders.call(this, type); 475 }; 476 477 478 479 // --- Briefcase External DnD upload initiation 480 481 ZmBriefcaseApp.prototype.initExternalDndUpload = function(files, node, isInline, selectionCallback, folderId) { 482 var name = ""; 483 484 if (!AjxEnv.supportsHTML5File) { 485 // IE, FF 3.5 and lower - use the File browser 486 if (selectionCallback) { 487 selectionCallback.run(); 488 } 489 return; 490 } 491 492 if (!files) { 493 files = node.files; 494 } 495 496 var size = 0; 497 if (files) { 498 var file; 499 var docFiles = []; 500 var errors = {}; 501 var aCtxt = ZmAppCtxt.handleWindowOpener(); 502 var maxSize = aCtxt.get(ZmSetting.DOCUMENT_SIZE_LIMIT); 503 var briefcaseController = AjxDispatcher.run("GetBriefcaseController"); 504 505 if (!folderId) { 506 if (briefcaseController) { 507 folderId = briefcaseController.getFolderId(); 508 } 509 if(!folderId || folderId == ZmOrganizer.ID_TRASH) { 510 folderId = ZmOrganizer.ID_BRIEFCASE; 511 } 512 } 513 514 if(this.chkFolderPermission(folderId)){ 515 var cFolder = appCtxt.getById(folderId); 516 var uploadManager = appCtxt.getZmUploadManager(); 517 518 var errors = []; 519 for (var i = 0; i < files.length; i++){ 520 var newError = uploadManager.getErrors(files[i], maxSize); 521 if (newError) { 522 errors.push(newError); 523 } 524 } 525 if (errors.length > 0) { 526 var errorMsg = uploadManager.createUploadErrorMsg(errors, maxSize, "<br>"); 527 var msgDlg = appCtxt.getMsgDialog(); 528 msgDlg.setMessage(errorMsg, DwtMessageDialog.WARNING_STYLE); 529 msgDlg.popup(); 530 } else { 531 var params = { 532 attachment: false, 533 uploadFolder: cFolder, 534 files: files, 535 notes: "", 536 allResponses: null, 537 start: 0, 538 curView: null, 539 preAllCallback: null, 540 initOneUploadCallback: null, 541 progressCallback: null, 542 errorCallback: null, 543 completeOneCallback: null, 544 completeAllCallback: this.uploadSaveDocs.bind(this), 545 completeDocSaveCallback: this._finishUpload.bind(this, null) 546 } 547 uploadManager.upload(params); 548 } 549 } 550 } 551 }; 552 553 ZmBriefcaseApp.prototype.chkFolderPermission = function(folderId){ 554 var briefcase = appCtxt.getById(folderId); 555 if(briefcase.isRemote() && briefcase.isReadOnly()){ 556 var dialog = appCtxt.getMsgDialog(); 557 dialog.setMessage(ZmMsg.errorPermissionCreate, DwtMessageDialog.WARNING_STYLE); 558 dialog.popup(); 559 return false; 560 } 561 return true; 562 }; 563 564 // --- Briefcase Upload Completion - SaveDocuments and Conflict resolution ------ 565 566 /** 567 * uploadSaveDocs performs SaveDocument calls, creating a document with an associated uploadId. If the file 568 * already exists, conflict resolution is performed. 569 * 570 * @param {object} params params to customize the upload flow: 571 * uploadFolder Folder to save associated document into 572 * files: raw File object from the external HTML5 drag and drop 573 * notes: Notes associated with each of the files being added 574 * allResponses: All the server responses. Contains the uploadId (guid) for a file 575 * errorCallback: Run upon an error 576 * conflictAction If specified, the action used to resolve a file conflict 577 * preResolveConflictCallback: Standard processing (SaveDocument), Run prior to conflict resolution 578 * completeDocSaveCallback: Standard processing (SaveDocument), Run when all documents have been saved 579 * 580 */ 581 ZmBriefcaseApp.prototype.uploadSaveDocs = function(allResponses, params, status, guids) { 582 if (status != AjxPost.SC_OK) { 583 var errorMessage = appCtxt.getAppController().createErrorMessage(ZmItem.BRIEFCASE, status); 584 this._popupErrorDialog(errorMessage, params.errorCallback); 585 } else { 586 var docFiles; 587 if (allResponses) { 588 // External DnD files 589 docFiles = []; 590 var files = params.files; 591 if (allResponses.length === files.length) { 592 for (var i = 0; i < files.length; i++){ 593 var file = files[i]; 594 var response = allResponses[i]; 595 var aid = (response && response.aid); 596 docFiles.push( 597 {name: file.name, 598 fullname: file.name, 599 notes: params.notes, 600 version: file.version, 601 id: file.id, 602 guid: aid, 603 preventDuplicate: file.preventDuplicate}); 604 } 605 params.docFiles = docFiles; 606 } 607 } else { 608 // AjxPost callback, providing the guids separately 609 docFiles = params.docFiles; 610 if (guids) { 611 guids = guids.split(","); 612 for (var i = 0; i < docFiles.length; i++) { 613 DBG.println("guids[" + i + "]: " + guids[i] + ", files[" + i + "]: " + docFiles[i]); 614 docFiles[i].guid = guids[i]; 615 } 616 } 617 } 618 if (params.uploadFolder) { 619 this._uploadSaveDocs2(params); 620 } else { 621 this._completeUpload(params); 622 } 623 } 624 }; 625 626 ZmBriefcaseApp.prototype._popupErrorDialog = function(message, errorCallback) { 627 if (errorCallback) { 628 errorCallback.run(); 629 } 630 var dialog = appCtxt.getMsgDialog(); 631 dialog.setMessage(message, DwtMessageDialog.CRITICAL_STYLE); 632 dialog.popup(); 633 }; 634 635 ZmBriefcaseApp.prototype._uploadSaveDocs2 = function(params) { 636 637 // create document wrappers 638 var request = []; 639 var foundOne = false; 640 var docFiles = params.docFiles; 641 for (var i = 0; i < docFiles.length; i++) { 642 var file = docFiles[i]; 643 if (file.done) { 644 continue; 645 } 646 foundOne = true; 647 648 var SaveDocumentRequest = { _jsns: "urn:zimbraMail", requestId: i, doc: {}} 649 var doc = SaveDocumentRequest.doc; 650 if (file.id) { 651 doc.id = file.id; 652 doc.ver = file.version; 653 } else { 654 doc.l = params.uploadFolder.id; 655 } 656 if (file.notes) { 657 doc.desc = file.notes; 658 } 659 doc.upload = { 660 id: file.guid 661 } 662 request.push(SaveDocumentRequest); 663 } 664 665 if (foundOne) { 666 var json = { 667 BatchRequest: { 668 _jsns: "urn:zimbra", 669 onerror: "continue", 670 SaveDocumentRequest: ( (request.length == 1) ? request[0] : request ) 671 } 672 }; 673 var callback = this._uploadSaveDocsResponse.bind(this, params); 674 var saveDocParams = { 675 jsonObj: json, 676 asyncMode:true, 677 callback: callback 678 }; 679 var appController = appCtxt.getAppController(); 680 appController.sendRequest(saveDocParams); 681 } 682 else { 683 // This calls the callback of the client - e.g. ZmHtmlEditor.prototype._imageUploaded since 684 // _uploadSaveDocsResponse is not called in this case, we still need the client callback since the 685 // user chose the "old" version of the image 686 this._completeUpload(params); 687 } 688 }; 689 690 ZmBriefcaseApp.prototype._uploadSaveDocsResponse = function(params, response) { 691 var resp = response && response._data && response._data.BatchResponse; 692 var docFiles = params.docFiles; 693 694 // mark successful uploads 695 if (resp && resp.SaveDocumentResponse) { 696 for (var i = 0; i < resp.SaveDocumentResponse.length; i++) { 697 var saveDocResp = resp.SaveDocumentResponse[i]; 698 docFiles[saveDocResp.requestId].done = true; 699 docFiles[saveDocResp.requestId].name = saveDocResp.doc[0].name; 700 docFiles[saveDocResp.requestId].id = saveDocResp.doc[0].id; 701 docFiles[saveDocResp.requestId].ver = saveDocResp.doc[0].ver; 702 docFiles[saveDocResp.requestId].version = saveDocResp.doc[0].ver; 703 } 704 } 705 706 // check for conflicts 707 var mailboxQuotaExceeded = false; 708 var alreadyExists = false; 709 var uploadRejected = false; 710 var isItemLocked = false; 711 var code = 0; 712 var conflicts = []; 713 if (resp && resp.Fault) { 714 var errors = []; 715 var uploadRejected = false, rejectedFile = "Unknown", rejectedReason = "Unknown"; 716 for (var i = 0; i < resp.Fault.length; i++) { 717 var fault = resp.Fault[i]; 718 var error = fault.Detail.Error; 719 code = error.Code; 720 var attrs = error.a; 721 isItemLocked = (code == ZmCsfeException.LOCKED); 722 var file = docFiles[fault.requestId]; 723 if ((code == ZmCsfeException.MAIL_ALREADY_EXISTS) && file.preventDuplicate) { 724 alreadyExists = true; 725 } else if (code == ZmCsfeException.MAIL_ALREADY_EXISTS || 726 code == ZmCsfeException.MODIFY_CONFLICT) { 727 for (var p in attrs) { 728 var attr = attrs[p]; 729 switch (attr.n) { 730 case "itemId" : { file.id = attr._content; break } 731 case "id": { file.id = attr._content; break; } 732 case "ver": { file.version = attr._content; break; } 733 case "name": { file.name = attr._content; break; } 734 } 735 } 736 file.version = file.version || 1; 737 conflicts.push(file); 738 }else { 739 DBG.println("Unknown error occurred: " + code); 740 if (code == ZmCsfeException.MAIL_QUOTA_EXCEEDED) { 741 mailboxQuotaExceeded = true; 742 } else if (code === ZmCsfeException.UPLOAD_REJECTED) { 743 uploadRejected = true; 744 for (var p in attrs) { 745 var attr = attrs[p]; 746 switch (attr.n) { 747 case "reason" : rejectedReason = attr._content; break; 748 case "name": rejectedFile = attr._content; break; 749 } 750 } 751 } 752 753 errors[fault.requestId] = fault; 754 } 755 } 756 } 757 758 // dismiss dialog/enable the upload button 759 if (params.preResolveConflictCallback) { 760 params.preResolveConflictCallback.run(); 761 } 762 763 // TODO: What to do about other errors? 764 // TODO: This should handle reporting several errors at once 765 if (mailboxQuotaExceeded) { 766 this._popupErrorDialog(ZmMsg.errorQuotaExceeded, params.errorCallback); 767 return; 768 } else if (alreadyExists) { 769 this._popupErrorDialog(AjxMessageFormat.format(ZmMsg.itemWithFileNameExits, file.name), params.errorCallback); 770 return; 771 } else if (isItemLocked) { 772 this._popupErrorDialog(ZmMsg.errorItemLocked, params.errorCallback); 773 return; 774 } else if (uploadRejected) { 775 var rejectedMsg = AjxMessageFormat.format(ZmMsg.uploadRejectedError, [ rejectedFile, rejectedReason ] ); 776 this._popupErrorDialog(rejectedMsg, params.errorCallback); 777 return; 778 } 779 else if (code == ZmCsfeException.SVC_PERM_DENIED) { 780 this._popupErrorDialog(ZmMsg.errorPermissionDenied, params.errorCallback); 781 return; 782 } 783 784 // resolve conflicts 785 var conflictCount = conflicts.length; 786 787 var action = params.conflictAction || ZmBriefcaseApp.ACTION_KEEP_MINE; 788 if (conflictCount > 0 && action == ZmBriefcaseApp.ACTION_ASK) { 789 var dialog = appCtxt.getUploadConflictDialog(); 790 dialog.popup(params.uploadFolder, conflicts, this._uploadSaveDocs2.bind(this, params)); 791 } else if (conflictCount > 0 && action == ZmBriefcaseApp.ACTION_KEEP_MINE) { 792 if (params.conflictAction) { 793 this._shieldSaveDocs(params); 794 } else { 795 this._uploadSaveDocs2(params); 796 } 797 } else { 798 this._completeUpload(params); 799 } 800 }; 801 802 ZmBriefcaseApp.prototype._shieldSaveDocs = function(params) { 803 var dlg = appCtxt.getYesNoMsgDialog(); 804 dlg.reset(); 805 dlg.setButtonListener(DwtDialog.YES_BUTTON, new AjxListener(this, this._shieldSaveDocsYesCallback, [dlg, params])); 806 dlg.setMessage(ZmMsg.uploadConflictShield, DwtMessageDialog.WARNING_STYLE, ZmMsg.uploadConflict); 807 dlg.popup(); 808 }; 809 810 ZmBriefcaseApp.prototype._shieldSaveDocsYesCallback = function(dlg, params) { 811 this._uploadSaveDocs2(params); 812 dlg.popdown(); 813 }; 814 815 ZmBriefcaseApp.prototype._completeUpload = function(params) { 816 if (params.completeDocSaveCallback) { 817 params.completeDocSaveCallback.run(params.docFiles, params.uploadFolder); 818 } 819 }; 820 821 ZmBriefcaseApp.prototype._finishUpload = function(finishCallback, docFiles, uploadFolder) { 822 var filenames = []; 823 for (var i in docFiles) { 824 var name = docFiles[i].name; 825 filenames.push(name); 826 } 827 this._handlePostUpload(uploadFolder, filenames, docFiles); 828 829 if (finishCallback) { 830 finishCallback(docFiles); 831 } 832 }; 833 834 ZmBriefcaseApp.prototype._handlePostUpload = function(folder, filenames, files) { 835 var msg = ZmMsg.successfullyUploaded; 836 if(files.length > 1){ 837 msg = AjxMessageFormat.format(ZmMsg.successfullyUploadedFiles, files.length); 838 } 839 appCtxt.setStatusMsg(msg, ZmStatusView.LEVEL_INFO); 840 // Remove the previous selection(s) 841 var briefcaseController = AjxDispatcher.run("GetBriefcaseController"); 842 briefcaseController.resetSelection(); 843 }; 844 845 846 847 ZmBriefcaseApp.ACTION_KEEP_MINE = "mine"; 848 ZmBriefcaseApp.ACTION_KEEP_THEIRS = "theirs"; 849 ZmBriefcaseApp.ACTION_ASK = "ask"; 850 851 852