1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 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) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Creates a Zimlets preference page. 26 * @constructor 27 * @class ZmZimletsPage 28 * This class represents a page that allows the user to enable/disable available 29 * zimlets. User can see all the simlets those are enabled by admin for his account. 30 * Out of these available zimlets user can choose some or all for his account. 31 * 32 * @author Rajendra Patil 33 * 34 * @param {DwtControl} parent the containing widget 35 * @param {Object} section the page 36 * @param {ZmPrefController} controller the prefs controller 37 * 38 * @extends ZmPreferencesPage 39 * @private 40 */ 41 ZmZimletsPage = function(parent, section, controller) { 42 ZmPreferencesPage.call(this, parent, section, controller); 43 }; 44 45 ZmZimletsPage.prototype = new ZmPreferencesPage; 46 ZmZimletsPage.prototype.constructor = ZmZimletsPage; 47 48 // CONSTS 49 ZmZimletsPage.ADMIN_URI = [ 50 "http://", 51 location.hostname, ":", 52 location.port, 53 "/service/admin/soap/" 54 ].join(""); 55 56 ZmZimletsPage.ADMIN_UPLOAD_URI = [ 57 "http://", 58 location.hostname, ":", 59 location.port, 60 "/service/upload" 61 ].join(""); 62 63 ZmZimletsPage.ADMIN_COOKIE_NAME = "ZM_ADMIN_AUTH_TOKEN"; 64 ZmZimletsPage.ZIMLET_UPLOAD_STATUS_PENDING = "pending"; 65 ZmZimletsPage.ZIMLET_UPLOAD_STATUS_SUCCESS = "succeeded"; 66 ZmZimletsPage.ZIMLET_UPLOAD_STATUS_FAIL = "failed"; 67 ZmZimletsPage.ZIMLET_MAX_CHECK_STATUS = 8; 68 69 ZmZimletsPage.prototype.toString = 70 function () { 71 return "ZmZimletsPage"; 72 }; 73 74 ZmZimletsPage.prototype.reset = 75 function(){ 76 var arr = this.getZimletsArray(); 77 for (var i = 0; i < arr.length; i++) { 78 arr[i].restoreStatus(); 79 } 80 this.showMe(); 81 }; 82 83 ZmZimletsPage.prototype.showMe = 84 function(deferred){ 85 ZmPreferencesPage.prototype.showMe.call(this); 86 var zimlets = this.getZimlets(); 87 if (zimlets.size() === 0) { 88 this._zimlets = null; //otherwise it would stay cached and would not update when this method is calledback. 89 if (!deferred) { 90 appCtxt.getAppController().addListener(ZmAppEvent.POST_STARTUP, new AjxListener(this, this.showMe, [true])); 91 } 92 return; 93 } 94 if (this._listView) { 95 var s = this._listView.getSelection(); 96 this._listView.set(zimlets.clone()); 97 if (s && s[0]) { 98 this._listView.setSelection(s[0]); 99 } 100 } 101 }; 102 103 ZmZimletsPage.prototype._getTemplateData = 104 function() { 105 var data = ZmPreferencesPage.prototype._getTemplateData.apply(this, arguments); 106 if (appCtxt.isOffline) { 107 data.action = ZmZimletsPage.ADMIN_UPLOAD_URI; 108 } 109 return data; 110 }; 111 112 /** 113 * @private 114 */ 115 ZmZimletsPage.prototype._setupCustom = 116 function(id, setup, value) { 117 if (id == ZmSetting.CHECKED_ZIMLETS) { 118 this._listView = new ZmPrefZimletListView(this, this._controller); 119 return this._listView; 120 } 121 122 return ZmPreferencesPage.prototype._setupCustom.apply(this, arguments); 123 }; 124 125 ZmZimletsPage.prototype._createControls = 126 function() { 127 if (appCtxt.isOffline) { 128 // add "upload" button 129 this._uploadButton = new DwtButton({parent:this, parentElement: this._htmlElId+"_button"}); 130 this._uploadButton.setText(ZmMsg.uploadNewFile); 131 this._uploadButton.addSelectionListener(new AjxListener(this, this._fileUploadListener)); 132 } 133 134 ZmPreferencesPage.prototype._createControls.apply(this, arguments); 135 }; 136 137 ZmZimletsPage.prototype._fileUploadListener = 138 function() { 139 this._uploadButton.setEnabled(false); 140 141 // first, fetch the admin auth token if none exists 142 var callback = new AjxCallback(this, this._uploadZimlet); 143 this._getAdminAuth(callback); 144 }; 145 146 ZmZimletsPage.prototype._uploadZimlet = 147 function() { 148 this._checkStatusCount = 0; 149 150 var callback = new AjxCallback(this, this._handleZimletUpload); 151 var formEl = document.getElementById(this._htmlElId + "_form"); 152 var um = window._uploadManager = appCtxt.getUploadManager(); 153 um.execute(callback, formEl); 154 }; 155 156 ZmZimletsPage.prototype._handleZimletUpload = 157 function(status, aid) { 158 if (status == 200) { 159 this._deployZimlet(aid); 160 } 161 else { 162 var msg = (status == AjxPost.SC_NO_CONTENT) 163 ? ZmMsg.zimletUploadError 164 : (AjxMessageFormat.format(ZmMsg.zimletUploadStatus, status)); 165 appCtxt.setStatusMsg(msg, ZmStatusView.LEVEL_CRITICAL); 166 167 this._uploadButton.setEnabled(true); 168 } 169 }; 170 171 ZmZimletsPage.prototype._getAdminAuth = 172 function(callback) { 173 // first, make sure we have a valid admin password (parsed from location) 174 var pword; 175 var searches = document.location.search.split("&"); 176 for (var i = 0; i < searches.length; i++) { 177 var idx = searches[i].indexOf("at="); 178 if (idx != -1) { 179 pword = searches[i].substring(idx+3); 180 break; 181 } 182 } 183 184 // for dev build 185 if (!pword) { 186 pword = "@install.key@"; 187 } 188 189 var soapDoc = AjxSoapDoc.create("AuthRequest", "urn:zimbraAdmin"); 190 soapDoc.set("name", "zimbra"); 191 soapDoc.set("password", pword); 192 193 var params = { 194 soapDoc: soapDoc, 195 noSession: true, 196 asyncMode: true, 197 noAuthToken: true, 198 skipAuthCheck: true, 199 serverUri: ZmZimletsPage.ADMIN_URI, 200 callback: (new AjxCallback(this, this._handleResponseAuthenticate, [callback])) 201 }; 202 (new ZmCsfeCommand()).invoke(params); 203 }; 204 205 ZmZimletsPage.prototype._handleResponseAuthenticate = 206 function(callback, result) { 207 if (result.isException()) { 208 this._handleZimletDeployError(); 209 return; 210 } 211 212 // set the admin auth cookie (expires when browser closes) 213 this._adminAuthToken = result.getResponse().Body.AuthResponse.authToken[0]._content; 214 AjxCookie.setCookie(document, ZmZimletsPage.ADMIN_COOKIE_NAME, this._adminAuthToken, null, "/service/upload"); 215 216 if (callback) { 217 callback.run(); 218 } 219 }; 220 221 ZmZimletsPage.prototype._deployZimlet = 222 function(aid, action) { 223 var dialog = appCtxt.getCancelMsgDialog(); 224 dialog.reset(); 225 dialog.setMessage(ZmMsg.zimletDeploying, DwtMessageDialog.INFO_STYLE); 226 dialog.registerCallback(DwtDialog.CANCEL_BUTTON, new AjxCallback(this, this._handleZimletCancel, [dialog])); 227 dialog.popup(); 228 229 var soapDoc = AjxSoapDoc.create("DeployZimletRequest", "urn:zimbraAdmin"); 230 var method = soapDoc.getMethod(); 231 method.setAttribute("action", (action || "deployLocal")); 232 method.setAttribute("flush", "1"); 233 method.setAttribute("synchronous", "1"); 234 var content = soapDoc.set("content"); 235 content.setAttribute("aid", aid); 236 237 var params = { 238 soapDoc: soapDoc, 239 asyncMode: true, 240 skipAuthCheck: true, 241 callback: (new AjxCallback(this, this._deployZimletResponse, [dialog, aid])), 242 serverUri: ZmZimletsPage.ADMIN_URI, 243 authToken: this._adminAuthToken 244 }; 245 246 (new ZmCsfeCommand()).invoke(params); 247 }; 248 249 ZmZimletsPage.prototype._deployZimletResponse = 250 function(dialog, aid, result) { 251 252 // remove Admin auth key 253 AjxCookie.deleteCookie(document, ZmZimletsPage.ADMIN_COOKIE_NAME, "/service/upload"); 254 255 if (result.isException()) { 256 dialog.popdown(); 257 this._uploadButton.setEnabled(true); 258 259 this._controller.popupErrorDialog(ZmMsg.zimletDeployError, result.getException()); 260 return; 261 } 262 263 var status = result.getResponse().Body.DeployZimletResponse.progress[0].status; 264 if (status == ZmZimletsPage.ZIMLET_UPLOAD_STATUS_PENDING) { 265 if (this._checkStatusCount++ > ZmZimletsPage.ZIMLET_MAX_CHECK_STATUS) { 266 this._handleZimletDeployError(); 267 } else { 268 AjxTimedAction.scheduleAction(new AjxTimedAction(this, this._deployZimlet, [aid, "status"]), 1500); 269 } 270 } else { 271 dialog.popdown(); 272 273 if (status == ZmZimletsPage.ZIMLET_UPLOAD_STATUS_FAIL) { 274 this._handleZimletDeployError(); 275 } 276 else { 277 this._uploadButton.setEnabled(true); 278 279 var settings = appCtxt.getSettings(appCtxt.accountList.mainAccount); 280 settings._showConfirmDialog(ZmMsg.zimletDeploySuccess, settings._refreshBrowserCallback.bind(settings), DwtMessageDialog.INFO_STYLE); 281 } 282 } 283 }; 284 285 ZmZimletsPage.prototype._handleZimletDeployError = 286 function() { 287 this._uploadButton.setEnabled(true); 288 289 var dialog = appCtxt.getMsgDialog(); 290 dialog.setMessage(ZmMsg.zimletDeployError, DwtMessageDialog.CRITICAL_STYLE); 291 dialog.popup(); 292 }; 293 294 ZmZimletsPage.prototype._handleZimletCancel = 295 function(dialog) { 296 dialog.popdown(); 297 this._uploadButton.setEnabled(true); 298 }; 299 300 ZmZimletsPage.prototype.undeployZimlet = 301 function(zimletName) { 302 var callback = new AjxCallback(this, this._handleUndeployZimlet, [zimletName]); 303 this._getAdminAuth(callback); 304 }; 305 306 ZmZimletsPage.prototype._handleUndeployZimlet = 307 function(zimletName) { 308 var soapDoc = AjxSoapDoc.create("UndeployZimletRequest", "urn:zimbraAdmin"); 309 soapDoc.getMethod().setAttribute("name", zimletName); 310 311 var params = { 312 soapDoc: soapDoc, 313 asyncMode: true, 314 skipAuthCheck: true, 315 callback: (new AjxCallback(this, this._undeployZimletResponse, [zimletName])), 316 serverUri: ZmZimletsPage.ADMIN_URI, 317 authToken: this._adminAuthToken 318 }; 319 320 (new ZmCsfeCommand()).invoke(params); 321 }; 322 323 ZmZimletsPage.prototype._undeployZimletResponse = 324 function(zimletName, result) { 325 326 // remove admin auth key 327 AjxCookie.deleteCookie(document, ZmZimletsPage.ADMIN_COOKIE_NAME, "/service/upload"); 328 329 if (result.isException()) { 330 this._controller.popupErrorDialog(ZmMsg.zimletUndeployError, result.getException()); 331 return; 332 } 333 334 // remove the uninstalled zimlet from the listview 335 var zimletsCtxt = this.getZimletsCtxt(); 336 var zimlet = zimletsCtxt.getPrefZimletByName(zimletName); 337 if (zimlet) { 338 zimletsCtxt.removePrefZimlet(zimlet); 339 this._listView.set(zimletsCtxt.getZimlets().clone()); 340 } 341 342 // prompt user to restart client 343 var settings = appCtxt.getSettings(appCtxt.accountList.mainAccount); 344 settings._showConfirmDialog(ZmMsg.zimletUndeploySuccess, settings._refreshBrowserCallback.bind(settings), DwtMessageDialog.INFO_STYLE); 345 }; 346 347 ZmZimletsPage.prototype.addCommand = 348 function(batchCommand) { 349 var soapDoc = AjxSoapDoc.create("ModifyZimletPrefsRequest", "urn:zimbraAccount"); 350 // LDAP supports multi-valued attrs, so don't serialize list 351 var zimlets = this.getZimletsArray(); 352 var settingsObj = appCtxt.getSettings(); 353 var setting = settingsObj.getSetting(ZmSetting.CHECKED_ZIMLETS); 354 var checked = []; 355 for (var i = 0; i < zimlets.length; i++) { 356 if (zimlets[i].active) { 357 checked.push(zimlets[i].name); 358 } 359 var node = soapDoc.set("zimlet", null); 360 node.setAttribute("name", zimlets[i].name); 361 node.setAttribute("presence", (zimlets[i].active ? "enabled" : "disabled")); 362 } 363 setting.setValue(checked); 364 batchCommand.addNewRequestParams(soapDoc); 365 }; 366 367 ZmZimletsPage.prototype._reloadZimlets = 368 function() { 369 // reset all zimlets origStatus 370 var zimlets = this.getZimletsArray(); 371 for (var i = 0; i < zimlets.length; i++) { 372 zimlets[i].resetStatus(); 373 } 374 }; 375 376 ZmZimletsPage.prototype.getPostSaveCallback = 377 function() { 378 return new AjxCallback(this, this._postSave); 379 }; 380 ZmZimletsPage.prototype._postSave = 381 function() { 382 if (!this.isDirty()) { return; } 383 384 this._reloadZimlets(); 385 386 var settings = appCtxt.getSettings(); 387 settings._showConfirmDialog(ZmMsg.zimletChangeRestart, settings._refreshBrowserCallback.bind(settings)); 388 }; 389 390 ZmZimletsPage.prototype._isChecked = 391 function(name) { 392 var z = this.getZimletsCtxt().getPrefZimletByName(name); 393 return (z && z.active); 394 }; 395 396 ZmZimletsPage.prototype.isDirty = 397 function() { 398 var dirty = false; 399 var arr = this.getZimletsArray(); 400 var dirtyZimlets = []; 401 402 var printZimlet = function(zimlet) { 403 if (AjxUtil.isArray(zimlet)) { 404 return AjxUtil.map(zimlet, printZimlet).join("\n"); 405 } 406 return [zimlet.name," (from ",zimlet._origStatus," to ",zimlet.active,")"].join(""); 407 } 408 409 for (var i = 0; i < arr.length; i++) { 410 if (arr[i]._origStatus != arr[i].active) { 411 dirty = true; 412 dirtyZimlets.push(arr[i]); 413 } 414 } 415 416 if (dirty) { 417 AjxDebug.println(AjxDebug.PREFS, "Dirty preferences:\n" + "Dirty zimlets:\n" + printZimlet(dirtyZimlets)); 418 } 419 return dirty; 420 }; 421 422 /** 423 * Gets the zimlet preferences. 424 * 425 * @return {ZmPrefZimlets} the zimlets 426 * 427 * @private 428 */ 429 ZmZimletsPage.prototype.getZimletsCtxt = 430 function() { 431 if (!this._zimlets) { 432 this._zimlets = ZmZimletsPage._getZimlets(); 433 } 434 return this._zimlets; 435 }; 436 437 ZmZimletsPage.prototype.getZimlets = 438 function() { 439 return this.getZimletsCtxt().getZimlets(); 440 }; 441 442 ZmZimletsPage.prototype.getZimletsArray = 443 function() { 444 return this.getZimlets().getArray(); 445 }; 446 447 448 ZmZimletsPage._getZimlets = 449 function() { 450 var allz = appCtxt.get(ZmSetting.ZIMLETS) || []; 451 var zimlets = new ZmPrefZimlets(); 452 var zimletsLoaded = appCtxt.getZimletMgr().isLoaded(); 453 for (var i = 0; i < allz.length; i++) { 454 var name = allz[i].zimlet[0].name; 455 if (allz[i].zimletContext[0].presence == "mandatory") { 456 continue; // skip mandatory zimlets to be shown in prefs 457 } 458 var desc = allz[i].zimlet[0].description; 459 var label = allz[i].zimlet[0].label || name.replace(/^.*_/,""); 460 if (zimletsLoaded) { 461 desc = ZmZimletContext.processMessage(name, desc); 462 label = ZmZimletContext.processMessage(name, label); 463 } 464 var isEnabled = allz[i].zimletContext[0].presence == "enabled"; 465 zimlets.addPrefZimlet(new ZmPrefZimlet(name, isEnabled, desc, label)); 466 } 467 zimlets.sortByName(); 468 return zimlets; 469 }; 470 471 /** 472 * ZmPrefZimletListView 473 * 474 * @param parent 475 * @param controller 476 * @private 477 */ 478 ZmPrefZimletListView = function(parent, controller) { 479 DwtListView.call(this, { 480 parent: parent, 481 className: "ZmPrefZimletListView", 482 headerList: this._getHeaderList(), 483 id: "ZmPrefZimletListView", 484 view: ZmId.VIEW_PREF_ZIMLETS 485 }); 486 487 this._controller = controller; 488 this.setMultiSelect(false); // single selection only 489 this._internalId = AjxCore.assignId(this); 490 }; 491 492 ZmPrefZimletListView.COL_ACTIVE = "ac"; 493 ZmPrefZimletListView.COL_NAME = "na"; 494 ZmPrefZimletListView.COL_DESC = "ds"; 495 ZmPrefZimletListView.COL_ACTION = "an"; 496 497 ZmPrefZimletListView.prototype = new DwtListView; 498 ZmPrefZimletListView.prototype.constructor = ZmPrefZimletListView; 499 500 ZmPrefZimletListView.prototype.toString = 501 function() { 502 return "ZmPrefZimletListView"; 503 }; 504 505 /** 506 * Only show zimlets that have at least one valid action (eg, if the only action 507 * is "tag" and tagging is disabled, don't show the rule). 508 */ 509 ZmPrefZimletListView.prototype.set = 510 function(list) { 511 this._checkboxIds = []; 512 this._zimletsLoaded = appCtxt.getZimletMgr().isLoaded(); 513 DwtListView.prototype.set.call(this, list); 514 if (!this._zimletsLoaded) { 515 appCtxt.addZimletsLoadedListener(new AjxListener(this, this._handleZimletsLoaded)); 516 } 517 }; 518 519 ZmPrefZimletListView.prototype._handleZimletsLoaded = function(evt) { 520 this._zimletsLoaded = true; 521 var array = this.parent.getZimletsArray(); 522 for (var i = 0; i < array.length; i++) { 523 var item = array[i]; 524 var label = item.label || item.name.replace(/^.*_/,""); 525 item.label = ZmZimletContext.processMessage(item.name, label); 526 item.desc = ZmZimletContext.processMessage(item.name, item.desc); 527 this.setCellContents(item, ZmPrefZimletListView.COL_NAME, AjxStringUtil.htmlEncode(item.label)); 528 this.setCellContents(item, ZmPrefZimletListView.COL_DESC, AjxStringUtil.htmlEncode(item.desc)); 529 } 530 }; 531 532 ZmPrefZimletListView.prototype._getRowId = 533 function(item, params) { 534 if (item) { 535 var rowIndex = this._list && this._list.indexOf(item); 536 if (rowIndex && rowIndex != -1) 537 return "ZmPrefZimletListView_row_" + rowIndex; 538 } 539 540 return null; 541 }; 542 543 544 ZmPrefZimletListView.prototype.setCellContents = function(item, field, html) { 545 var el = this.getCellElement(item, field); 546 if (!el) { return; } 547 el.innerHTML = html; 548 }; 549 550 ZmPrefZimletListView.prototype.getCellElement = function(item, field) { 551 return document.getElementById(this._getCellId(item, field)); 552 }; 553 554 ZmPrefZimletListView.prototype._getCellId = function(item, field, params) { 555 return DwtId.getListViewItemId(DwtId.WIDGET_ITEM_CELL, "zimlets", item.name, field); 556 }; 557 558 ZmPrefZimletListView.prototype._getHeaderList = 559 function() { 560 var hlist = [ 561 (new DwtListHeaderItem({field:ZmPrefZimletListView.COL_ACTIVE, text:ZmMsg.active, width:ZmMsg.COLUMN_WIDTH_ACTIVE})), 562 (new DwtListHeaderItem({field:ZmPrefZimletListView.COL_NAME, text:ZmMsg.name, width:ZmMsg.COLUMN_WIDTH_FOLDER_DLV})), 563 (new DwtListHeaderItem({field:ZmPrefZimletListView.COL_DESC, text:ZmMsg.description})) 564 ]; 565 566 if (appCtxt.isOffline) { 567 hlist.push(new DwtListHeaderItem({field:ZmPrefZimletListView.COL_ACTION, text:ZmMsg.action, width:ZmMsg.COLUMN_WIDTH_FOLDER_DLV})); 568 } 569 570 return hlist; 571 }; 572 573 ZmPrefZimletListView.prototype._getCellContents = 574 function(html, idx, item, field, colIdx, params) { 575 if (field == ZmPrefZimletListView.COL_ACTIVE) { 576 html[idx++] = "<input name='checked_zimlets' type='checkbox' "; 577 html[idx++] = item.active ? "checked " : ""; 578 html[idx++] = "id='"; 579 html[idx++] = item.name; 580 html[idx++] = "_zimletCheckbox' _name='"; 581 html[idx++] = item.name; 582 html[idx++] = "' _flvId='"; 583 html[idx++] = this._internalId; 584 html[idx++] = "' onchange='ZmPrefZimletListView._activeStateChange'>"; 585 } 586 else if (field == ZmPrefZimletListView.COL_DESC) { 587 var desc = this._zimletsLoaded ? item.desc : ZmMsg.loading; 588 html[idx++] = "<div id='"; 589 html[idx++] = this._getCellId(item, ZmPrefZimletListView.COL_DESC); 590 html[idx++] = "'>"; 591 html[idx++] = AjxStringUtil.stripTags(desc, true); 592 html[idx++] = "</div>"; 593 } 594 else if (field == ZmPrefZimletListView.COL_NAME) { 595 html[idx++] = "<div id='"; 596 html[idx++] = this._getCellId(item, ZmPrefZimletListView.COL_NAME); 597 html[idx++] = "' title='"; 598 html[idx++] = item.name; 599 html[idx++] = "'>"; 600 html[idx++] = AjxStringUtil.stripTags(item.getNameWithoutPrefix(!this._zimletsLoaded), true); 601 html[idx++] = "</div>"; 602 } 603 else if (field == ZmPrefZimletListView.COL_ACTION) { 604 html[idx++] = "<a href='javascript:;' onclick='ZmPrefZimletListView.undeployZimlet("; 605 html[idx++] = '"' + item.name + '"'; 606 html[idx++] = ");'>"; 607 html[idx++] = ZmMsg.uninstall; 608 html[idx++] = "</a>"; 609 } 610 611 return idx; 612 }; 613 614 ZmPrefZimletListView.undeployZimlet = 615 function(zimletName) { 616 appCtxt.getCurrentView().prefView["PREF_ZIMLETS"].undeployZimlet(zimletName); 617 }; 618 619 /** 620 * Handles click of 'active' checkbox by toggling the rule's active state. 621 * 622 * @param ev [DwtEvent] click event 623 */ 624 ZmPrefZimletListView._activeStateChange = 625 function(ev) { 626 var target = DwtUiEvent.getTarget(ev); 627 var flvId = target.getAttribute("_flvId"); 628 var flv = AjxCore.objectWithId(flvId); 629 var name = target.getAttribute("_name"); 630 var z = flv.parent.getZimletsCtxt().getPrefZimletByName(name); 631 if (z) { 632 z.active = !z.active; 633 } 634 }; 635 636 /** 637 * Override so that we don't change selection when the 'active' checkbox is clicked. 638 * Also contains a hack for IE for handling a click of the 'active' checkbox, because 639 * the ONCHANGE handler was only getting invoked on every other checkbox click for IE. 640 * 641 * @param clickedEl [Element] list DIV that received the click 642 * @param ev [DwtEvent] click event 643 * @param button [constant] button that was clicked 644 */ 645 ZmPrefZimletListView.prototype._allowLeftSelection = 646 function(clickedEl, ev, button) { 647 var target = DwtUiEvent.getTarget(ev); 648 var isInput = (target.id.indexOf("_zimletCheckbox") > 0); 649 if (isInput) { 650 ZmPrefZimletListView._activeStateChange(ev); 651 } 652 653 return !isInput; 654 }; 655 656 /** 657 * Model class to hold the list of PrefZimlets 658 * @private 659 */ 660 ZmPrefZimlets = function() { 661 ZmModel.call(this, ZmEvent.S_PREF_ZIMLET); 662 this._vector = new AjxVector(); 663 this._zNameHash = {}; 664 }; 665 666 ZmPrefZimlets.prototype = new ZmModel; 667 ZmPrefZimlets.prototype.constructor = ZmPrefZimlets; 668 669 ZmPrefZimlets.prototype.toString = 670 function() { 671 return "ZmPrefZimlets"; 672 }; 673 674 ZmPrefZimlets.prototype.getZimlets = 675 function() { 676 return this._vector; 677 }; 678 679 ZmPrefZimlets.prototype.addPrefZimlet = 680 function(zimlet) { 681 this._vector.add(zimlet); 682 this._zNameHash[zimlet.name] = zimlet; 683 }; 684 685 ZmPrefZimlets.prototype.removePrefZimlet = 686 function(zimlet) { 687 delete this._zNameHash[zimlet.name]; 688 this._vector.remove(zimlet); 689 }; 690 691 ZmPrefZimlets.prototype.getPrefZimletByName = 692 function(name) { 693 return this._zNameHash[name]; 694 }; 695 696 /** 697 * 698 * @param desc true for desc sorting, false or empty otherwise 699 */ 700 ZmPrefZimlets.prototype.sortByName = 701 function(desc) { 702 var r = 0; 703 var zimletsLoaded = appCtxt.getZimletMgr().isLoaded(); 704 this._vector.sort(function(a,b) { 705 var aname = a.getNameWithoutPrefix(!zimletsLoaded).toLowerCase(); 706 var bname = b.getNameWithoutPrefix(!zimletsLoaded).toLowerCase(); 707 708 if (aname == bname) { 709 r = 0; 710 } else if (aname > bname) { 711 r = 1; 712 } else { 713 r = -1; 714 } 715 return (desc ? -r : r); 716 }); 717 }; 718 719 /** 720 * ZmPrefZimlet 721 * 722 * @param name 723 * @param active 724 * @param desc 725 * 726 * @private 727 */ 728 ZmPrefZimlet = function(name, active, desc, label) { 729 this.name = name; 730 this.active = (active !== false); 731 this.desc = desc; 732 this.label = label; 733 this._origStatus = this.active; 734 }; 735 736 ZmPrefZimlet.prototype.getNameWithoutPrefix = 737 function(noLabel) { 738 if (!noLabel && this.label != null && this.label.length > 0) { 739 return this.label; 740 } 741 742 return this.name.substring(this.name.lastIndexOf("_")+1); 743 }; 744 745 ZmPrefZimlet.prototype.resetStatus = 746 function() { 747 this._origStatus = this.active; 748 }; 749 750 ZmPrefZimlet.prototype.restoreStatus = 751 function() { 752 this.active = this._origStatus; 753 }; 754