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