1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  * 
 27  */
 28 
 29 /**
 30  * Abstract class
 31  * @class
 32  * This class is a base class for briefcase item classes.
 33  * 
 34  * @param {int}			id			the unique id
 35  * @param {ZmList}		list		a list that contains this item
 36  * @param {Boolean}		noCache		if <code>true</code>, do not cache this item
 37 
 38  * @extends		ZmItem
 39  * 
 40  * @see		ZmBriefcaseBaseItem
 41  */
 42 ZmBriefcaseBaseItem = function(id, list, noCache, type) {
 43 
 44 	if (arguments.length == 0) { return; }
 45 	ZmItem.call(this, type, id, list, noCache);
 46 };
 47 
 48 ZmBriefcaseBaseItem.prototype = new ZmItem;
 49 ZmBriefcaseBaseItem.prototype.constructor = ZmBriefcaseBaseItem;
 50 
 51 // Constants
 52 ZmBriefcaseBaseItem.NAME_UPDATED = "nameUpdated";
 53 
 54 //Public methods
 55 
 56 /**
 57  * Gets the path.
 58  * 
 59  * @param	{Boolean}	dontIncludeThisName		if <code>true</code>, do not include this item name in the path
 60  * @return	{String}	the path
 61  */
 62 ZmBriefcaseBaseItem.prototype.getPath =
 63 function(dontIncludeThisName) {
 64 	var briefcase = appCtxt.getById(this.folderId);
 65 	var name = !dontIncludeThisName ? this.name : "";
 66 	return [briefcase.getPath(), "/", name].join("");
 67 };
 68                       
 69 /**
 70  * Gets the REST URL.
 71  * 
 72  * @param	{Boolean}	dontIncludeThisName		if <code>true</code>, do not include this item name in the path
 73  * @param	{Boolean}	ignoreCustomDocs		if <code>true</code>, ignore custom docs
 74  * @param   {Boolean}   includeVersion			if <code>true</code> include the version if exists (it's latest for the base item)
 75  * @return	{String}	the REST URL
 76  */
 77 ZmBriefcaseBaseItem.prototype.getRestUrl =
 78 function(dontIncludeThisName, ignoreCustomDocs, includeVersion) {
 79 	var url = ZmItem.prototype.getRestUrl.call(this);
 80 	if (dontIncludeThisName) {
 81 		url = url.replace(/[^\/]+$/,"");
 82 	}
 83 	if (includeVersion && this.version){
 84 		url = url + (url.match(/\?/) ? '&' : '?' ) + "ver=" + this.version;
 85 	}
 86 
 87 	return url;
 88 };
 89 
 90 /**
 91  * Checks if this item is a real file.
 92  * 
 93  * @return	{Boolean}	<code>true</code> if this item is a real file (not a web doc or folder)
 94  */
 95 ZmBriefcaseBaseItem.prototype.isRealFile =
 96 function() {
 97     return (!this.isFolder && !this.isWebDoc());  
 98 };
 99 
