1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 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) 2005, 2006, 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 a folder properties dialog. 30 * @class 31 * This class represents a folder properties dialog. 32 * 33 * @param {DwtControl} parent the parent 34 * @param {String} className the class name 35 * 36 * @extends DwtDialog 37 */ 38 ZmFolderPropsDialog = function(parent, className) { 39 className = className || "ZmFolderPropsDialog"; 40 var extraButtons; 41 if (appCtxt.get(ZmSetting.SHARING_ENABLED)) { 42 extraButtons = [ 43 new DwtDialog_ButtonDescriptor(ZmFolderPropsDialog.ADD_SHARE_BUTTON, ZmMsg.addShare, DwtDialog.ALIGN_LEFT) 44 ]; 45 } 46 47 48 DwtDialog.call(this, {parent:parent, className:className, title:ZmMsg.folderProperties, extraButtons:extraButtons, id:"FolderProperties"}); 49 50 this._tabViews = []; 51 this._tabKeys = []; 52 this._tabInUse = []; 53 this._tabKeyMap = {}; 54 55 if (appCtxt.get(ZmSetting.SHARING_ENABLED)) { 56 this.registerCallback(ZmFolderPropsDialog.ADD_SHARE_BUTTON, this._handleAddShareButton, this); 57 } 58 this.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._handleOkButton)); 59 this.setButtonListener(DwtDialog.CANCEL_BUTTON, new AjxListener(this, this._handleCancelButton)); 60 61 this._folderChangeListener = new AjxListener(this, this._handleFolderChange); 62 63 this._createView(); 64 this._initializeTabGroup(); 65 }; 66 67 ZmFolderPropsDialog.prototype = new DwtDialog; 68 ZmFolderPropsDialog.prototype.constructor = ZmFolderPropsDialog; 69 70 // Constants 71 72 ZmFolderPropsDialog.ADD_SHARE_BUTTON = ++DwtDialog.LAST_BUTTON; 73 74 ZmFolderPropsDialog.SHARES_HEIGHT = "9em"; 75 76 // Tab identifiers 77 ZmFolderPropsDialog.TABKEY_PROPERTIES = "PROPERTIES_TAB"; 78 ZmFolderPropsDialog.TABKEY_RETENTION = "RETENTION_TAB"; 79 80 81 // Public methods 82 83 ZmFolderPropsDialog.prototype.toString = 84 function() { 85 return "ZmFolderPropsDialog"; 86 }; 87 88 ZmFolderPropsDialog.prototype.getTabKey = 89 function(id) { 90 var index = this._tabKeyMap[id]; 91 return this._tabKeys[index]; 92 }; 93 94 /** 95 * Pops-up the properties dialog. 96 * 97 * @param {ZmOrganizer} organizer the organizer 98 */ 99 ZmFolderPropsDialog.prototype.popup = 100 function(organizer) { 101 this._organizer = organizer; 102 for (var i = 0; i < this._tabViews.length; i++) { 103 this._tabViews[i].setOrganizer(organizer); 104 } 105 // On popup, make the property view visible 106 var tabKey = this.getTabKey(ZmFolderPropsDialog.TABKEY_PROPERTIES); 107 this._tabContainer.switchToTab(tabKey, true); 108 109 organizer.addChangeListener(this._folderChangeListener); 110 111 this._handleFolderChange(); 112 if (appCtxt.get(ZmSetting.SHARING_ENABLED)) { 113 var isShareable = ZmOrganizer.SHAREABLE[organizer.type]; 114 115 var isVisible = (!organizer.link || organizer.isAdmin()); 116 this.setButtonVisible(ZmFolderPropsDialog.ADD_SHARE_BUTTON, isVisible && isShareable); 117 } 118 119 DwtDialog.prototype.popup.call(this); 120 }; 121 122 ZmFolderPropsDialog.prototype.popdown = 123 function() { 124 if (this._organizer) { 125 this._organizer.removeChangeListener(this._folderChangeListener); 126 this._organizer = null; 127 } 128 DwtDialog.prototype.popdown.call(this); 129 }; 130 131 // Protected methods 132 133 ZmFolderPropsDialog.prototype._getSeparatorTemplate = 134 function() { 135 return ""; 136 }; 137 138 ZmFolderPropsDialog.prototype._handleEditShare = 139 function(event, share) { 140 share = share || Dwt.getObjectFromElement(DwtUiEvent.getTarget(event)); 141 var sharePropsDialog = appCtxt.getSharePropsDialog(); 142 sharePropsDialog.popup(ZmSharePropsDialog.EDIT, share.object, share); 143 return false; 144 }; 145 146 ZmFolderPropsDialog.prototype._handleRevokeShare = 147 function(event, share) { 148 share = share || Dwt.getObjectFromElement(DwtUiEvent.getTarget(event)); 149 var revokeShareDialog = appCtxt.getRevokeShareDialog(); 150 revokeShareDialog.popup(share); 151 return false; 152 }; 153 154 ZmFolderPropsDialog.prototype._handleResendShare = 155 function(event, share) { 156 157 AjxDispatcher.require("Share"); 158 share = share || Dwt.getObjectFromElement(DwtUiEvent.getTarget(event)); 159 160 // create share info 161 var tmpShare = new ZmShare({object:share.object}); 162 tmpShare.grantee.id = share.grantee.id; 163 tmpShare.grantee.email = (share.grantee.type == "guest") ? share.grantee.id : share.grantee.name; 164 tmpShare.grantee.name = share.grantee.name; 165 if (tmpShare.object.isRemote()) { 166 tmpShare.grantor.id = tmpShare.object.zid; 167 tmpShare.grantor.email = tmpShare.object.owner; 168 tmpShare.grantor.name = tmpShare.grantor.email; 169 tmpShare.link.id = tmpShare.object.rid; 170 } else { 171 tmpShare.grantor.id = appCtxt.get(ZmSetting.USERID); 172 tmpShare.grantor.email = appCtxt.get(ZmSetting.USERNAME); 173 tmpShare.grantor.name = appCtxt.get(ZmSetting.DISPLAY_NAME) || tmpShare.grantor.email; 174 tmpShare.link.id = tmpShare.object.id; 175 } 176 177 tmpShare.link.name = share.object.name; 178 tmpShare.link.view = ZmOrganizer.getViewName(share.object.type); 179 tmpShare.link.perm = share.link.perm; 180 181 if (share.grantee.type == "guest") { 182 // Pass action as ZmShare.NEW even for resend for external user 183 tmpShare._sendShareNotification(tmpShare.grantee.email, tmpShare.link.id, "", ZmShare.NEW); 184 } 185 else { 186 tmpShare.sendMessage(ZmShare.NEW); 187 } 188 appCtxt.setStatusMsg(ZmMsg.resentShareMessage); 189 190 return false; 191 }; 192 193 ZmFolderPropsDialog.prototype._handleAddShareButton = 194 function(event) { 195 var sharePropsDialog = appCtxt.getSharePropsDialog(); 196 sharePropsDialog.popup(ZmSharePropsDialog.NEW, this._organizer, null); 197 }; 198 199 ZmFolderPropsDialog.prototype._handleOkButton = 200 function(event) { 201 202 // New batch command, stop on error 203 var batchCommand = new ZmBatchCommand(null, null, true); 204 var saveState = { 205 commandCount: 0, 206 errorMessage: [] 207 }; 208 for (var i = 0; i < this._tabViews.length; i++) { 209 if (this._tabInUse[i]) { 210 // Save all in use tabs 211 this._tabViews[i].doSave(batchCommand, saveState); 212 } 213 } 214 215 if (saveState.errorMessage.length > 0) { 216 var msg = saveState.errorMessage.join("<br>"); 217 var dialog = appCtxt.getMsgDialog(); 218 dialog.reset(); 219 dialog.setMessage(msg, DwtMessageDialog.WARNING_STYLE); 220 dialog.popup(); 221 } else if (saveState.commandCount > 0) { 222 var callback = new AjxCallback(this, this.popdown); 223 batchCommand.run(callback); 224 } else { 225 this.popdown(); 226 } 227 }; 228 229 230 231 ZmFolderPropsDialog.prototype._handleError = 232 function(response) { 233 // Returned 'not handled' so that the batch command will preform the default 234 // ZmController._handleException 235 return false; 236 }; 237 238 239 ZmFolderPropsDialog.prototype._handleCancelButton = 240 function(event) { 241 this.popdown(); 242 }; 243 244 ZmFolderPropsDialog.prototype._handleFolderChange = 245 function(event) { 246 var organizer = this._organizer; 247 248 // Get the components that will be hidden or displayed 249 var tabBar = this._tabContainer.getTabBar(); 250 var tabKey = this.getTabKey(ZmFolderPropsDialog.TABKEY_RETENTION); 251 var retentionTabButton = this._tabContainer.getTabButton(tabKey); 252 var retentionIndex = this._tabKeyMap[ZmFolderPropsDialog.TABKEY_RETENTION]; 253 254 if ((organizer.type != ZmOrganizer.FOLDER) || organizer.link) { 255 // Not a folder, or shared - hide the retention view (and possibly the toolbar) 256 this._tabInUse[retentionIndex] = false; 257 if (this._tabViews.length > 2) { 258 // More than two tabs, hide the retention tab, leave the toolbar intact 259 tabBar.setVisible(true); 260 retentionTabButton.setVisible(false); 261 } else { 262 // Two or fewer tabs. Hide the toolbar, just let the properties view display standalone 263 // (On popup, the display defaults to the property view) 264 tabBar.setVisible(false); 265 } 266 } else { 267 // Using the retention tab view - show the toolbar and all tabs 268 this._tabInUse[retentionIndex] = true; 269 retentionTabButton.setVisible(true); 270 tabBar.setVisible(true); 271 } 272 273 for (var i = 0; i < this._tabViews.length; i++) { 274 if (this._tabInUse[i]) { 275 // Update all in use tabs to use the specified folder 276 this._tabViews[i]._handleFolderChange(event); 277 } 278 } 279 280 if (appCtxt.get(ZmSetting.SHARING_ENABLED)) { 281 this._populateShares(organizer); 282 } 283 284 }; 285 286 ZmFolderPropsDialog.prototype._populateShares = 287 function(organizer) { 288 289 this._sharesGroup.setContent(""); 290 291 var displayShares = this._getDisplayShares(organizer); 292 293 var getFolder = false; 294 if (displayShares.length) { 295 for (var i = 0; i < displayShares.length; i++) { 296 var share = displayShares[i]; 297 if (!(share.grantee && share.grantee.name)) { 298 getFolder = true; 299 } 300 } 301 } 302 303 if (getFolder) { 304 var respCallback = new AjxCallback(this, this._handleResponseGetFolder, [displayShares]); 305 organizer.getFolder(respCallback); 306 } else { 307 this._handleResponseGetFolder(displayShares); 308 } 309 310 311 this._sharesGroup.setVisible(displayShares.length > 0); 312 }; 313 314 ZmFolderPropsDialog.prototype._getDisplayShares = 315 function(organizer) { 316 317 var shares = organizer.shares; 318 var displayShares = []; 319 if ((!organizer.link || organizer.isAdmin()) && shares && shares.length > 0) { 320 AjxDispatcher.require("Share"); 321 var userZid = appCtxt.accountList.mainAccount.id; 322 // if a folder was shared with us with admin rights, a share is created since we could share it; 323 // don't show any share that's for us in the list 324 for (var i = 0; i < shares.length; i++) { 325 var share = shares[i]; 326 if (share.grantee) { 327 var granteeId = share.grantee.id; 328 if ((share.grantee.type != ZmShare.TYPE_USER) || (share.grantee.id != userZid)) { 329 displayShares.push(share); 330 } 331 } 332 } 333 } 334 335 return displayShares; 336 }; 337 338 ZmFolderPropsDialog.prototype._handleResponseGetFolder = 339 function(displayShares, organizer) { 340 341 if (organizer) { 342 displayShares = this._getDisplayShares(organizer); 343 } 344 345 if (displayShares.length) { 346 var table = document.createElement("TABLE"); 347 table.className = "ZPropertySheet"; 348 table.cellSpacing = "6"; 349 for (var i = 0; i < displayShares.length; i++) { 350 var share = displayShares[i]; 351 var row = table.insertRow(-1); 352 var nameEl = row.insertCell(-1); 353 nameEl.style.paddingRight = "15px"; 354 var nameText = share.grantee && share.grantee.name; 355 if (share.isAll()) { 356 nameText = ZmMsg.shareWithAll; 357 } else if (share.isPublic()) { 358 nameText = ZmMsg.shareWithPublic; 359 } else if (share.isGuest()){ 360 nameText = nameText || (share.grantee && share.grantee.id); 361 } 362 nameEl.innerHTML = AjxStringUtil.htmlEncode(nameText); 363 364 var roleEl = row.insertCell(-1); 365 roleEl.style.paddingRight = "15px"; 366 roleEl.innerHTML = ZmShare.getRoleName(share.link.role); 367 368 this.__createCmdCells(row, share); 369 } 370 this._sharesGroup.setElement(table); 371 372 var width = Dwt.DEFAULT; 373 var height = displayShares.length > 5 ? ZmFolderPropsDialog.SHARES_HEIGHT : Dwt.CLEAR; 374 375 var insetElement = this._sharesGroup.getInsetHtmlElement(); 376 Dwt.setScrollStyle(insetElement, Dwt.SCROLL); 377 Dwt.setSize(insetElement, width, height); 378 } 379 380 this._sharesGroup.setVisible(displayShares.length > 0); 381 }; 382 383 ZmFolderPropsDialog.prototype.__createCmdCells = 384 function(row, share) { 385 var type = share.grantee.type; 386 if (type == ZmShare.TYPE_DOMAIN || !share.link.role) { 387 var cell = row.insertCell(-1); 388 cell.colSpan = 3; 389 cell.innerHTML = ZmMsg.configureWithAdmin; 390 return; 391 } 392 393 var actions = [ZmShare.EDIT, ZmShare.REVOKE, ZmShare.RESEND]; 394 var handlers = [this._handleEditShare, this._handleRevokeShare, this._handleResendShare]; 395 396 for (var i = 0; i < actions.length; i++) { 397 var action = actions[i]; 398 var cell = row.insertCell(-1); 399 400 // public shares have no editable fields, and sent no mail 401 var isAllShare = share.grantee && (share.grantee.type == ZmShare.TYPE_ALL); 402 // Fix for bug: 76685. Removed share.isGuest() from the condition and it adds edit cmd link 403 if (((isAllShare || share.isPublic()) && (action == ZmShare.EDIT)) || 404 ((isAllShare || share.isPublic()) && action == ZmShare.RESEND)) { continue; } 405 406 var link = document.createElement("A"); 407 link.href = "#"; 408 link.innerHTML = ZmShare.ACTION_LABEL[action]; 409 410 Dwt.setHandler(link, DwtEvent.ONCLICK, handlers[i]); 411 Dwt.associateElementWithObject(link, share); 412 this._sharesGroup.getTabGroupMember().addMember(link); 413 414 cell.appendChild(link); 415 } 416 }; 417 418 ZmFolderPropsDialog.prototype.addTab = 419 function(index, id, tabViewPage) { 420 if (!this._tabContainer || !tabViewPage) { return null; } 421 422 this._tabViews[index] = tabViewPage; 423 this._tabKeys[index] = this._tabContainer.addTab(tabViewPage.getTitle(), tabViewPage); 424 this._tabInUse[index] = true; 425 this._tabKeyMap[id] = index; 426 return this._tabKeys[index]; 427 }; 428 429 ZmFolderPropsDialog.prototype._initializeTabView = 430 function(view) { 431 this._tabContainer = new DwtTabView(view, null, Dwt.STATIC_STYLE); 432 433 //ZmFolderPropertyView handle things such as color and type. (in case you're searching for "color" and can't find in this file. I know I did) 434 this.addTab(0, ZmFolderPropsDialog.TABKEY_PROPERTIES, new ZmFolderPropertyView(this, this._tabContainer)); 435 this.addTab(1, ZmFolderPropsDialog.TABKEY_RETENTION, new ZmFolderRetentionView(this, this._tabContainer)); 436 437 // setup shares group 438 if (appCtxt.get(ZmSetting.SHARING_ENABLED)) { 439 this._sharesGroup = new DwtGrouper(view, "DwtGrouper ZmFolderPropSharing"); 440 this._sharesGroup.setLabel(ZmMsg.folderSharing); 441 this._sharesGroup.setVisible(false); 442 this._sharesGroup.setScrollStyle(Dwt.SCROLL); 443 view.getHtmlElement().appendChild(this._sharesGroup.getHtmlElement()); 444 } 445 }; 446 447 // This creates the tab views managed by this dialog, the tabToolbar, and 448 // the share buttons and view components 449 ZmFolderPropsDialog.prototype._createView = 450 function() { 451 this._baseContainerView = new DwtComposite({parent:this, className:"ZmFolderPropertiesDialog-container "}); 452 this._initializeTabView(this._baseContainerView); 453 this.setView(this._baseContainerView); 454 }; 455 456 ZmFolderPropsDialog.prototype._initializeTabGroup = function() { 457 458 if (this._sharesGroup) { 459 this._tabGroup.addMember(this._sharesGroup.getTabGroupMember(), 0); 460 } 461 this._tabGroup.addMember(this._tabContainer.getTabGroupMember(), 0); 462 }; 463