1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 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 a search folder class.
 27  */
 28 
 29 /**
 30  * Creates the search folder.
 31  * @class
 32  * This class represents a search folder.
 33  * 
 34  * @param	{Hash}	params		a hash of parameters
 35  * 
 36  * @extends	ZmFolder
 37  */
 38 ZmSearchFolder = function(params) {
 39 	params.type = ZmOrganizer.SEARCH;
 40 	ZmFolder.call(this, params);
 41 	
 42 	if (params.query) {
 43 		var searchParams = {
 44 			query:			params.query,
 45 			types:			params.types,
 46 			checkTypes:		true,
 47 			sortBy:			params.sortBy,
 48 			searchId:		params.id,
 49 			accountName:	(params.account && params.account.name)
 50 		};
 51 		this.search = new ZmSearch(searchParams);
 52 	}
 53 };
 54 
 55 ZmSearchFolder.prototype = new ZmFolder;
 56 ZmSearchFolder.prototype.constructor = ZmSearchFolder;
 57 
 58 /**
 59  * Returns a string representation of the object.
 60  *
 61  * @return		{String}		a string representation of the object
 62  */
 63 ZmSearchFolder.prototype.toString =	function() {
 64 	return "ZmSearchFolder";
 65 };
 66 
 67 ZmSearchFolder.ID_ROOT = ZmOrganizer.ID_ROOT;
 68 
 69 /**
 70  * Creates a search folder.
 71  * 
 72  * @param	{Hash}	params		a hash of parameters
 73  */
 74 ZmSearchFolder.create = function(params) {
 75 
 76 	params = params || {};
 77 
 78 	var search = params.search,
 79 		jsonObj = { CreateSearchFolderRequest: { _jsns:"urn:zimbraMail" } },
 80 		searchNode = jsonObj.CreateSearchFolderRequest.search = {};
 81 
 82 	searchNode.name = params.name;
 83 	searchNode.query = search.query;
 84 	searchNode.l = params.l;
 85 	if (params.sortBy) {
 86 		searchNode.sortBy = params.sortBy;
 87 	}
 88 
 89 	searchNode.types = ZmSearchFolder._getSearchTypes(search);
 90 
 91 	if (params.rgb) {
 92 		searchNode.rgb = params.rgb;
 93 	}
 94 	else if (params.color) {
 95 		var color = ZmOrganizer.getColorValue(params.color, params.type);
 96 		if (color) {
 97 			searchNode.color = color;
 98 		}
 99 	}
100 
101 	var accountName;
102 	if (params.isGlobal) {
103 		searchNode.f = 'g';
104 		accountName = appCtxt.accountList.mainAccount.name;
105 	}
106 
107 	return appCtxt.getAppController().sendRequest({
108 		jsonObj:        jsonObj,
109 		asyncMode:      params.asyncMode !== false,
110 		accountName:    accountName,
111 		callback:       ZmSearchFolder._handleCreate,
112 		errorCallback:  params.errorCallback || ZmOrganizer._handleErrorCreate.bind(null)
113 	});
114 };
115 
116 // converts a vector of types to a string the server can understand
117 ZmSearchFolder._getSearchTypes = function(search) {
118 
119 	var typeStr = "";
120 	if (search && search.types) {
121 		var a = search.types.getArray();
122 		if (a.length) {
123 			var typeStr = [];
124 			for (var i = 0; i < a.length; i++) {
125 				typeStr.push(ZmSearch.TYPE[a[i]]);
126 			}
127 			typeStr = typeStr.join(",");
128 		}
129 	}
130 	return typeStr;
131 };
132 
133 ZmSearchFolder._handleCreate =
134 function(params) {
135 	appCtxt.setStatusMsg(ZmMsg.searchSaved);
136 };
137 
138 /**
139  * Sets the underlying search query.
140  *
141  * @param	{String}	    query		    search query
142  * @param	{AjxCallback}	callback		the callback
143  * @param	{AjxCallback}	errorCallback		the error callback
144  * @param	{ZmBatchCommand}	batchCmd		the batch command
145  */
146 ZmSearchFolder.prototype.setQuery = function(query, callback, errorCallback, batchCmd) {
147 
148 	if (query === this.search.query) {
149 		return;
150 	}
151 
152 	var params = {
153 		callback:       callback,
154 		errorCallback:  errorCallback,
155 		batchCmd:       batchCmd
156 	};
157 
158 	var cmd = "ModifySearchFolderRequest";
159 	var request = {
160 		_jsns: "urn:zimbraMail",
161 		search: {
162 			query:  query,
163 			id:     params.id || this.id,
164 			types:  ZmSearchFolder._getSearchTypes(this.search)
165 		}
166 	};
167 	var jsonObj = {};
168 	jsonObj[cmd] = request;
169 
170 	var respCallback = this._handleResponseOrganizerAction.bind(this, params);
171 	if (params.batchCmd) {
172 		params.batchCmd.addRequestParams(jsonObj, respCallback, params.errorCallback);
173 	}
174 	else {
175 		var accountName;
176 		if (appCtxt.multiAccounts) {
177 			accountName = this.account ? this.account.name : appCtxt.accountList.mainAccount.name;
178 		}
179 		appCtxt.getAppController().sendRequest({
180 			jsonObj:        jsonObj,
181 			asyncMode:      true,
182 			accountName:    accountName,
183 			callback:       respCallback,
184 			errorCallback:  params.errorCallback
185 		});
186 	}
187 };
188 
189 /**
190  * Gets the icon.
191  * 
192  * @return	{String}	the icon
193  */
194 ZmSearchFolder.prototype.getIcon = 
195 function() {
196 	return (this.nId == ZmOrganizer.ID_ROOT)
197 		? null
198 		: (this.isOfflineGlobalSearch ? "GlobalSearchFolder" : "SearchFolder");
199 };
200 
201 /**
202  * Gets the tool tip.
203  * 
204  */
205 ZmSearchFolder.prototype.getToolTip = function() {};
206 
207 /**
208  * Returns the organizer with the given ID. Looks in this organizer's tree first.
209  * Since a search folder may have either a regular folder or another search folder
210  * as its parent, we may need to get the parent folder from another type of tree.
211  *
212  * @param {int}	parentId	the ID of the organizer to find
213  * 
214  * @private
215  */
216 ZmSearchFolder.prototype._getNewParent =
217 function(parentId) {
218 	var parent = appCtxt.getById(parentId);
219 	if (parent) {
220 		return parent;
221 	}
222 	
223 	return appCtxt.getById(parentId);
224 };
225 
226 // Handle a change to the underlying search query
227 ZmSearchFolder.prototype.notifyModify =	function(obj) {
228 
229 	if (obj.query && obj.query !== this.search.query && obj.id === this.id) {
230 		this.search.query = obj.query;
231 		var fields = {};
232 		fields[ZmOrganizer.F_QUERY] = true;
233 		this._notify(ZmEvent.E_MODIFY, {
234 			fields: fields
235 		});
236 		obj.query = null;
237 	}
238 	ZmFolder.prototype.notifyModify.apply(this, [obj]);
239 };
240