1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 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 contains the task tree controller class.
 27  */
 28 
 29 /**
 30  * Creates the task tree controller.
 31  * @class
 32  * This class represents the task tree controller.
 33  * 
 34  * @extends		ZmFolderTreeController
 35  */
 36 ZmTaskTreeController = function() {
 37 
 38 	ZmFolderTreeController.call(this, ZmOrganizer.TASKS);
 39 
 40 	this._listeners[ZmOperation.NEW_TASK_FOLDER] = new AjxListener(this, this._newListener);
 41 	this._listeners[ZmOperation.SHARE_TASKFOLDER] = new AjxListener(this, this._shareTaskFolderListener);
 42 
 43 	this._eventMgrs = {};
 44 };
 45 
 46 ZmTaskTreeController.prototype = new ZmFolderTreeController;
 47 ZmTaskTreeController.prototype.constructor = ZmTaskTreeController;
 48 
 49 /**
 50  * Returns a string representation of the object.
 51  * 
 52  * @return		{String}		a string representation of the object
 53  */
 54 ZmTaskTreeController.prototype.toString =
 55 function() {
 56 	return "ZmTaskTreeController";
 57 };
 58 
 59 // Public methods
 60 
 61 /**
 62  * Displays the tree of this type.
 63  *
 64  * @param {Hash}	params		a hash of parameters
 65  * @param	{constant}	params.overviewId		the overview ID
 66  * @param	{Boolean}	params.showUnread		if <code>true</code>, unread counts will be shown
 67  * @param	{Object}	params.omit				a hash of organizer IDs to ignore
 68  * @param	{Object}	params.include			a hash of organizer IDs to include
 69  * @param	{Boolean}	params.forceCreate		if <code>true</code>, tree view will be created
 70  * @param	{String}	params.app				the app that owns the overview
 71  * @param	{Boolean}	params.hideEmpty		if <code>true</code>, don't show header if there is no data
 72  * @param	{Boolean}	params.noTooltips	if <code>true</code>, don't show tooltips for tree items
 73  */
 74 ZmTaskTreeController.prototype.show = function(params) {
 75 	params.include = params.include || {};
 76     params.include[ZmFolder.ID_TRASH] = true;
 77     params.showUnread = false;
 78     return ZmFolderTreeController.prototype.show.call(this, params);
 79 };
 80 
 81 ZmTaskTreeController.prototype.resetOperations =
 82 function(parent, type, id) {
 83 	var deleteText = ZmMsg.del;
 84 	var folder = appCtxt.getById(id);
 85     var isShareVisible = true;
 86 
 87 	parent.enableAll(true);
 88 	if (folder) {
 89         if (folder.isSystem() || appCtxt.isExternalAccount()) {
 90             parent.enable([ZmOperation.DELETE_WITHOUT_SHORTCUT, ZmOperation.RENAME_FOLDER], false);
 91         } else if (folder.link && !folder.isAdmin()) {
 92             isShareVisible = false;
 93         }
 94         if (appCtxt.isOffline) {
 95             var acct = folder.getAccount();
 96             isShareVisible = !acct.isMain && acct.isZimbraAccount;
 97         }
 98         parent.enable([ZmOperation.SHARE_TASKFOLDER], isShareVisible);
 99         parent.enable(ZmOperation.SYNC, folder.isFeed());
100 	}
101 
102     parent.enable(ZmOperation.EMPTY_FOLDER,((folder.numTotal > 0) || (folder.children && (folder.children.size() > 0))));
103     var nId = ZmOrganizer.normalizeId(id);
104     var isTrash = nId == ZmOrganizer.ID_TRASH;
105 	this.setVisibleIfExists(parent, ZmOperation.EMPTY_FOLDER, isTrash);
106 
107 	parent.enable(ZmOperation.EDIT_PROPS, !isTrash);
108 	var emptyFolderOp = parent.getOp(ZmOperation.EMPTY_FOLDER);
109 	if (emptyFolderOp) {
110 		emptyFolderOp.setText(ZmMsg.emptyTrash);
111 	}
112 
113 	this._enableRecoverDeleted(parent, isTrash);
114 
115 	var op = parent.getOp(ZmOperation.DELETE_WITHOUT_SHORTCUT);
116 	if (op) {
117 		op.setText(deleteText);
118 	}
119 
120     parent.enable(ZmOperation.NEW_TASK_FOLDER, !isTrash && !appCtxt.isExternalAccount());
121 
122 
123 	// we always enable sharing in case we're in multi-mbox mode
124 	// But no sharing for trash folder
125 	this._resetButtonPerSetting(parent, ZmOperation.SHARE_TASKFOLDER, !isTrash && appCtxt.get(ZmSetting.SHARING_ENABLED));
126 };
127 
128 ZmTaskTreeController.prototype._getAllowedSubTypes =
129 function() {
130 	return ZmTreeController.prototype._getAllowedSubTypes.call(this);
131 };
132 
133 ZmTaskTreeController.prototype._getSearchTypes =
134 function(ev) {
135 	return [ZmItem.TASK];
136 };
137 /*
138 * Returns a "New Task Folder" dialog.
139 */
140 ZmTaskTreeController.prototype._getNewDialog =
141 function() {
142 	return appCtxt.getNewTaskFolderDialog();
143 };
144 
145 ZmTaskTreeController.prototype._newCallback =
146 function(params) {
147     // For a task, set the parent folder (params.l) if specified
148     var folder = this._pendingActionData instanceof ZmOrganizer ? this._pendingActionData :
149                     (this._pendingActionData && this._pendingActionData.organizer);
150     if (folder) {
151         params.l = folder.id;
152     }
153     ZmTreeController.prototype._newCallback.call(this, params);
154 };
155 
156 // Returns a list of desired header action menu operations
157 ZmTaskTreeController.prototype._getHeaderActionMenuOps =
158 function() {
159     if (appCtxt.isExternalAccount()) {
160         return [];
161     }
162 	return [ZmOperation.NEW_TASK_FOLDER, ZmOperation.FIND_SHARES];
163 };
164 
165 // Returns a list of desired action menu operations
166 ZmTaskTreeController.prototype._getActionMenuOps = function() {
167 
168 	return [
169         ZmOperation.NEW_TASK_FOLDER,
170 		ZmOperation.SYNC,
171 		ZmOperation.EMPTY_FOLDER,
172 		ZmOperation.RECOVER_DELETED_ITEMS,
173 		ZmOperation.SHARE_TASKFOLDER,
174 		ZmOperation.DELETE_WITHOUT_SHORTCUT,
175 		ZmOperation.RENAME_FOLDER,
176 		ZmOperation.EDIT_PROPS
177 	];
178 };
179 
180 
181 // Listeners
182 
183 ZmTaskTreeController.prototype._shareTaskFolderListener =
184 function(ev) {
185 	this._pendingActionData = this._getActionedOrganizer(ev);
186 	appCtxt.getSharePropsDialog().popup(ZmSharePropsDialog.NEW, this._pendingActionData);
187 };
188 
189 ZmTaskTreeController.prototype._deleteListener =
190 function(ev) {
191     var organizer = this._getActionedOrganizer(ev);
192     if (organizer.isInTrash()) {
193         var callback = new AjxCallback(this, this._deleteListener2, [organizer]);
194         var message = AjxMessageFormat.format(ZmMsg.confirmDeleteTaskFolder, AjxStringUtil.htmlEncode(organizer.name));
195 
196         appCtxt.getConfirmationDialog().popup(message, callback);
197     }
198     else {
199         this._doMove(organizer, appCtxt.getById(ZmOrganizer.ID_TRASH));
200     }
201 };
202 
203 ZmTaskTreeController.prototype._deleteListener2 =
204 function(organizer) {
205     this._doDelete(organizer);
206 };
207 
208 /**
209  * Called when a left click occurs (by the tree view listener). The folder that
210  * was clicked may be a search, since those can appear in the folder tree. The
211  * appropriate search will be performed.
212  *
213  * @param {ZmOrganizer}		folder		folder or search that was clicked
214  * 
215  * @private
216  */
217 ZmTaskTreeController.prototype._itemClicked =
218 function(folder) {
219 	appCtxt.getApp(ZmApp.TASKS).search(folder);
220 };
221 
222 /**
223  * Gets the task Folders.
224  *
225  * @param	{String}	overviewId		the overview id
226  * @param   {boolean}   includeTrash    True to include trash, if checked.
227  * @return	{Array}		an array of {@link ZmCalendar} objects
228  */
229 ZmTaskTreeController.prototype.getTaskFolders =
230 function(overviewId, includeTrash) {
231 	var tasks = [];
232 	var items = this._getItems(overviewId);
233 	for (var i = 0; i < items.length; i++) {
234 		var item = items[i];
235 		if (item._isSeparator) { continue; }
236 		var task = item.getData(Dwt.KEY_OBJECT);
237         if (task && task.id == ZmOrganizer.ID_TRASH && !includeTrash && task.type) continue;
238 		if (task) tasks.push(task);
239 	}
240 
241 	return tasks;
242 };
243 
244 
245 ZmTaskTreeController.prototype._getItems =
246 function(overviewId) {
247 	var treeView = this.getTreeView(overviewId);
248 	if (treeView) {
249 		var account = appCtxt.multiAccounts ? treeView._overview.account : null;
250 		if (!appCtxt.get(ZmSetting.TASKS_ENABLED, null, account)) { return []; }
251 
252 		var rootId = ZmOrganizer.getSystemId(ZmOrganizer.ID_ROOT, account);
253 		var root = treeView.getTreeItemById(rootId);
254 		if (root) {
255 			var totalItems = [];
256 			this._getSubItems(root, totalItems);
257 			return totalItems;
258 		}
259 	}
260 	return [];
261 };
262 
263 ZmTaskTreeController.prototype._getSubItems =
264 function(root, totalItems) {
265 	if (!root || (root && root._isSeparator)) { return; }
266 
267 	var items = root.getItems();
268 	for (var i in items) {
269 		var item = items[i];
270 		if (item && !item._isSeparator) {
271 			totalItems.push(item);
272 			this._getSubItems(item, totalItems);
273 		}
274 	}
275 };
276