100 /**
101  * Checks if this item is a web doc.
102  * 
103  * @return	{Boolean}	<code>true</code> if this item is a web doc
104  */
105 ZmBriefcaseBaseItem.prototype.isWebDoc =
106 function() {
107     return (this.contentType == ZmMimeTable.APP_ZIMBRA_DOC);
108 };
109 
110 /**
111  * Checks if this item is an document which can only be downloaded and cannot be rendered by browser
112  *
113  * @return	{Boolean}	<code>true</code> if this item is downloadable
114  */
115 ZmBriefcaseBaseItem.prototype.isDownloadable =
116 function() {
117     return (!this.isWebDoc() && !ZmMimeTable.isRenderable(this.contentType) && !ZmMimeTable.isRenderableImage(this.contentType) && !ZmMimeTable.isTextType(this.contentType));
118 };
119 
120 /**
121  * Gets the content type.
122  * 
123  * @return	{String}	the content type
124  */
125 ZmBriefcaseBaseItem.prototype.getContentType =
126 function() {
127     return this.contentType;
128 };
129 
130 /**
131  * Gets the icon.
132  * 
133  * @param	{Boolean}	large		if <code>true</code>, return the large icon
134  * @return	{String}	the icon
135  */
136 ZmBriefcaseBaseItem.prototype.getIcon =
137 function(large) {
138 
139 	if (this.isFolder) {
140 		return "Folder";
141 	}
142 
143 	var ct = this.contentType, icon;
144 	if (ct && ct.match(/;/)) {
145 		ct = ct.split(";")[0];
146 	}
147 	var mimeInfo = ct ? ZmMimeTable.getInfo(ct) : null;
148 	if (large) {
149 		icon = mimeInfo ? mimeInfo.imageLarge : "UnknownDoc_48";
150 	} else {
151 		icon = mimeInfo ? mimeInfo.image : "UnknownDoc" ;
152 	}
153 
154 	return icon;
155 };
156 
157 /**
158  * Checks if this item is read only.
159  * 
160  * @return	{Boolean}	<code>true</code> if this item is read only
161  */
162 ZmBriefcaseBaseItem.prototype.isReadOnly =
163 function() {
164 	// if one of the ancestor is readonly then no chances of childs being writable
165 	var isReadOnly = false;
166 	var folder = appCtxt.getById(this.folderId);
167 	var rootId = ZmOrganizer.getSystemId(ZmOrganizer.ID_ROOT);
168 	while (folder && folder.parent && (folder.parent.id != rootId) && !folder.isReadOnly()) {
169 		folder = folder.parent;
170 	}
171 
172 	if (folder && folder.isReadOnly()) {
173 		isReadOnly = true;
174 	}
175 
176 	return isReadOnly;
177 };
178 
179 /**
180  * Gets the briefcase folder.
181  * 
182  * @return	{ZmBriefcase}	the folder
183  */
184 ZmBriefcaseBaseItem.prototype.getBriefcaseFolder =
185 function() {
186 	if (!this._briefcase) {
187 		var folder = appCtxt.getById(this.folderId);
188 		var rootId = ZmOrganizer.getSystemId(ZmOrganizer.ID_ROOT);
189 		while (folder && folder.parent && (folder.parent.id != rootId)) {
190 			folder = folder.parent;
191 		}
192 		this._briefcase = folder;
193 	}
194 	return this._briefcase;
195 };
196 
197 /**
198  * Checks if this item is shared.
199  * 
200  * @return	{Boolean}	<code>true</code> if this item is shared
201  */
202 ZmBriefcaseBaseItem.prototype.isShared =
203 function() {
204 	var briefcase = this.getBriefcaseFolder();
205 	return briefcase && briefcase.link;
206 };
207 
208 /**
209  * Creates an item from an attachment.
210  * 
211  * @param	{String}	msgId		the message
212  * @param	{String}	partId		the message part
213  * @param	{String}	name		the item name
214  * @param	{String}	folderId		the folder id
215  */
216 ZmBriefcaseBaseItem.prototype.createFromAttachment =
217 function(msgId, partId, name, folderId, attribs) {
218 
219     attribs = attribs || {};
220 
221     var acctId = appCtxt.getActiveAccount().id;
222 
223     var json = {
224         SaveDocumentRequest: {
225             _jsns: "urn:zimbraMail",
226 			doc: {
227                 m: {
228                     id: msgId,
229                     part: partId
230                 }
231             }
232         }
233     };
234 
235     var doc = json.SaveDocumentRequest.doc;
236     if (attribs.id && attribs.version) {
237         doc.id = attribs.id;
238         doc.ver = attribs.version;
239     }else{
240         doc.l = folderId;
241     }
242     if(attribs.rename){
243         doc.name = attribs.rename;
244     }
245     var params = {
246 		jsonObj: json,
247 		asyncMode: true,
248 		callback: (new AjxCallback(this, this._handleResponseCreateItem, [folderId, attribs.callback])),
249 		errorCallback: (new AjxCallback(this, this._handleErrorCreateItem, [attribs.errorCallback]))
250 	};
251     appCtxt.getAppController().sendRequest(params);
252 };
253 
254 ZmBriefcaseBaseItem.prototype.restoreVersion =
255 function(restoreVerion, callback){
256 
257     var json = {
258 		SaveDocumentRequest: {
259 			_jsns: "urn:zimbraMail",
260 			doc: {
261 				id:	this.id,
262                 ver: this.version,
263                 doc: {
264                     id: this.id,
265                     ver: restoreVerion
266                 }
267 			}
268 		}
269 	};
270 
271 	var params = {
272 		jsonObj:		json,
273 		asyncMode:		true,
274 		callback:		callback
275 	};
276 	return appCtxt.getAppController().sendRequest(params);
277     
278 };
279 
280 ZmBriefcaseBaseItem.prototype.deleteVersion =
281 function(version, callback, batchCmd){
282 
283     var json = {
284 		PurgeRevisionRequest: {
285 			_jsns: "urn:zimbraMail",
286 			revision: {
287 				id:	this.id,
288                 ver: version,
289                 includeOlderRevisions: false
290 			}
291 		}
292 	};
293 
294     if(batchCmd){
295         batchCmd.addRequestParams(json, callback);
296     }else{
297         var params = {
298             jsonObj:		json,
299             asyncMode:		true,
300             callback:		callback
301         };
302         return appCtxt.getAppController().sendRequest(params);
303     }
304 
305 };
306 
307 
308 ZmBriefcaseBaseItem.prototype._handleResponseCreateItem =
309 function(folderId, callback, response) {
310 	appCtxt.getAppController().setStatusMsg(ZmMsg.fileCreated);
311 	appCtxt.getChooseFolderDialog().popdown();
312     if(callback)
313         callback.run(response);
314 };
315 
316 ZmBriefcaseBaseItem.prototype._handleErrorCreateItem =
317 function(callback, ex) {
318 
319     var handled = false;
320 	if(callback){
321         handled = callback.run(ex);
322     }
323     appCtxt.getAppController().setStatusMsg(ZmMsg.errorCreateFile, ZmStatusView.LEVEL_CRITICAL);
324     return handled;
325 };
326 
327 ZmBriefcaseBaseItem.prototype.notifyModify =
328 function(obj, batchMode) {
329 
330 	var result = ZmItem.prototype.notifyModify.apply(this, arguments);
331 	if (result) {
332 		return result;
333 	}
334 
335     var modified = false, doNotify = true, fields=[];    
336     //Updating modified attributes
337 	var nameUpdated = false;
338 	if (obj.name && (obj.name !== this.name)) {
339 		nameUpdated = true;
340 	}
341     this.set(obj);
342 
343     if (doNotify) {
344 		var details = {fields: fields};
345 		details[ZmBriefcaseBaseItem.NAME_UPDATED] = nameUpdated;
346 		this._notify(ZmEvent.E_MODIFY, details);
347 	}
348 	
349 };
350 /**
351  * Gets the folder.
352  * 
353  * @return	{ZmFolder}		the folder
354  */
355 ZmBriefcaseBaseItem.prototype.getFolder =
356 function() {
357 	return appCtxt.getById(this.folderId);
358 };
359 
360 ZmBriefcaseBaseItem.prototype._loadFromDom =
361 function(node) {
362 
363 	this.id = node.id;
364 
365 	if (node.rest)	{ this.restUrl = node.rest; }
366 	if (node.l)		{ this.folderId = node.l; }
367 	if (node.name)	{ this.name = node.name; }
368 	if (node.cr)	{ this.creator = node.cr; }
369 
370 	if (node.cd)	{ this.createDate = new Date(Number(node.cd)); }
371 	if (node.md)	{ //node.md is seconds since epoch
372         var mdMilliSecs = Number(node.md)*1000;
373         this.modifyDate = new Date(mdMilliSecs);
374     }
375 	if (node.d)     { this.contentChangeDate = new Date(Number(node.d)); }
376 
377 	if (node.leb)	{ this.modifier = node.leb; }
378 	if (node.s || node.s == 0) //size can be 0
379                     { this.size = Number(node.s); }
380 	if (node.ver)	{ this.version = Number(node.ver) || 0; }
381 	if (node.ct)	{ this.contentType = node.ct.split(";")[0]; }
382 	if (node.tn)	{ this._parseTagNames(node.tn);	}
383     this.locked = false;
384     if (node.loid)    {
385         this.locked = true;
386         this.lockId = node.loid;
387         this.lockUser = node.loe;
388         this.lockTime = new Date(Number(node.lt));
389     }
390 
391     if (node.desc){  this.notes = AjxStringUtil.htmlEncode(node.desc); }
392     this.subject = this.getNotes();
393 
394     this._parseFlags(node.f);
395 
396 };
397 
398 /**
399  * Creates a briefcase item.
400  * @class
401  * This class represents a briefcase item.
402  * 
403  * @param {int}			id			the unique id
404  * @param {ZmList}		list		a list that contains this item
405  * @param {Boolean}		noCache		if <code>true</code>, do not cache this item
406 
407  * @extends		ZmBriefcaseBaseItem
408  * 
409  * @see		ZmBriefcase
410  */
411 ZmBriefcaseItem = function(id, list, noCache) {
412 
413 	if (arguments.length == 0) { return; }
414 	ZmBriefcaseBaseItem.call(this, id, list, noCache, ZmItem.BRIEFCASE_ITEM);
415 };
416 
417 ZmBriefcaseItem.prototype = new ZmBriefcaseBaseItem;
418 ZmBriefcaseItem.prototype.constructor = ZmBriefcaseItem;
419 
420 /**
421  * Returns a string representation of the object.
422  * 
423  * @return		{String}		a string representation of the object
424  */
425 ZmBriefcaseItem.prototype.toString =
426 function() {
427 	return "ZmBriefcaseItem";
428 };
429 
430 
431 // Static functions
432 /**
433  * Creates a briefcase item from the dom.
434  * 
435  * @param	{Object}	node		the node
436  * @param	{Hash}		args		a hash of arguments
437  * 
438  * @return	{ZmBriefcaseItem}	the briefcase item
439  */
440 ZmBriefcaseItem.createFromDom =
441 function(node, args) {
442 	var item = new ZmBriefcaseItem(node.id, args.list);
443 	item._loadFromDom(node);
444 	return item;
445 };
446 
447 ZmBriefcaseItem.getRevision =
448 function(itemId, version, callback, errorCallback, accountName) {
449 	var json = {
450 		ListDocumentRevisionsRequest: {
451 			_jsns: "urn:zimbraMail",
452 			doc: {
453 				id:	itemId,
454                 ver: version,   //verion=-1 for all versions of count
455                 count: 50       //parametrize count to allow pagination
456 			}
457 		}
458 	};
459 
460 	var params = {
461 		jsonObj:		json,
462 		asyncMode:		Boolean(callback),
463 		callback:		callback,
464 		errorCallback:	errorCallback,
465 		accountName:	accountName
466 	};
467 	return appCtxt.getAppController().sendRequest(params);
468 };
469 
470 ZmBriefcaseItem.lock =
471 function(itemId, callback, errorCallback, accountName) {
472 	var json = {
473 		ItemActionRequest: {
474 			_jsns: "urn:zimbraMail",
475 			action: {
476 				id:	itemId instanceof Array ? itemId.join() : itemId,
477 				op:	"lock"
478 			}
479 		}
480 	};
481 
482 	var params = {
483 		jsonObj:		json,
484 		asyncMode:		Boolean(callback),
485 		callback:		callback,
486 		errorCallback:	errorCallback,
487 		accountName:	accountName
488 	};
489 	return appCtxt.getAppController().sendRequest(params);
490 };
491 
492 
493 ZmBriefcaseItem.unlock =
494 function(itemId, callback, errorCallback, accountName) {
495 	var json = {
496 		ItemActionRequest: {
497 			_jsns: "urn:zimbraMail",
498 			action: {
499 				id:	itemId instanceof Array ? itemId.join() : itemId,
500 				op:	"unlock"
501 			}
502 		}
503 	};
504 
505 	var params = {
506 		jsonObj:		json,
507 		asyncMode:		Boolean(callback),
508 		callback:		callback,
509 		errorCallback:	errorCallback,
510 		accountName:	accountName
511 	};
512 	return appCtxt.getAppController().sendRequest(params);
513 };
514 	
515 
516 // Mendoza line
517 
518 ZmBriefcaseItem.prototype.getRevisions =
519 function(callback, errorCallback, accountName){
520 	ZmBriefcaseItem.getRevision(this.id, -1 ,callback, errorCallback, accountName);
521 };
522 
523 ZmBriefcaseItem.prototype.lock =
524 function(callback, errorCallback, accountName){
525 	ZmBriefcaseItem.lock(this.id, callback, errorCallback, accountName);  
526 };
527 
528 
529 ZmBriefcaseItem.prototype.unlock =
530 function(callback, errorCallback, accountName){
531 	ZmBriefcaseItem.unlock(this.id, callback, errorCallback, accountName);
532 };
533 
534 ZmBriefcaseItem.prototype.set =
535 function(data) {
536 
537 	this.id = data.id;
538 	if (data.rest) this.restUrl = data.rest;
539 	if (data.l)    this.folderId = data.l;
540 	if (data.name) this.name = data.name;
541 	if (data.cr)   this.creator = data.cr;
542 	if (data.cd)   this.createDate = new Date(Number(data.cd));
543 	if (data.md)	{ //node.md is seconds since epoch
544 		var mdMilliSecs = Number(data.md)*1000;
545 		this.modifyDate = new Date(mdMilliSecs);
546 	}
547 	if (data.d)    this.contentChangeDate = new Date(Number(data.d));
548 	if (data.leb)  this.modifier = data.leb;
549 	if (data.s)    this.size = Number(data.s);
550 	if (data.ver)  this.version = Number(data.ver);
551 	if (data.ct)   this.contentType = data.ct.split(";")[0];
552     if (data.tn)   this._parseTagNames(data.tn);
553     if (data.loid)    {
554         this.locked = true;
555         this.lockId = data.loid;
556         this.lockUser = data.loe;
557         this.lockTime = new Date(Number(data.lt));
558     } else if (data.loid===""){
559         //loid is not always set in response; set locked to false when value is blank
560         this.locked = false;
561     }
562 
563     if (data.desc)  this.notes = AjxStringUtil.htmlEncode(data.desc);
564     this.subject = this.getNotes();
565 };
566 
567 ZmBriefcaseItem.prototype.getNotes =
568 function(){
569     return AjxMessageFormat.format(ZmMsg.revisionNotes, [this.version, (this.notes || "")]);
570 };
571 
572 /**
573  * Gets the normalized item id by splitting it from a/c id if any associated
574  *
575  * @return	{Int}	normalized item id
576  */
577 ZmBriefcaseItem.prototype.getNormalizedItemId =
578 function(){
579     if(!this.getBriefcaseFolder().isShared()){return this.id;}
580     return AjxStringUtil.split(this.id,':')[1];
581 };
582 
583 ZmBriefcaseFolderItem = function(folder) {
584 
585 	ZmBriefcaseItem.call(this, folder.id, null, true);
586 
587 	this.name = folder.name;
588 	this.folderId = folder.parent && folder.parent.id;
589 	this.isFolder = true;
590 	this.folder = folder;
591     this.size = folder.sizeTotal;
592     this.creator = folder.getOwner();
593 
594 	this._data = {};
595 };
596 
597 ZmBriefcaseFolderItem.prototype = new ZmBriefcaseItem;
598 ZmBriefcaseFolderItem.prototype.constructor = ZmBriefcaseFolderItem;
599 
600 ZmBriefcaseFolderItem.prototype.toString =
601 function() {
602 	return "ZmBriefcaseFolderItem";
603 };
604 
605 ZmBriefcaseFolderItem.prototype.getData =
606 function(key) {
607 	return this._data[key];
608 };
609 
610 ZmBriefcaseFolderItem.prototype.setData =
611 function(key, value) {
612   this._data[key] = value;
613 };
614 
615 ZmBriefcaseFolderItem.prototype.getIcon =
616 function(baseIcon, large){
617     if(baseIcon)
618         return ZmBriefcaseBaseItem.prototype.getIcon.call(this, true);
619     else
620         return this.folder.getIconWithColor();  
621 };
622 
623 ZmBriefcaseFolderItem.prototype.getOwner =
624 function(){
625     return this.folder.getOwner();
626 };
627 
628 //ZmRevisionItem
629 ZmRevisionItem = function(id, parentItem){
630     if(arguments.length == 0) return;
631     this.parent = parentItem;
632     this.isRevision = true;
633     this.id = id;
634     ZmBriefcaseBaseItem.call(this, id, null, false, ZmItem.BRIEFCASE_REVISION_ITEM);
635 };
636 
637 ZmRevisionItem.prototype = new ZmBriefcaseBaseItem;
638 ZmRevisionItem.prototype.constructor = ZmRevisionItem;
639 
640 ZmRevisionItem.prototype.toString = function() {
641 	return "ZmRevisionItem"; 
642 }
643 ZmRevisionItem.prototype.set =
644 function(data){
645 
646     //Props
647     //this.id =       this.id || data.id;
648     this.version =  data.ver;
649     if (data.name)  this.name = data.name;
650     if (data.l)     this.folderId = data.l;
651     if (data.ct)    this.contentType = data.ct.split(";")[0];
652     if (data.s)     this.size = Number(data.s);
653 
654     //Data
655     if (data.cr)    this.creator = data.cr;
656     if (data.cd)    this.createDate = new Date(Number(data.cd));
657     if (data.leb)   this.modifier = data.leb;
658 	if (data.md)	{ //node.md is seconds since epoch
659 		var mdMilliSecs = Number(data.md)*1000;
660 		this.modifyDate = new Date(mdMilliSecs);
661 	}
662 	if (data.d)     this.contentChangeDate = new Date(Number(data.d));
663 	if (data.desc)  this.notes = AjxStringUtil.htmlEncode(data.desc);
664 
665     this.subject = this.getNotes();
666 	if (data.tn) { this._parseTagNames(data.tn); }
667 
668 };
669 
670 ZmRevisionItem.prototype.getNotes =
671 function(){
672     return ((this.notes)?AjxMessageFormat.format(ZmMsg.revisionNotes, [this.version, this.notes]):AjxMessageFormat.format(ZmMsg.revisionWithoutNotes, [this.version]));
673 };
674 
675 ZmRevisionItem.prototype.getRestUrl =
676 function(){
677     var restUrl = this.parent.getRestUrl();
678     if(this.version){
679         restUrl = restUrl + ( restUrl.match(/\?/) ? '&' : '?' ) + "ver="+this.version;
680     }
681     return restUrl;
682 };
683 
684 ZmRevisionItem.prototype.getIcon =
685 function(){
686    return null; 
687 };
688 
689 /**
690  * Rename the item.
691  *
692  * @param	{String}	newName
693  * @param	{AjxCallback}	callback		the callback
694  * @param	{AjxCallback}	errorCallback	the callback on error
695  * @return	{Object}		the result of the move
696  */
697 ZmRevisionItem.prototype.rename =
698 function(newName, callback, errorCallback) {
699 	return ZmItem.rename(this.parent.id, newName, callback, errorCallback);
700 };
701