1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 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) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Creates the data source collection. 26 * @class 27 * This class represents a data source collection. 28 * 29 * @extends ZmModel 30 */ 31 ZmDataSourceCollection = function() { 32 ZmModel.call(this, ZmEvent.S_DATA_SOURCE); 33 this._initialized = false; 34 this._itemMap = {}; 35 this._pop3Map = {}; 36 this._imapMap = {}; 37 }; 38 ZmDataSourceCollection.prototype = new ZmModel; 39 ZmDataSourceCollection.prototype.constructor = ZmDataSourceCollection; 40 41 // 42 // Public methods 43 // 44 45 ZmDataSourceCollection.prototype.toString = 46 function() { 47 return "ZmDataSourceCollection"; 48 }; 49 50 ZmDataSourceCollection.prototype.getItems = function() { 51 return AjxUtil.values(this._itemMap); 52 }; 53 54 ZmDataSourceCollection.prototype.getItemsFor = function(folderId) { 55 var accounts = []; 56 for (var id in this._itemMap) { 57 var account = this._itemMap[id]; 58 if (account.folderId == folderId && account.enabled) { 59 accounts.push(account); 60 } 61 } 62 return accounts; 63 }; 64 65 /** 66 * Gets the POP accounts. 67 * 68 * @return {Array} an array of {@link ZmPopAccount} objects 69 */ 70 ZmDataSourceCollection.prototype.getPopAccounts = function() { 71 return AjxUtil.values(this._pop3Map); 72 }; 73 74 /** 75 * Gets the IMAP accounts. 76 * 77 * @return {Array} an array of {@link ZmImapAccount} objects 78 */ 79 ZmDataSourceCollection.prototype.getImapAccounts = function() { 80 return AjxUtil.values(this._imapMap); 81 }; 82 83 /** 84 * Gets the POP accounts. 85 * 86 * @param {String} folderId the folder id 87 * @return {Array} an array of {@link ZmPopAccount} objects 88 */ 89 ZmDataSourceCollection.prototype.getPopAccountsFor = function(folderId) { 90 var accounts = []; 91 for (var id in this._pop3Map) { 92 var account = this._pop3Map[id]; 93 if (account.folderId == folderId && account.enabled) { 94 accounts.push(account); 95 } 96 } 97 return accounts; 98 }; 99 100 /** 101 * Gets the IMAP accounts. 102 * 103 * @param {String} folderId the folder id 104 * @return {Array} an array of {@link ZmImapAccount} objects 105 */ 106 ZmDataSourceCollection.prototype.getImapAccountsFor = function(folderId) { 107 var accounts = []; 108 for (var id in this._imapMap) { 109 var account = this._imapMap[id]; 110 if (account.folderId == folderId && account.enabled) { 111 accounts.push(account); 112 } 113 } 114 return accounts; 115 }; 116 117 ZmDataSourceCollection.prototype.importMailFor = function(folderId) { 118 this.importMail(this.getItemsFor(folderId)); 119 }; 120 121 ZmDataSourceCollection.prototype.importPopMailFor = function(folderId) { 122 this.importMail(this.getPopAccountsFor(folderId)); 123 }; 124 125 ZmDataSourceCollection.prototype.importImapMailFor = function(folderId) { 126 this.importMail(this.getImapAccountsFor(folderId)); 127 }; 128 129 ZmDataSourceCollection.prototype.importMail = function(accounts) { 130 if (accounts && accounts.length > 0) { 131 var sourceMap = {}; 132 var soapDoc = AjxSoapDoc.create("ImportDataRequest", "urn:zimbraMail"); 133 for (var i = 0; i < accounts.length; i++) { 134 var account = accounts[i]; 135 sourceMap[account.id] = account; 136 137 var dsrc = soapDoc.set(account.ELEMENT_NAME); 138 dsrc.setAttribute("id", account.id); 139 } 140 141 // send import request 142 var params = { 143 soapDoc: soapDoc, 144 asyncMode: true, 145 noBusyOverlay: true, 146 callback: null, 147 errorCallback: null 148 }; 149 appCtxt.getAppController().sendRequest(params); 150 151 // kick off check status request because import request 152 // doesn't return for (potentially) a looong time 153 var delayMs = 2000; 154 var action = new AjxTimedAction(this, this.checkStatus, [sourceMap, delayMs]); 155 AjxTimedAction.scheduleAction(action, delayMs); 156 } 157 }; 158 159 ZmDataSourceCollection.prototype.getById = function(id) { 160 return this._itemMap[id]; 161 }; 162 163 /** 164 * Gets a list of data sources associated with the given folder ID. 165 * 166 * @param {String} folderId [String] the folderId 167 * @param {constant} type the type of data source (see <code>ZmAccount.TYPE_</code> constants) 168 * @return {Array} an array of items 169 * 170 * @see ZmAccount 171 */ 172 ZmDataSourceCollection.prototype.getByFolderId = function(folderId, type) { 173 var list = []; 174 for (var id in this._itemMap) { 175 var item = this._itemMap[id]; 176 if (item.folderId == folderId) { 177 if (!type || (type && type == item.type)) 178 list.push(item); 179 } 180 } 181 return list; 182 }; 183 184 ZmDataSourceCollection.prototype.add = function(item) { 185 this._itemMap[item.id] = item; 186 if (item.type == ZmAccount.TYPE_POP) { 187 this._pop3Map[item.id] = item; 188 } 189 else if (item.type == ZmAccount.TYPE_IMAP) { 190 this._imapMap[item.id] = item; 191 } 192 appCtxt.getIdentityCollection().add(item.getIdentity()); 193 this._notify(ZmEvent.E_CREATE, {item:item}); 194 }; 195 196 ZmDataSourceCollection.prototype.modify = function(item) { 197 appCtxt.getIdentityCollection().notifyModify(item.getIdentity(), true); 198 this._notify(ZmEvent.E_MODIFY, {item:item}); 199 }; 200 201 ZmDataSourceCollection.prototype.remove = function(item) { 202 delete this._itemMap[item.id]; 203 delete this._pop3Map[item.id]; 204 delete this._imapMap[item.id]; 205 appCtxt.getIdentityCollection().remove(item.getIdentity()); 206 this._notify(ZmEvent.E_DELETE, {item:item}); 207 }; 208 209 ZmDataSourceCollection.prototype.initialize = function(dataSources) { 210 if (!dataSources || this._initialized) { return; } 211 212 var errors = []; 213 214 if (appCtxt.get(ZmSetting.POP_ACCOUNTS_ENABLED)) { 215 var popAccounts = dataSources.pop3 || []; 216 for (var i = 0; i < popAccounts.length; i++) { 217 var object = popAccounts[i]; 218 var dataSource = new ZmPopAccount(object.id); 219 dataSource.setFromJson(object); 220 this.add(dataSource); 221 if (!dataSource.isStatusOk()) { 222 errors.push(dataSource); 223 } 224 } 225 } 226 227 if (appCtxt.get(ZmSetting.IMAP_ACCOUNTS_ENABLED)) { 228 var imapAccounts = dataSources.imap || []; 229 for (var i = 0; i < imapAccounts.length; i++) { 230 var object = imapAccounts[i]; 231 var dataSource = new ZmImapAccount(object.id); 232 dataSource.setFromJson(object); 233 this.add(dataSource); 234 if (!dataSource.isStatusOk()) { 235 errors.push(dataSource); 236 } 237 } 238 } 239 240 this._initialized = true; 241 242 var count = errors.length; 243 if (count > 0) { 244 // build error message 245 var array = [ 246 AjxMessageFormat.format(ZmMsg.dataSourceFailureDescription, [count]) 247 ]; 248 for (var i = 0; i < count; i++) { 249 var dataSource = errors[i]; 250 var timestamp = Number(dataSource.failingSince); 251 var lastError = dataSource.lastError; 252 if (isNaN(timestamp)) { 253 var pattern = ZmMsg.dataSourceFailureItem_noDate; 254 var params = [AjxStringUtil.htmlEncode(dataSource.getName()), AjxStringUtil.htmlEncode(lastError)]; 255 } else { 256 var pattern = ZmMsg.dataSourceFailureItem; 257 var params = [AjxStringUtil.htmlEncode(dataSource.getName()), new Date(timestamp * 1000), AjxStringUtil.htmlEncode(lastError)]; 258 } 259 array.push(AjxMessageFormat.format(pattern, params)); 260 } 261 array.push(ZmMsg.dataSourceFailureInstructions); 262 var message = array.join(""); 263 264 // show error message 265 var shell = DwtShell.getShell(window); 266 var dialog = new DwtMessageDialog({parent:shell,buttons:[DwtDialog.OK_BUTTON,DwtDialog.CANCEL_BUTTON]}); 267 dialog.setMessage(message, DwtMessageDialog.CRITICAL_STYLE, ZmMsg.dataSourceFailureTitle); 268 dialog.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this.__handleErrorDialogOk, [dialog])); 269 dialog.popup(); 270 } 271 }; 272 273 ZmDataSourceCollection.prototype.__handleErrorDialogOk = function(dialog) { 274 dialog.popdown(); 275 276 var callback = new AjxCallback(this, this.__gotoPrefSection, ["ACCOUNTS"]); 277 appCtxt.getAppController().activateApp(ZmApp.PREFERENCES, true, callback); 278 }; 279 280 ZmDataSourceCollection.prototype.__gotoPrefSection = function(prefSectionId) { 281 var controller = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController(); 282 controller.getPrefsView().selectSection(prefSectionId); 283 }; 284 285 /** 286 * Periocially check status of the import 287 * @param {Object} sourceMap map of accounts 288 * @param {int} delayMs delay time between checks 289 */ 290 ZmDataSourceCollection.prototype.checkStatus = 291 function(sourceMap, delayMs) { 292 // Slowly back off import status checks but no more than 15 secs. 293 if (delayMs && delayMs < 15000) { 294 delayMs += 2000; 295 } 296 297 var soapDoc = AjxSoapDoc.create("GetImportStatusRequest", "urn:zimbraMail"); 298 var callback = new AjxCallback(this, this._checkStatusResponse, [sourceMap, delayMs]); 299 var params = { 300 soapDoc: soapDoc, 301 asyncMode: true, 302 callback: callback, 303 errorCallback: null 304 }; 305 306 var appController = appCtxt.getAppController(); 307 var action = new AjxTimedAction(appController, appController.sendRequest, [params]); 308 AjxTimedAction.scheduleAction(action, delayMs || 2000); 309 }; 310 311 // 312 // Protected methods 313 // 314 315 ZmDataSourceCollection.prototype._checkStatusResponse = 316 function(sourceMap, delayMs, result) { 317 var dataSources = []; 318 319 // gather sources from the response 320 var popSources = result._data.GetImportStatusResponse.pop3; 321 if (popSources) { 322 for (var i in popSources) { 323 dataSources.push(popSources[i]); 324 } 325 } 326 var imapSources = result._data.GetImportStatusResponse.imap; 327 if (imapSources) { 328 for (var i in imapSources) { 329 dataSources.push(imapSources[i]); 330 } 331 } 332 var genericSources = result._data.GetImportStatusResponse.dsrc; 333 if (genericSources) { 334 for (var i in genericSources) { 335 dataSources.push(genericSources[i]); 336 } 337 } 338 339 // is there anything to do? 340 if (dataSources.length == 0) return; 341 342 // report status 343 for (var i = 0; i < dataSources.length; i++) { 344 var dsrc = dataSources[i]; 345 // NOTE: Only report the ones we were asked to; forget others 346 if (!dsrc.isRunning && sourceMap[dsrc.id]) { 347 var source = sourceMap[dsrc.id]; 348 if (sourceMap[dsrc.id]) { 349 delete sourceMap[dsrc.id]; 350 if (dsrc.success) { 351 var message = AjxMessageFormat.format(ZmMsg.dataSourceLoadSuccess, AjxStringUtil.htmlEncode(source.name)); 352 appCtxt.setStatusMsg(message); 353 } 354 else { 355 var message = AjxMessageFormat.format(ZmMsg.dataSourceLoadFailure, AjxStringUtil.htmlEncode(source.name)); 356 appCtxt.setStatusMsg(message, ZmStatusView.LEVEL_CRITICAL); 357 var dialog = appCtxt.getErrorDialog(); 358 dialog.setMessage(message, dsrc.error, DwtMessageDialog.CRITICAL_STYLE); 359 dialog.popup(); 360 } 361 } 362 } 363 } 364 365 // continue checking status 366 if (AjxUtil.keys(sourceMap).length > 0) { 367 this.checkStatus(sourceMap, delayMs); 368 } 369 }; 370