1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Singleton alert class that alerts the user by popping up a message on the desktop.
 26  * @class
 27  * @private
 28  */
 29 ZmDesktopAlert = function() {
 30     if (window.webkitNotifications) {
 31         this.useWebkit = true;
 32 	} else if (window.Notification) {
 33 		this.useNotification = true;
 34     } else if (appCtxt.isOffline && window.platform && (AjxEnv.isWindows || AjxEnv.isMac)) {
 35         this.usePrism = true;
 36     }
 37 };
 38 
 39 ZmDesktopAlert.prototype = new ZmAlert;
 40 ZmDesktopAlert.prototype.constructor = ZmDesktopAlert;
 41 
 42 ZmDesktopAlert.prototype.toString =
 43 function() {
 44 	return "ZmDesktopAlert";
 45 };
 46 
 47 ZmDesktopAlert.getInstance =
 48 function() {
 49 	return ZmDesktopAlert.INSTANCE = ZmDesktopAlert.INSTANCE || new ZmDesktopAlert();
 50 };
 51 
 52 /**
 53  * Returns text to show in a prefs page next to the checkbox to enable this type of alert.
 54  */
 55 ZmDesktopAlert.prototype.getDisplayText =
 56 function() {
 57     if (this.useWebkit || this.useNotification) {
 58        return ZmMsg.showPopup;
 59     } else if (this.usePrism) {
 60 		return AjxEnv.isMac ? ZmMsg.showPopupMac : ZmMsg.showPopup;
 61 	}
 62 };
 63 
 64 ZmDesktopAlert.prototype.start =
 65 function(title, message, sticky) {
 66     if (this.useWebkit) {
 67         var allowedCallback = this._showWebkitNotification.bind(this, title, message, sticky);
 68         this._checkWebkitPermission(allowedCallback);
 69 	} else if (this.useNotification) {
 70 		var notificationCallback = this._showNotification.bind(this, title, message, sticky);
 71 		this._checkNotificationPermission(notificationCallback);
 72     } else if (this.usePrism) {
 73 		if (AjxEnv.isMac) {
 74 			try {
 75 				window.platform.showNotification(title, message, "resource://webapp/icons/default/launcher.icns");
 76 			} catch (err) {}
 77 		}
 78 		else if (AjxEnv.isWindows) {
 79 			try {
 80 				window.platform.icon().showNotification(title, message, 5);
 81 			} catch (err) {}
 82 		}
 83 	}
 84 };
 85 
 86 /* Checks if we have permission to use webkit notifications. If so, or when the user
 87  * grants permission, allowedCallback is called.
 88  */
 89 ZmDesktopAlert.prototype._checkWebkitPermission =
 90 function(allowedCallback) {
 91     var allowed = window.webkitNotifications.checkPermission() == 0;
 92     if (allowed) {
 93         allowedCallback();
 94     } else if (!ZmDesktopAlert.requestedPermission) {
 95         ZmDesktopAlert.requestedPermission = true; // Prevents multiple permission requests in one session.
 96         window.webkitNotifications.requestPermission(this._checkWebkitPermission.bind(this, allowedCallback));
 97     }
 98 };
 99 
100 ZmDesktopAlert.prototype._showWebkitNotification =
101 function(title, message, sticky) {
102 	sticky = sticky || false;
103     // Icon: I chose to use the favIcon because it's already overridable by skins.
104     // It's a little ugly though.
105     // change for bug#67359: Broken notification image in chrome browser
106     // //var icon = window.favIconUrl;
107 	var icon = skin.hints.notificationBanner;
108     var popup = window.webkitNotifications.createNotification(icon, title, message);
109     popup.show();
110 	popup.onclick = function() {popup.cancel();};
111     if (sticky) {
112         if (!ZmDesktopAlert.notificationArray) {
113             ZmDesktopAlert.notificationArray = [];
114         }
115         ZmDesktopAlert.notificationArray.push(popup);
116     }
117     else {
118         // Close the popup after 5 seconds.
119         setTimeout(popup.cancel.bind(popup), 5000);
120     }
121 };
122 
123 /* Checks if we have permission to use the notification api. If so, or when the user
124  * grants permission, allowedCallback is called.
125  */
126 ZmDesktopAlert.prototype._checkNotificationPermission = function(allowedCallback) {
127 	var allowed = window.Notification.permission === 'granted';
128 	if (allowed) {
129 		allowedCallback();
130 	} else if (!ZmDesktopAlert.requestedPermission) {
131 		ZmDesktopAlert.requestedPermission = true; // Prevents multiple permission requests in one session.
132 		// Currently, cannot directly call requestPermission.  Re-test when Chrome 37 is released
133 		//window.Notification.requestPermission(this._checkNotificationPermission.bind(this, allowedCallback));
134 		var requestCallback = this._checkNotificationPermission.bind(this, allowedCallback);
135 		this.requestRequestPermission(requestCallback);
136 	}
137 };
138 
139 // Chrome Notification only allows requesting permission in response to a user action, not a programmatic call.
140 // The issue may be fixed in Chrome 37, whenever that comes out.  See:
141 //   https://code.google.com/p/chromium/issues/detail?id=274284
142 ZmDesktopAlert.prototype.requestRequestPermission = function(requestCallback) {
143 	var msgDialog = appCtxt.getYesNoMsgDialog();
144 	var callback = 	this._doRequestPermission.bind(this, msgDialog, requestCallback);
145 	msgDialog.registerCallback(DwtDialog.YES_BUTTON, callback);
146 	msgDialog.setMessage(ZmMsg.notificationPermission, DwtMessageDialog.INFO_STYLE);
147 	msgDialog.popup();
148 };
149 
150 ZmDesktopAlert.prototype._doRequestPermission = function(msgDialog, requestCallback) {
151 	msgDialog.popdown();
152 	window.Notification.requestPermission(requestCallback);
153 }
154 
155 ZmDesktopAlert.prototype._showNotification = function(title, message, sticky) {
156 	var icon = skin.hints.notificationBanner;
157 
158 	var popup = new Notification(title, { body: message, icon: icon});
159 	//popup.show();
160 	popup.onclick = function() {popup.close();};
161 	if (sticky) {
162 		if (!ZmDesktopAlert.notificationArray) {
163 			ZmDesktopAlert.notificationArray = [];
164 		}
165 		ZmDesktopAlert.notificationArray.push(popup);
166 	}
167 	else {
168 		// Close the popup after 5 seconds.
169 		setTimeout(popup.close.bind(popup), 5000);
170 	}
171 };
172 
173 ZmDesktopAlert.prototype._notifyServiceCallback =
174 function(title, message, service) {
175 	try {
176 		service.show({ title: title, message: message }, function(){});
177 	} catch (err) {}
178 };
179 
180 /**
181  * Closes desktop notification if any during onbeforeunload event
182  */
183 ZmDesktopAlert.closeNotification =
184 function() {
185     var notificationArray = ZmDesktopAlert.notificationArray,
186         popup;
187 
188     if (notificationArray) {
189         while (popup = notificationArray.pop()) {
190             //notifications may be already closed by the user go for try catch
191             try {
192                 popup.cancel();
193             }
194             catch (e) {
195             }
196         }
197     }
198 };
199