1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2007, 2008, 2009, 2010, 2011, 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) 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  * This file contains the briefcase class.
 27  */
 28 
 29 /**
 30  * Creates the briefcase 
 31  * @class
 32  * This class represents a briefcase. A briefcase contains briefcase items.
 33  * 
 34  * @param	{Hash}	params		a hash of parameters
 35  * @param {int}	params.id			the numeric ID
 36  * @param {String}	params.name		the name
 37  * @param {ZmOrganizer}	params.parent		the parent organizer
 38  * @param {ZmTree}	params.tree		the tree model that contains this organizer
 39  * @param {constant}	params.color	the color for this briefcase
 40  * @param {String}	params.owner		the owner of this organizer
 41  * @param {String}	params.oname		the owner's name for this organizer
 42  * @param {String}	[params.zid]		the Zimbra id of owner, if remote share
 43  * @param {String}	[params.rid]		the Remote id of organizer, if remote share
 44  * @param {String}	[params.restUrl]	the REST URL of this organizer.
 45  * 
 46  * @extends		ZmFolder
 47  */
 48 ZmBriefcase = function(params) {
 49 	params.type = ZmOrganizer.BRIEFCASE;
 50 	ZmFolder.call(this, params);
 51 }
 52 
 53 ZmBriefcase.prototype = new ZmFolder;
 54 ZmBriefcase.prototype.constructor = ZmBriefcase;
 55 
 56 // Constants
 57 
 58 ZmBriefcase.PAGE_INDEX = "_Index";
 59 ZmBriefcase.PAGE_CHROME = "_Template";
 60 ZmBriefcase.PAGE_CHROME_STYLES = "_TemplateStyles";
 61 ZmBriefcase.PAGE_TITLE_BAR = "_TitleBar";
 62 ZmBriefcase.PAGE_HEADER = "_Header";
 63 ZmBriefcase.PAGE_FOOTER = "_Footer";
 64 ZmBriefcase.PAGE_SIDE_BAR = "_SideBar";
 65 ZmBriefcase.PAGE_TOC_BODY_TEMPLATE = "_TocBodyTemplate";
 66 ZmBriefcase.PAGE_TOC_ITEM_TEMPLATE = "_TocItemTemplate";
 67 ZmBriefcase.PATH_BODY_TEMPLATE = "_PathBodyTemplate";
 68 ZmBriefcase.PATH_ITEM_TEMPLATE = "_PathItemTemplate";
 69 ZmBriefcase.PATH_SEPARATOR = "_PathSeparator";
 70 
 71 // Public methods
 72 
 73 /**
 74  * Returns a string representation of the object.
 75  * 
 76  * @return		{String}		a string representation of the object
 77  */
 78 ZmBriefcase.prototype.toString = 
 79 function() {
 80 	return "ZmBriefcase";
 81 };
 82 
 83 ZmBriefcase.prototype.getIcon = 
 84 function() {
 85 	if (this.nId == ZmOrganizer.ID_ROOT)	{ return null; }
 86 	if (this.link)							{ return "SharedMailFolder"; }
 87 	return "Folder";
 88 };
 89 
 90 ZmBriefcase.prototype.notifyModify =
 91 function(obj) {
 92 	ZmOrganizer.prototype.notifyModify.call(this, obj);
 93 
 94 	var doNotify = false;
 95 	var fields = {};
 96 	if (obj.name != null && this.name != obj.name && !obj._isRemote) {
 97 		this.name = obj.name;
 98 		fields[ZmOrganizer.F_NAME] = true;
 99 		doNotify = true;
100 	} else if (obj.color != null && this.color != obj.color && !obj._isRemote) {
101 		this.color = obj.color;
102 		fields[ZmOrganizer.F_COLOR] = true;
103 		doNotify = true;
104 	}
105 	
106 	if (doNotify) {
107 		this._notify(ZmEvent.E_MODIFY, {fields: fields});
108 	}
109 };
110 
111 // Static methods
112 
113 /**
114 * Checks the briefcase name for validity. Returns an error message if the
115 * name is invalid and null if the name is valid.
116 *
117 * @param {String}		name		a briefcase name
118 * @return	{String}	the name
119 */
120 ZmBriefcase.checkName =
121 function(name) {
122 	return ZmOrganizer.checkName(name);
123 };
124 
125 /**
126  * Returns true if the given object(s) may be placed in this folder.
127  *
128  * If the object is a folder, check that:
129  * <ul>
130  * <li>We are not the immediate parent of the folder</li>
131  * <li>We are not a child of the folder</li>
132  * <li>We are not Spam or Drafts</li>
133  * <li>We don't already have a child with the folder's name (unless we are in Trash)</li>
134  * <li>We are not moving a regular folder into a search folder</li>
135  * <li>We are not moving a search folder into the Folders container</li>
136  * <li>We are not moving a folder into itself</li>
137  * </ul>
138  *
139  * If the object is an item or a list or items, check that:
140  * <ul>
141  * <li>We are not the Folders container</li>
142  * <li>We are not a search folder</li>
143  * <li>The items aren't already in this folder</li>
144  * <li>A contact can only be moved to Trash</li>
145  * <li>A draft can be moved to Trash or Drafts</li>
146  * <li>Non-drafts cannot be moved to Drafts</li>
147  * </ul>
148  *
149  * @param {Object}	what		the object(s) to possibly move into this briefcase (item or organizer)
150  */
151 ZmBriefcase.prototype.mayContain =
152 function(what, targetFolderType) {
153 
154     if (!what) return true;
155 
156 	var invalid = false;
157     targetFolderType = targetFolderType || this.type;
158 
159     if (what instanceof ZmFolder) { //ZmBriefcase
160          invalid =(
161                     what.parent == this || this.isChildOf(what)
162                  || targetFolderType == ZmOrganizer.SEARCH || targetFolderType == ZmOrganizer.TAG
163                  || (!this.isInTrash() && this.hasChild(what.name))
164                  || (what.id == this.id)
165                  || (this.isRemote() && !this._remoteMoveOk(what))
166                  || (what.isRemote() && !this._remoteMoveOk(what))
167                  ||  this.disallowSubFolder
168                  );
169     } else { //ZmBriefcaseItem
170         var items = AjxUtil.toArray(what);
171 		var item = items[0];
172         if (item.type == ZmItem.BRIEFCASE_ITEM) {
173             invalid = this._checkInvalidFolderItems(items);
174 
175             if (!invalid) {
176                 for (var i = 0; i < items.length; i++) {
177                     if (items[i] instanceof ZmBriefcaseFolderItem && (items[i].id == this.id ||             // Can't move folder items to themselves
178                     		this.isChildOf(items[i].folder))) { // Can't move parent folder to child folder
179                         invalid = true;
180                         break;
181                     }
182                 }
183             }
184             
185             
186             // can't move items to folder they're already in; we're okay if
187             // we have one item from another folder
188             if (!invalid && item.folderId) {
189                 invalid = true;
190                 for (var i = 0; i < items.length; i++) {
191                     var tree = appCtxt.getById(items[i].folderId);
192                     if (tree != this) {
193                         invalid = false;
194                         break;
195                     }
196                 }
197             }
198         } else {
199             invalid = true;
200         }
201         
202         // attachments from mail can be moved inside briefcase
203 		if (item && item.msgId && item.partId) {
204 			invalid = false;
205 		}
206 
207     }
208 
209     if (!invalid && this.link) {
210         invalid = this.isReadOnly();
211     }	
212 
213 	return !invalid;
214 };
215 
216 ZmBriefcase.prototype._checkInvalidFolderItems =
217 function(items, targetFolderType) {
218   var invalid = false;
219   for (var i=0; i<items.length && !invalid; i++) {
220     if (items[i] instanceof ZmBriefcaseFolderItem) {
221         var item = items[i];
222         invalid = (
223            item.parent == this || this.isChildOf(item)
224         || targetFolderType == ZmOrganizer.SEARCH || targetFolderType == ZmOrganizer.TAG
225         || (!this.isInTrash() && this.hasChild(item.name))
226         || (item.id == this.id)
227         || (item.folder && item.folder.isRemote() && !this.isRemote() && !item.folder.rid)
228         || (item.folder && this.isRemote())
229         );
230     }
231   }
232     return invalid;
233 };
234 
235 ZmBriefcase.prototype.supportsPublicAccess =
236 function() {
237 	return true;
238 };
239 
240 ZmBriefcase.prototype.isShared =
241 function(){
242     return this.link ? true : false;  
243 };
244 
245 ZmBriefcase.prototype._generateRestUrl =
246 function() {
247 	var loc = document.location;
248 	var uname = this.getOwner();
249 	var host = loc.host;
250 	var m = uname.match(/^(.*)@(.*)$/);
251 
252 	host = (m && m[2]) || host;
253 
254 	// REVISIT: What about port? For now assume other host uses same port
255 	if (loc.port && loc.port != 80) {
256 		host = host + ":" + loc.port;
257 	}
258 
259 	var searchPath =  this.getSearchPath(true);
260 	var generatedRestURL = [loc.protocol, "//", host, "/service/user/", uname, "/", AjxStringUtil.urlEncode(searchPath)].join("");
261 	var restUrl = this.restUrl;
262 	var oname = this.oname;
263 	var parent = this.parent;
264 	//Get the restUrl and oname from remote share
265 	while (parent) {
266 		if (parent.restUrl) {
267 			restUrl = parent.restUrl;
268 		}
269 		if (parent.oname) {
270 			oname = parent.oname;
271 		}
272 		parent = parent.parent;
273 	}
274 
275 	if (restUrl) {
276 		var index = searchPath.indexOf(oname);  //remove oname from searchPath
277 		if (index != -1) {
278 			searchPath = searchPath.substring(index + oname.length);
279 		}
280 		generatedRestURL = restUrl +  AjxStringUtil.urlEncode(searchPath);
281 	}
282 	return generatedRestURL;
283 };
284