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 contains the Zimlet context class. 27 */ 28 29 /** 30 * Creates the Zimlet context. 31 * @class 32 * This class represents the Zimlet context. 33 * 34 * @param {String} id the id 35 * @param {ZmZimletBase} zimlet the Zimlet 36 * 37 */ 38 ZmZimletContext = function(id, zimlet) { 39 40 // sanitize JSON here 41 this.json = ZmZimletContext.sanitize(zimlet, "zimlet", ZmZimletContext.RE_ARRAY_ELEMENTS); 42 43 this.id = id; 44 this.icon = "ZimbraIcon"; 45 this.ctxt = zimlet.zimletContext; 46 this.config = zimlet.zimletConfig; 47 zimlet = zimlet.zimlet[0]; 48 /** 49 * The zimlet name. 50 * @type String 51 */ 52 this.name = zimlet.name; 53 this._url = this.ctxt[0].baseUrl; 54 this.priority = this.ctxt[0].priority; 55 /** 56 * The zimlet description. 57 * @type String 58 */ 59 this.description = zimlet.description; 60 /** 61 * The zimlet version. 62 * @type String 63 */ 64 this.version = zimlet.version; 65 this.label = zimlet.label; 66 this.includes = this.json.zimlet.include || []; 67 this.includes.push([appContextPath, "/messages/", this.name, ".js?v=", cacheKillerVersion].join("")); 68 this.includeCSS = this.json.zimlet.includeCSS; 69 70 if (zimlet.serverExtension && zimlet.serverExtension[0].hasKeyword) { 71 this.keyword = zimlet.serverExtension[0].hasKeyword; 72 } 73 74 DBG.println(AjxDebug.DBG2, "Zimlets - context: " + this.name); 75 76 this.targets = {}; 77 var targets = (zimlet.target || "main").split(" "); 78 for (var i = 0; i < targets.length; i++) { 79 this.targets[targets[i]] = true; 80 } 81 82 this._contentActionMenu = null; 83 if (zimlet.contentObject) { 84 this.contentObject = zimlet.contentObject[0]; 85 if (this.contentObject.type) { 86 this.type = this.contentObject.type; 87 } 88 if (this.contentObject.contextMenu) { 89 if (this.contentObject.contextMenu instanceof Array) { 90 this.contentObject.contextMenu = this.contentObject.contextMenu[0]; 91 } 92 this._contentActionMenu = new AjxCallback(this, this._makeMenu,[this.contentObject.contextMenu.menuItem]); 93 } 94 } 95 96 this._panelActionMenu = null; 97 if (zimlet.zimletPanelItem && !appCtxt.isChildWindow) { 98 this.zimletPanelItem = zimlet.zimletPanelItem[0]; 99 if (this.zimletPanelItem.label) { 100 this.zimletPanelItem.label = this.process(this.zimletPanelItem.label); 101 } 102 if (this.zimletPanelItem.toolTipText && this.zimletPanelItem.toolTipText[0]) { 103 this.zimletPanelItem.toolTipText = this.process(this.zimletPanelItem.toolTipText[0]._content); 104 } 105 if (this.zimletPanelItem.icon) { 106 this.icon = this.zimletPanelItem.icon; 107 } 108 if (this.zimletPanelItem.contextMenu) { 109 if (this.zimletPanelItem.contextMenu instanceof Array) { 110 this.zimletPanelItem.contextMenu = this.zimletPanelItem.contextMenu[0]; 111 } 112 this._panelActionMenu = new AjxCallback(this, this._makeMenu, [this.zimletPanelItem.contextMenu.menuItem]); 113 } 114 if (this.zimletPanelItem.onClick instanceof Array) { 115 this.zimletPanelItem.onClick = this.zimletPanelItem.onClick[0]; 116 } 117 if (this.zimletPanelItem.onDoubleClick instanceof Array) { 118 this.zimletPanelItem.onDoubleClick = this.zimletPanelItem.onDoubleClick[0]; 119 } 120 } 121 122 if (zimlet.handlerObject) { 123 this.handlerObject = zimlet.handlerObject[0]._content; 124 } 125 126 var portlet = zimlet.portlet && zimlet.portlet[0]; 127 if (portlet) { 128 portlet = ZmZimletContext.sanitize(portlet); 129 portlet.portletProperties = (portlet.portletProperties && portlet.portletProperties.property) || {}; 130 this.portlet = portlet; 131 } 132 133 this.userProperties = zimlet.userProperties ? zimlet.userProperties[0] : []; 134 this._propsById = {}; 135 if (zimlet.userProperties) { 136 this._translateUserProp(); 137 } 138 139 if (this.config) { 140 if (this.config instanceof Array || 141 (appCtxt.isChildWindow && this.config.length && this.config[0])) { 142 143 this.config = this.config[0]; 144 } 145 this._translateConfig(); 146 } 147 148 this._handleMenuItemSelected = new AjxListener(this, this._handleMenuItemSelected); 149 }; 150 151 ZmZimletContext.prototype.constructor = ZmZimletContext; 152 153 154 // 155 // Consts 156 // 157 ZmZimletContext.RE_ARRAY_ELEMENTS = /^(dragSource|include|includeCSS|menuItem|param|property|resource|portlet)$/; 158 ZmZimletContext.APP = { 159 contextPath: appContextPath, 160 currentSkin: appCurrentSkin 161 }; 162 163 // NOTE: I have no idea why these regexes start with (^|[^\\]). But 164 // since they have always been public, I can't change them now. 165 ZmZimletContext.RE_SCAN_APP = /(^|[^\\])\$\{app\.([\$a-zA-Z0-9_]+)\}/g; 166 ZmZimletContext.RE_SCAN_OBJ = /(^|[^\\])\$\{(?:obj|src)\.([\$a-zA-Z0-9_]+)\}/g; 167 ZmZimletContext.RE_SCAN_PROP = /(^|[^\\])\$\{prop\.([\$a-zA-Z0-9_]+)\}/g; 168 ZmZimletContext.RE_SCAN_MSG = /(^|[^\\])\$\{msg\.([\$a-zA-Z0-9_]+)\}/g; 169 170 ZmZimletContext.__RE_SCAN_SETTING = /\$\{setting\.([\$a-zA-Z0-9_]+)\}/g; 171 172 /** 173 * This function creates a 'sane' JSON object, given one returned by the 174 * Zimbra server. 175 *<p> 176 * It will basically remove unnecessary arrays and create String objects for 177 * those tags that have text data, so that we don't need to dereference lots of 178 * arrays and use _content. It does the job that the server should do. *grin* 179 * </p> 180 * <b>WARNING</b>: usage of an attribute named "length" may give sporadic 181 * results, since we convert tags that have text content to Strings. 182 * 183 * @param obj -- array or object, whatever was given by server 184 * @param tag -- the tag of this object, if it's an array 185 * @param wantarray_re -- RegExp that matches tags that must remain an array 186 * 187 * @return -- sanitized object 188 * 189 * @private 190 */ 191 ZmZimletContext.sanitize = 192 function(obj, tag, wantarray_re) { 193 function doit(obj, tag) { 194 var cool_json, val, i; 195 if (obj instanceof DwtControl) { //Don't recurse into DwtControls, causes too much recursion 196 return obj; 197 } 198 else if (obj instanceof Array || AjxUtil.isArray1(obj)) { 199 if (obj.length == 1 && !(wantarray_re && wantarray_re.test(tag))) { 200 cool_json = doit(obj[0], tag); 201 } else { 202 cool_json = []; 203 for (i = 0; i < obj.length; ++i) { 204 cool_json[i] = doit(obj[i], tag); 205 } 206 } 207 } 208 else if (obj && typeof obj == "object") { 209 if (obj._content) { 210 cool_json = new String(obj._content); 211 } else { 212 cool_json = {}; 213 } 214 for (i in obj) { 215 cool_json[i] = doit(obj[i], i); 216 } 217 } else { 218 cool_json = obj; 219 } 220 return cool_json; 221 } 222 return doit(obj, tag); 223 }; 224 225 /** 226 * Returns a string representation of the object. 227 * 228 * @return {String} a string representation of the object 229 */ 230 ZmZimletContext.prototype.toString = 231 function() { 232 return "ZmZimletContext - " + this.name; 233 }; 234 235 /** 236 * <strong>Note:</strong> 237 * This method is called by ZmZimletMgr#_finished_loadIncludes. 238 * 239 * @private 240 */ 241 ZmZimletContext.prototype._finished_loadIncludes = 242 function() { 243 // localize messages 244 this.label = this.label && this.processMessage(this.label); 245 this.description = this.description && this.processMessage(this.description); 246 247 var CTOR = this.handlerObject ? window[this.handlerObject] : ZmZimletBase; 248 if (!CTOR) { 249 DBG.println("zimlet handler not defined ("+this.handlerObject+")"); 250 return; 251 } 252 this.handlerObject = new CTOR(); 253 if (!this.handlerObject._init) { 254 var msg = [ 255 "ERROR - Zimlet handler (", 256 this.name, 257 ") not defined. ", 258 "Make sure the Zimlet name and handlerObject defined in ", 259 this.name, 260 ".xml are different." 261 ].join(""); 262 DBG.println(AjxDebug.DBG1, msg); 263 } 264 this.handlerObject._init(this, DwtShell.getShell(window)); 265 if (this.contentObject) { 266 appCtxt.getZimletMgr().registerContentZimlet(this.handlerObject, this.type, this.priority); 267 } 268 this.handlerObject.init(); 269 this.handlerObject._zimletContext = this; 270 // If it has an _id then we need to make sure the treeItem is up-to-date now 271 // that the i18n files have loaded. 272 if (this._id) { 273 var acct = appCtxt.isOffline ? appCtxt.accountList.mainAccount : null; 274 var tree = appCtxt.getZimletTree(acct); 275 if (tree) { 276 var zimletItem = tree.getById(this._id); 277 zimletItem.resetNames(); 278 } 279 } 280 281 // initialize portlets 282 if (appCtxt.get(ZmSetting.PORTAL_ENABLED) && !appCtxt.isChildWindow) { 283 var params = { 284 name: "Portal", 285 callback: new AjxCallback(this, this._finished_loadIncludes2) 286 }; 287 DBG.println("------------------- REQUIRING Portal (ZmZimletContext)"); 288 AjxPackage.require(params); 289 } 290 291 DBG.println(AjxDebug.DBG2, "Zimlets - init() complete: " + this.name); 292 }; 293 294 /** 295 * @private 296 */ 297 ZmZimletContext.prototype._finished_loadIncludes2 = 298 function() { 299 appCtxt.getApp(ZmApp.PORTAL).getPortletMgr().zimletLoaded(this); 300 }; 301 302 /** 303 * Gets the organizer. 304 * 305 * @return {ZmOrganizer} the organizer 306 */ 307 ZmZimletContext.prototype.getOrganizer = 308 function() { 309 // this._organizer is a ZmZimlet and is set in ZmZimlet.createFromJs 310 return this._organizer; 311 }; 312 313 /** 314 * Gets the URL. 315 * 316 * @return {String} the URL 317 */ 318 ZmZimletContext.prototype.getUrl = 319 function() { 320 return this._url; 321 }; 322 323 /** 324 * Gets the value. 325 * 326 * @param {String} key the key 327 * @return {Object} the value 328 */ 329 ZmZimletContext.prototype.getVal = 330 function(key) { 331 var ret = this.json.zimlet; 332 var keyParts = key.split('.'); 333 for (var i = 0; i < keyParts.length; i++) { 334 ret = ret[keyParts[i]]; 335 } 336 return ret; 337 }; 338 339 /** 340 * Calls the handler. 341 * 342 * @param {String} funcname the function 343 * @param {Hash} args the arguments 344 * @return {Object} the results or <code>null</code> for none 345 * 346 * @private 347 */ 348 ZmZimletContext.prototype.callHandler = 349 function(funcname, args) { 350 if (this.handlerObject) { 351 var f = this.handlerObject[funcname]; 352 if (typeof f == "function") { 353 if (typeof args == "undefined") { 354 args = []; 355 } 356 else if (!(args instanceof Array)) { 357 args = [args]; 358 } 359 return f.apply(this.handlerObject, args); 360 } 361 } 362 return null; 363 }; 364 365 /** 366 * @private 367 */ 368 ZmZimletContext.prototype._translateUserProp = 369 function() { 370 var a = this.userProperties = this.userProperties.property; 371 for (var i = 0; i < a.length; ++i) { 372 this._propsById[a[i].name] = a[i]; 373 } 374 }; 375 376 /** 377 * Sets the property. 378 * 379 * @param {String} name the name 380 * @param {Object} val the value 381 * 382 */ 383 ZmZimletContext.prototype.setPropValue = 384 function(name, val) { 385 if (!this._propsById[name]) { 386 var prop = { name: name }; 387 this.userProperties.push(prop); 388 this._propsById[name] = prop; 389 } 390 this._propsById[name].value = val; 391 }; 392 393 /** 394 * Gets the property. 395 * 396 * @param {String} name the name 397 * @return {Object} value 398 */ 399 ZmZimletContext.prototype.getPropValue = 400 function(name) { 401 return this._propsById[name] && this._propsById[name].value; 402 }; 403 404 /** 405 * Gets the property. 406 * 407 * @param {String} name the name 408 * @return {Object} the property 409 */ 410 ZmZimletContext.prototype.getProp = 411 function(name) { 412 return this._propsById[name]; 413 }; 414 415 /** 416 * @private 417 */ 418 ZmZimletContext.prototype._translateConfig = 419 function() { 420 if (!this.config) { return; } 421 422 if (this.config.global && this.config.global[0]) { 423 var prop = this.config.global[0].property; 424 this.config.global = {}; 425 for (var i in prop) { 426 this.config.global[prop[i].name] = prop[i]._content; 427 } 428 } 429 if (this.config.local && this.config.local[0]) { 430 var propLocal = this.config.local[0].property; 431 this.config.local = {}; 432 for (var j in propLocal) { 433 this.config.local[propLocal[j].name] = propLocal[j]._content; 434 } 435 } 436 }; 437 438 /** 439 * Gets the configuration value. 440 * 441 * @param {String} name the config key name 442 * @return {Object} the config value or <code>null</code> if not set 443 */ 444 ZmZimletContext.prototype.getConfig = 445 function(name) { 446 447 var config = (this.config && this.config.length && this.config[0]) ? this.config[0] : this.config; 448 if (!config) { return; } 449 450 if (config.local && config.local[name]) { 451 return config.local[name]; 452 } 453 454 if (config.global && config.global[name]) { 455 return config.global[name]; 456 } 457 458 return null; 459 }; 460 461 /** 462 * Gets the panel action menu. 463 * 464 * @return {ZmActionMenu} the menu 465 */ 466 ZmZimletContext.prototype.getPanelActionMenu = 467 function() { 468 if (this._panelActionMenu instanceof AjxCallback) { 469 this._panelActionMenu = this._panelActionMenu.run(); 470 } 471 return this._panelActionMenu; 472 }; 473 474 /** 475 * Sets the panel action menu. 476 * 477 * @param {ZmActionMenu} menu the menu 478 */ 479 ZmZimletContext.prototype.setPanelActionMenu = 480 function(menu) { 481 if (menu == null || (menu instanceof ZmActionMenu) == false) 482 return; 483 484 var items = menu.getMenuItems(); 485 for (menuId in items) { 486 var item = items[menuId]; 487 if (item.id != null || item.id != "") 488 item.addSelectionListener(this._handleMenuItemSelected); 489 } 490 491 this._panelActionMenu = menu; 492 }; 493 494 /** 495 * @private 496 */ 497 ZmZimletContext.prototype._makeMenu = 498 function(obj) { 499 var menu = new ZmActionMenu({parent:DwtShell.getShell(window), menuItems:ZmOperation.NONE}); 500 for (var i = 0; i < obj.length; ++i) { 501 var data = obj[i]; 502 if (!data.id) { 503 menu.createSeparator(); 504 } else { 505 var params = {image:data.icon, text:this.process(data.label),disImage:data.disabledIcon}; 506 var item = menu.createMenuItem(data.id, params); 507 item.setData("xmlMenuItem", data); 508 item.addSelectionListener(this._handleMenuItemSelected); 509 if (data.menuItem) { 510 item.setMenu(this._makeMenu(data.menuItem)); 511 } 512 } 513 } 514 return menu; 515 }; 516 517 /** 518 * @private 519 */ 520 ZmZimletContext.prototype._handleMenuItemSelected = 521 function(ev) { 522 var data = ev.item.getData("xmlMenuItem"); 523 if (data.actionUrl) { 524 this.handleActionUrl(data.actionUrl[0], data.canvas); 525 } else { 526 this.callHandler("menuItemSelected", [data.id, data, ev]); 527 } 528 }; 529 530 /** 531 * @private 532 */ 533 ZmZimletContext.prototype.process = 534 function(str, obj, props) { 535 if (obj) { 536 str = this.processString(str, obj); 537 } 538 str = this.processMessage(str); 539 str = this.replaceObj(ZmZimletContext.RE_SCAN_PROP, str, props || this._propsById); 540 str = this.replaceObj(ZmZimletContext.RE_SCAN_APP, str, ZmZimletContext.APP); 541 str = str.replace(ZmZimletContext.__RE_SCAN_SETTING, ZmZimletContext.__replaceSetting); 542 return str; 543 }; 544 545 /** 546 * @private 547 */ 548 ZmZimletContext.prototype.processString = 549 function(str, obj) { 550 return this.replaceObj(ZmZimletContext.RE_SCAN_OBJ, str, obj); 551 }; 552 553 /** 554 * @private 555 */ 556 ZmZimletContext.processMessage = function(name, str) { 557 // i18n files load async so if not defined skip translation 558 if (!window[name]) { 559 DBG.println(AjxDebug.DBG2, "processMessage no messages: " + str); 560 return str; 561 } 562 var props = window[name]; 563 return ZmZimletContext.replaceObj(ZmZimletContext.RE_SCAN_MSG, str, props); 564 }; 565 566 /** 567 * @private 568 */ 569 ZmZimletContext.replaceObj = function(re, str, obj) { 570 return String(str).replace(re, 571 function(str, p1, prop) { 572 var txt = p1; 573 if (obj instanceof Array && obj.length > 1) { 574 for(var i=0; i < obj.length; i++) { 575 if(txt) {txt += ",";} 576 var o = obj[i]; 577 if (o[prop] instanceof Object) { 578 txt += o[prop].value; // user prop 579 } else { 580 txt += o[prop]; // string 581 } 582 } 583 } else { 584 if (typeof obj[prop] != "undefined") { 585 if (obj[prop] instanceof Object) { 586 txt += obj[prop].value; // user prop 587 } else { 588 txt += obj[prop]; // string 589 } 590 } else { 591 txt += "(UNDEFINED - str '" + str + "' obj '" + obj + "')"; 592 } 593 } 594 return txt; 595 }); 596 }; 597 598 /** 599 * Kept for backwards compatibility. 600 * @private 601 */ 602 ZmZimletContext.prototype.processMessage = function(str) { 603 return ZmZimletContext.processMessage(this.name, str); 604 }; 605 606 /** 607 * Kept for backwards compatibility. 608 * @private 609 */ 610 ZmZimletContext.prototype.replaceObj = ZmZimletContext.replaceObj; 611 612 /** 613 * @private 614 */ 615 ZmZimletContext.__replaceSetting = 616 function($0, name) { 617 return appCtxt.get(name); 618 }; 619 620 /** 621 * @private 622 */ 623 ZmZimletContext.prototype.makeURL = 624 function(actionUrl, obj, props) { 625 // All URL's to have REST substitutions 626 var url = this.process(actionUrl.target, obj, props); 627 var param = []; 628 if (actionUrl.param) { 629 var a = actionUrl.param; 630 for (var i = 0; i < a.length; ++i) { 631 // trim whitespace as it's almost certain that the 632 // developer didn't intend it. 633 var val = AjxStringUtil.trim(a[i]._content || a[i]); 634 val = this.process(val, obj, props); 635 param.push([ AjxStringUtil.urlEncode(a[i].name), 636 "=", 637 AjxStringUtil.urlEncode(val) ].join("")); 638 } 639 var startChar = actionUrl.paramStart || '?'; 640 var joinChar = actionUrl.paramJoin || '&'; 641 url = [ url, startChar, param.join(joinChar) ].join(""); 642 } 643 return url; 644 }; 645 646 /** 647 * If there already is a paintable canvas to use, as in the case of tooltip, 648 * pass it to 'div' parameter. otherwise a canvas (window, popup, dialog) will 649 * be created to display the contents from the url. 650 * 651 * @param actionUrl 652 * @param canvas 653 * @param obj 654 * @param div 655 * @param x 656 * @param y 657 * 658 * @private 659 */ 660 ZmZimletContext.prototype.handleActionUrl = 661 function(actionUrl, canvas, obj, div, x, y) { 662 var url = this.makeURL(actionUrl, obj); 663 var xslt = actionUrl.xslt && this.getXslt(actionUrl.xslt); 664 665 // need to use callback if the paintable canvas already exists, or if it 666 // needs xslt transformation. 667 if (div || xslt) { 668 if (!div) { 669 canvas = this.handlerObject.makeCanvas(canvas, null, x, y); 670 div = document.getElementById("zimletCanvasDiv"); 671 } 672 url = ZmZimletBase.PROXY + AjxStringUtil.urlComponentEncode(url); 673 AjxRpc.invoke(null, url, null, new AjxCallback(this, this._rpcCallback, [xslt, div]), true); 674 } else { 675 this.handlerObject.makeCanvas(canvas, url, x, y); 676 } 677 }; 678 679 /** 680 * @private 681 */ 682 ZmZimletContext._translateZMObject = 683 function(obj) { 684 // XXX Assumes all dragged objects are of the same type 685 var type = obj[0] ? obj[0].toString() : obj.toString(); 686 return (ZmZimletContext._zmObjectTransformers[type]) 687 ? ZmZimletContext._zmObjectTransformers[type](obj) : obj; 688 }; 689 690 /** 691 * @private 692 */ 693 ZmZimletContext._zmObjectTransformers = { 694 695 "ZmMailMsg" : function(o) { 696 var all = []; 697 o = (o instanceof Array) ? o : [o]; 698 for (var i = 0; i < o.length; i++) { 699 var ret = { TYPE: "ZmMailMsg" }; 700 var oi = o[i]; 701 ret.id = oi.id; 702 ret.convId = oi.cid; 703 ret.from = oi.getAddresses(AjxEmailAddress.FROM).getArray(); 704 ret.to = oi.getAddresses(AjxEmailAddress.TO).getArray(); 705 ret.cc = oi.getAddresses(AjxEmailAddress.CC).getArray(); 706 ret.subject = oi.subject; 707 ret.date = oi.date; 708 ret.size = oi.size; 709 ret.fragment = oi.fragment; 710 ret.tags = oi.tags; 711 ret.unread = oi.isUnread; 712 ret.attachment = oi.attachments.length > 0; 713 ret.attlinks = oi._attLinks || oi.getAttachmentLinks(); 714 ret.sent = oi.isSent; 715 ret.replied = oi.isReplied; 716 ret.draft = oi.isDraft; 717 ret.body = ZmZimletContext._getMsgBody(oi); 718 ret.srcObj = oi; 719 all[i] = ret; 720 } 721 if (all.length == 1) { 722 return all[0]; 723 } else { 724 all["TYPE"] = "ZmMailMsg"; 725 return all; 726 } 727 }, 728 729 "ZmConv" : function(o) { 730 var all = []; 731 o = (o instanceof Array) ? o : [o]; 732 for (var i = 0; i < o.length; i++) { 733 var oi = o[i]; 734 var ret = { TYPE: "ZmConv" }; 735 ret.id = oi.id; 736 ret.subject = oi.subject; 737 ret.date = oi.date; 738 ret.fragment = oi.fragment; 739 ret.participants = oi.participants.getArray(); 740 ret.numMsgs = oi.numMsgs; 741 ret.tags = oi.tags; 742 ret.unread = oi.isUnread; 743 ret.body = ZmZimletContext._getMsgBody(oi.getFirstHotMsg()); 744 ret.srcObj = oi; 745 all[i] = ret; 746 } 747 if (all.length == 1) { 748 return all[0]; 749 } else { 750 all["TYPE"] = "ZmConv"; 751 return all; 752 } 753 }, 754 755 ZmContact_fields : function() { 756 return [ 757 ZmContact.F_assistantPhone, 758 ZmContact.F_callbackPhone, 759 ZmContact.F_carPhone, 760 ZmContact.F_company, 761 ZmContact.F_companyPhone, 762 ZmContact.F_email, 763 ZmContact.F_email2, 764 ZmContact.F_email3, 765 ZmContact.F_fileAs, 766 ZmContact.F_firstName, 767 ZmContact.F_homeCity, 768 ZmContact.F_homeCountry, 769 ZmContact.F_homeFax, 770 ZmContact.F_homePhone, 771 ZmContact.F_homePhone2, 772 ZmContact.F_homePostalCode, 773 ZmContact.F_homeState, 774 ZmContact.F_homeStreet, 775 ZmContact.F_homeURL, 776 ZmContact.F_jobTitle, 777 ZmContact.F_lastName, 778 ZmContact.F_middleName, 779 ZmContact.F_mobilePhone, 780 ZmContact.F_namePrefix, 781 ZmContact.F_nameSuffix, 782 ZmContact.F_notes, 783 ZmContact.F_otherCity, 784 ZmContact.F_otherCountry, 785 ZmContact.F_otherFax, 786 ZmContact.F_otherPhone, 787 ZmContact.F_otherPostalCode, 788 ZmContact.F_otherState, 789 ZmContact.F_otherStreet, 790 ZmContact.F_otherURL, 791 ZmContact.F_pager, 792 ZmContact.F_workCity, 793 ZmContact.F_workCountry, 794 ZmContact.F_workFax, 795 ZmContact.F_workPhone, 796 ZmContact.F_workPhone2, 797 ZmContact.F_workPostalCode, 798 ZmContact.F_workState, 799 ZmContact.F_workStreet, 800 ZmContact.F_workURL 801 ]; 802 }, 803 804 "ZmContact" : function(o) { 805 o = (o instanceof Array) ? o : [o]; 806 var all = new Array(); 807 for (var i = 0; i < o.length; i++) { 808 var ret = { TYPE: "ZmContact" }; 809 var a = this.ZmContact_fields; 810 if (typeof a == "function") { 811 a = this.ZmContact_fields = a(); 812 } 813 var attr = o[i].getAttrs(); 814 for (var j = 0; j < a.length; ++j) { 815 ret[a[j]] = attr[a[j]]; 816 } 817 ret.id = o[i].id; 818 all[i] = ret; 819 } 820 if (all.length == 1) { 821 return all[0]; 822 } else { 823 all["TYPE"] = "ZmContact"; 824 return all; 825 } 826 }, 827 828 "ZmFolder" : function(o) { 829 var oi = o[0] ? o[0] : o; 830 var ret = { TYPE: "ZmFolder" }; 831 ret.id = oi.id; 832 ret.name = oi.getName(); 833 ret.path = oi.getPath(); 834 ret.isInTrash = oi.isInTrash(); 835 ret.unread = oi.numUnread; 836 ret.total = oi.numTotal; 837 ret.url = oi.getRestUrl(); 838 ret.srcObj = oi; 839 return ret; 840 }, 841 842 "ZmAppt" : function(o) { 843 var oi = o[0] ? o[0] : o; 844 oi.getDetails(); 845 var ret = { TYPE: "ZmAppt" }; 846 ret.id = oi.id; 847 ret.uid = oi.uid; 848 ret.subject = oi.getName(); 849 ret.startDate = oi.startDate; 850 ret.endDate = oi.endDate; 851 ret.allDayEvent = oi.isAllDayEvent(); 852 ret.exception = oi.isException; 853 ret.alarm = oi.alarm; 854 ret.otherAttendees = oi.otherAttendees; 855 ret.attendees = oi.getAttendeesText(ZmCalBaseItem.PERSON); 856 ret.resources = oi.getAttendeesText(ZmCalBaseItem.EQUIPMENT); 857 ret.location = oi.getLocation(); 858 ret.notes = oi.getNotesPart(); 859 ret.isRecurring = oi.isRecurring(); 860 ret.timeZone = oi.timezone; 861 ret.srcObj = oi; 862 return ret; 863 } 864 }; 865 866 /** 867 * Gets the xslt. 868 * 869 * @param {String} url the URL 870 * @return {AjxXslt} the xslt 871 */ 872 ZmZimletContext.prototype.getXslt = 873 function(url) { 874 if (!this._xslt) { 875 this._xslt = {}; 876 } 877 var realurl = this.getUrl() + url; 878 if (!this._xslt[realurl]) { 879 this._xslt[realurl] = AjxXslt.createFromUrl(realurl); 880 } 881 return this._xslt[realurl]; 882 }; 883 884 /** 885 * @private 886 */ 887 ZmZimletContext.prototype._rpcCallback = 888 function(xslt, canvas, result) { 889 var html, resp = result.xml; 890 if (!resp) { 891 var doc = AjxXmlDoc.createFromXml(result.text); 892 resp = doc.getDoc(); 893 } 894 // TODO: instead of changing innerHTML, maybe append 895 // the dom tree to the canvas. 896 if (xslt) { 897 html = xslt.transformToString(resp); 898 // If we don't have HTML at this point, we probably have a HTML fragment. 899 if (!html) { 900 html = result.text; 901 } 902 903 } else { 904 html = resp.innerHTML; 905 } 906 canvas.innerHTML = html; 907 }; 908 909 /** 910 * @private 911 */ 912 ZmZimletContext._getMsgBody = 913 function(o) { 914 //If message is not loaded let developer take care of it 915 if (!o._loaded) { 916 return ""; 917 } 918 var part = o.getBodyPart(ZmMimeTable.TEXT_PLAIN) || o.getBodyPart(ZmMimeTable.TEXT_HTML); 919 var content = part && part.getContent(); 920 if (content && (part.contentType == ZmMimeTable.TEXT_HTML)) { 921 var div = document.createElement("div"); 922 div.innerHTML = content; 923 content = AjxStringUtil.convertHtml2Text(div); 924 } 925 return content || ""; 926 }; 927