1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 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) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file defines the tag tree controller. 27 * 28 */ 29 30 /** 31 * Creates a tag tree controller. 32 * @class 33 * This class controls a tree display of tags. 34 * 35 * @extends ZmTreeController 36 */ 37 ZmTagTreeController = function() { 38 39 ZmTreeController.call(this, ZmOrganizer.TAG); 40 41 this._listeners[ZmOperation.NEW_TAG] = this._newListener.bind(this); 42 this._listeners[ZmOperation.RENAME_TAG] = this._renameListener.bind(this); 43 this._listeners[ZmOperation.TAG_COLOR_MENU] = this._colorListener.bind(this); 44 }; 45 46 ZmTagTreeController.prototype = new ZmTreeController; 47 ZmTagTreeController.prototype.constructor = ZmTagTreeController; 48 49 ZmTagTreeController.prototype.isZmTagTreeController = true; 50 ZmTagTreeController.prototype.toString = function() { return "ZmTagTreeController"; }; 51 52 // Public methods 53 54 /** 55 * Adds listeners for the color change menu items. 56 * 57 * @return {ZmActionMenu} the action menu 58 * 59 * @private 60 */ 61 ZmTagTreeController.prototype._getActionMenu = 62 function() { 63 var menu = ZmTreeController.prototype._getActionMenu.call(this); 64 if (menu && !menu._initialized) { 65 var mi = menu.getMenuItem(ZmOperation.TAG_COLOR_MENU); 66 if (mi) { 67 mi.getMenu().addSelectionListener(this._listeners[ZmOperation.TAG_COLOR_MENU]); 68 } 69 menu._initialized = true; 70 } 71 return menu; 72 }; 73 74 /** 75 * Resets and enables/disables operations based on context. 76 * 77 * @param {Object} parent the widget that contains the operations 78 * @param {String} id the currently selected/activated organizer 79 */ 80 ZmTagTreeController.prototype.resetOperations = 81 function(parent, type, id) { 82 var tag = appCtxt.getById(id); 83 parent.enableAll(true); 84 if (tag.isSystem()) { 85 parent.enable([ZmOperation.RENAME_TAG, 86 ZmOperation.TAG_COLOR_MENU, ZmOperation.DELETE_WITHOUT_SHORTCUT], false); 87 } 88 parent.enable(ZmOperation.MARK_ALL_READ, (tag && (tag.numUnread > 0))); 89 // this._resetOperation(parent, ZmOperation.EXPORT_FOLDER, ZmMsg.exportTag); 90 }; 91 92 // Private/protected methods 93 94 /** 95 * Returns ops available for "Tags" container. 96 * 97 * @private 98 */ 99 ZmTagTreeController.prototype._getHeaderActionMenuOps = 100 function() { 101 return [ZmOperation.NEW_TAG]; 102 }; 103 104 /** 105 * Returns ops available for tags. 106 * 107 * @private 108 */ 109 ZmTagTreeController.prototype._getActionMenuOps = function() { 110 111 return [ 112 ZmOperation.NEW_TAG, 113 ZmOperation.MARK_ALL_READ, 114 ZmOperation.DELETE_WITHOUT_SHORTCUT, 115 ZmOperation.RENAME_TAG, 116 ZmOperation.TAG_COLOR_MENU, 117 ZmOperation.OPEN_IN_TAB 118 ]; 119 }; 120 121 /** 122 * Returns a "New Tag" dialog. 123 * 124 * @private 125 */ 126 ZmTagTreeController.prototype._getNewDialog = 127 function() { 128 return appCtxt.getNewTagDialog(); 129 }; 130 131 /** 132 * Returns a "Rename Tag" dialog. 133 * 134 * @private 135 */ 136 ZmTagTreeController.prototype._getRenameDialog = 137 function() { 138 return appCtxt.getRenameTagDialog(); 139 }; 140 141 // Actions 142 143 /** 144 * Called when a left click occurs (by the tree view listener). A search for items with 145 * the tag will be performed. 146 * 147 * @param {ZmTag} tag the tag that was clicked 148 * 149 * @private 150 */ 151 ZmTagTreeController.prototype._itemClicked = function(tag, openInTab) { 152 153 var searchFor; 154 switch (appCtxt.getCurrentAppName()) { 155 case ZmApp.CONTACTS: searchFor = ZmItem.CONTACT; break; 156 case ZmApp.CALENDAR: searchFor = ZmItem.APPT; break; 157 case ZmApp.BRIEFCASE: searchFor = ZmItem.BRIEFCASE_ITEM; break; 158 case ZmApp.TASKS: searchFor = ZmItem.TASK; break; 159 default: searchFor = ZmId.SEARCH_MAIL; break; 160 } 161 162 var params = { 163 query: tag.createQuery(), 164 searchFor: searchFor, 165 noGal: true, 166 inclSharedItems: true, 167 getHtml: appCtxt.get(ZmSetting.VIEW_AS_HTML), 168 accountName: appCtxt.multiAccounts ? tag.getAccount().name : null, 169 userInitiated: openInTab 170 }; 171 172 //Bug:45878 Don't do a multi-account search for tags 173 var sc = appCtxt.getSearchController(); 174 sc.searchAllAccounts = false; 175 sc.search(params); 176 }; 177 178 // Listeners 179 180 /** 181 * Deletes a tag. A dialog will first be displayed asking the user if they 182 * are sure they want to delete the tag. 183 * 184 * @param {DwtUiEvent} ev the UI event 185 * 186 * @private 187 */ 188 ZmTagTreeController.prototype._deleteListener = 189 function(ev) { 190 var organizer = this._pendingActionData = this._getActionedOrganizer(ev); 191 var ds = this._deleteShield = appCtxt.getYesNoMsgDialog(); 192 ds.reset(); 193 ds.registerCallback(DwtDialog.NO_BUTTON, this._clearDialog, this, this._deleteShield); 194 ds.registerCallback(DwtDialog.YES_BUTTON, this._deleteShieldYesCallback, this, organizer); 195 var msg = AjxMessageFormat.format(ZmMsg.askDeleteTag, organizer.getName(false, ZmOrganizer.MAX_DISPLAY_NAME_LENGTH)); 196 ds.setMessage(msg, DwtMessageDialog.WARNING_STYLE); 197 ds.popup(); 198 }; 199 200 /** 201 * Changes the color of a tag. 202 * 203 * @param {DwtUiEvent} ev the UI event 204 * 205 * @private 206 */ 207 ZmTagTreeController.prototype._colorListener = 208 function(ev) { 209 var tag = this._getActionedOrganizer(ev); 210 if (tag) { 211 var color = ev.item.getData(ZmOperation.MENUITEM_ID); 212 if (String(color).match(/^#/)) { 213 tag.setRGB(color); 214 } 215 else { 216 tag.setColor(color); 217 } 218 } 219 }; 220 221 /** 222 * Handles the potential drop of something onto a tag. Only items may be dropped. 223 * The source data is not the items themselves, but an object with the items (data) 224 * and their controller, so they can be moved appropriately. Dropping an item onto 225 * a tag causes the item to be tagged. 226 * 227 * @param {DwtDropEvent} ev the drop event 228 * 229 * @private 230 */ 231 ZmTagTreeController.prototype._dropListener = 232 function(ev) { 233 var data = ev.srcData.data; 234 if (ev.action == DwtDropEvent.DRAG_ENTER) { 235 var sample = (data instanceof Array) ? data[0] : data; 236 var tag = ev.targetControl.getData(Dwt.KEY_OBJECT); 237 if (tag.id == ZmOrganizer.ID_ROOT) { 238 ev.doIt = false; 239 } else if (sample instanceof ZmItem && sample.isReadOnly()) { 240 ev.doIt = false; 241 } else if (appCtxt.multiAccounts && tag.getAccount() != sample.account) { 242 ev.doIt = false; 243 } else { 244 ev.doIt = this._dropTgt.isValidTarget(data); 245 } 246 } else if (ev.action == DwtDropEvent.DRAG_DROP) { 247 var ctlr = ev.srcData.controller; 248 var items = (data instanceof Array) ? data : [data]; 249 ctlr._doTag(items, ev.targetControl.getData(Dwt.KEY_OBJECT), true); 250 } 251 }; 252 253 /** 254 * Handles a color change event. 255 * 256 * @param {ZmEvent} ev the change event 257 * @param {ZmTreeView} treeView the tree view 258 * @param {constant} overviewId the overview ID 259 * 260 * @private 261 */ 262 ZmTagTreeController.prototype._changeListener = 263 function(ev, treeView, overviewId) { 264 var fields = ev.getDetail("fields"); 265 var organizers = ev.getDetail("organizers"); 266 for (var i = 0; i < organizers.length; i++) { 267 var tag = organizers[i]; 268 if (ev.event == ZmEvent.E_MODIFY && ((fields && fields[ZmOrganizer.F_COLOR]))) { 269 var node = treeView.getTreeItemById(tag.id); 270 if (node) 271 node.setImage(tag.getIconWithColor()); 272 } else { 273 ZmTreeController.prototype._changeListener.call(this, ev, treeView, overviewId); 274 } 275 } 276 }; 277 278 /** 279 * @private 280 */ 281 ZmTagTreeController.prototype._setTreeItemColor = 282 function(treeItem, organizer) { 283 }; 284