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