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 defines the Zimbra error dialog.
 27  *
 28  */
 29 
 30 /**
 31  * Creates an error dialog.
 32  * @class
 33  * Creates an error dialog which will have a "Send Error Report" button.
 34  * A normal {@link DwtMessageDialog} with a "Send Error Report" button that will post user info to the 
 35  * server when clicked.
 36  * 
 37  * @param	{Object}	parent		the parent
 38  * @param	{Hash}		msgs		a hash of messages
 39  * @param	{String}	msgs.showDetails		the show details message
 40  * @param	{String}	msgs.hideDetails		the hide details message
 41  * 
 42  * @extends DwtMessageDialog
 43  */
 44 ZmErrorDialog = function(parent, msgs) {
 45 
 46 	// go ahead and cache the navigator and subject info now (since it should never change)		
 47 	this._strNav = this._getNavigatorInfo();
 48 	this._subjPfx = this._getSubjectPrefix();
 49 
 50 	var reportButton = new DwtDialog_ButtonDescriptor(ZmErrorDialog.REPORT_BUTTON, msgs.report, DwtDialog.ALIGN_LEFT);
 51 	var detailButton = new DwtDialog_ButtonDescriptor(ZmErrorDialog.DETAIL_BUTTON, msgs.showDetails, DwtDialog.ALIGN_LEFT);
 52 	DwtMessageDialog.call(this, {parent:parent, extraButtons:[reportButton, detailButton], id:"ErrorDialog"});
 53 
 54 	this.registerCallback(ZmErrorDialog.REPORT_BUTTON, this._reportCallback, this);
 55 	this.registerCallback(ZmErrorDialog.DETAIL_BUTTON, this.showDetail, this);
 56 	
 57 	this._showDetailsMsg = msgs.showDetails;
 58 	this._hideDetailsMsg = msgs.hideDetails;
 59 
 60 	this._setAllowSelection();
 61 };
 62 
 63 ZmErrorDialog.prototype = new DwtMessageDialog;
 64 ZmErrorDialog.prototype.constructor = ZmErrorDialog;
 65 
 66 /**
 67  * Returns a string representation of the object.
 68  * 
 69  * @return		{String}		a string representation of the object
 70  */
 71 ZmErrorDialog.prototype.toString =
 72 function() {
 73 	return "ZmErrorDialog";
 74 };
 75 
 76 //
 77 // Consts
 78 //
 79 
 80 ZmErrorDialog.REPORT_BUTTON = "Report";
 81 ZmErrorDialog.DETAIL_BUTTON = "Detail";
 82 ZmErrorDialog.DEFAULT_REPORT_URL = "//www.zimbra.com/e/";
 83 
 84 //
 85 // Data
 86 //
 87 
 88 ZmErrorDialog.prototype._detailsVisible = false;
 89 ZmErrorDialog.prototype.CONTROLS_TEMPLATE = "zimbra.Widgets#ZmErrorDialogControls";
 90 
 91 //
 92 // Public methods
 93 //
 94 
 95 /**
 96  * Resets the dialog.
 97  * 
 98  */
 99 ZmErrorDialog.prototype.reset =
100 function() {
101 	this.setDetailString();
102 	DwtMessageDialog.prototype.reset.call(this);
103 };
104 
105 /**
106 * Sets the text to display when the "Show Details" button is pressed.
107 *
108 * @param {String}	text	the detail text
109 */
110 ZmErrorDialog.prototype.setDetailString = 
111 function(text) {
112 	if (!(this._button[ZmErrorDialog.DETAIL_BUTTON])) { return; }
113 
114 	this._button[ZmErrorDialog.DETAIL_BUTTON].setVisible(text != null);
115 	this._detailStr = text;
116 };
117 
118 /**
119  * Sets the message style (info/warning/critical) and content.
120  *
121  * @param {String}	msgStr		the message text
122  * @param {String}	detailStr	the detail text
123  * @param {constant}	style		the style (see {@link DwtMessageDialog} <code>_STYLE</code> constants)
124  * @param {String}	title		the dialog box title
125  */
126 ZmErrorDialog.prototype.setMessage =
127 function(msgStr, detailStr, style, title) {
128 	this._msgStr = msgStr;
129 	this.setDetailString(detailStr);
130 	this._msgStyle = style;
131 	this._msgTitle = title;
132 
133 	// clear the 'detailsVisible' flag and reset the title of the 'showDetails' button
134 	this._detailsVisible = false;
135 	this._button[ZmErrorDialog.DETAIL_BUTTON].setText(this._showDetailsMsg);
136 	
137 	// Set the content, enveloped
138 	this._updateContent();
139 };
140 
141 /**
142  * Sets/updates the content
143  */
144 ZmErrorDialog.prototype._updateContent = 
145 function() {
146 	var data = {
147 		message: this._msgStr,
148 		detail: this._detailStr,
149 		showDetails: this._detailsVisible
150 	};
151 	var html = AjxTemplate.expand("zimbra.Widgets#ZmErrorDialogContent", data);
152 	this.setSize(Dwt.CLEAR, this._detailsVisible ? "300" : Dwt.CLEAR);
153 	DwtMessageDialog.prototype.setMessage.call(this, html, this._msgStyle, this._msgTitle);
154 };
155 
156 /**
157  * Pops-up the error dialog.
158  * 
159  * @param {Object}	loc				the desired location
160  * @param {Boolean}	hideReportButton	if <code>true</code>, do not show "Send Error Report" button
161  * 
162  */
163 ZmErrorDialog.prototype.popup =
164 function(loc, hideReportButton) {
165 	if (hideReportButton) {
166 		this.setButtonVisible(ZmErrorDialog.REPORT_BUTTON, false);
167 	}
168 	DwtMessageDialog.prototype.popup.call(this, loc);
169 };
170 
171 /**
172  * Pops-down the dialog.
173  * 
174  */
175 ZmErrorDialog.prototype.popdown =
176 function() {
177 	DwtMessageDialog.prototype.popdown.call(this);
178 
179 	// reset dialog
180 	this.setSize(Dwt.CLEAR, Dwt.CLEAR);
181 	this.setButtonVisible(ZmErrorDialog.REPORT_BUTTON, true);
182 };
183 
184 //
185 // Protected methods
186 //
187 /**
188  * @private
189  */
190 ZmErrorDialog.prototype._getNavigatorInfo =
191 function() {
192 	var strNav = [];
193 	var idx = 0;
194 
195 	// Add the url
196 	strNav[idx++] = "\n\n";
197 	strNav[idx++] = "href: ";
198 	strNav[idx++] = location.href;
199 	strNav[idx++] = "\n";
200 
201 	for (var i in navigator) {
202 		// Skip functions
203 		if(typeof navigator[i] == "function") {continue;}
204 		if(typeof navigator[i] == "unknown") {continue;}	// IE7
205 		if(AjxEnv.isIE && i === "mimeTypes") {continue;}
206 		strNav[idx++] = i + ": " + navigator[i] + "\n";
207 	}
208 	return strNav.join("");
209 };
210 
211 /**
212  * @private
213  */
214 ZmErrorDialog.prototype._getSubjectPrefix = 
215 function() {
216 	var strSubj = [];
217 	var idx = 0;
218 
219 	strSubj[idx++] = "ER: ";
220 
221 	if (AjxEnv.isIE) 				strSubj[idx++] = "IE ";
222 	else if (AjxEnv.isFirefox)		strSubj[idx++] = "FF ";
223 	else if (AjxEnv.isMozilla)		strSubj[idx++] = "MOZ ";
224 	else if (AjxEnv.isSafari)		strSubj[idx++] = "SAF ";
225 	else if (AjxEnv.isOpera)		strSubj[idx++] = "OPE ";
226 	else							strSubj[idx++] = "UKN ";
227 
228 	if (AjxEnv.isWindows)			strSubj[idx++] = "WIN ";
229 	else if (AjxEnv.isLinux)		strSubj[idx++] = "LNX ";
230 	else if (AjxEnv.isMac)			strSubj[idx++] = "MAC ";
231 	else							strSubj[idx++] = "UNK ";
232 
233 	strSubj[idx++] = appCtxt.get(ZmSetting.CLIENT_VERSION) + " ";
234 	return strSubj.join("");
235 };
236 
237 /**
238  * @private
239  */
240 ZmErrorDialog.prototype._getUserPrefs = 
241 function() {
242 	var currSearch = appCtxt.getCurrentSearch();
243 	var strPrefs = [];
244 	var idx = 0;
245 
246 	// Add username and current search
247 	strPrefs[idx++] = "\n\n";
248 	strPrefs[idx++] = "username: ";
249 	strPrefs[idx++] = appCtxt.get(ZmSetting.USERNAME);
250 	strPrefs[idx++] = "\n";
251 	if (currSearch) {
252 		strPrefs[idx++] = "currentSearch: ";
253 		strPrefs[idx++] = currSearch.query;
254 		strPrefs[idx++] = "\n";
255 	}
256 	for (var i in ZmSetting.INIT) {
257 		if (ZmSetting.INIT[i][0]) {
258 			strPrefs[idx++] = ZmSetting.INIT[i][0];
259 			strPrefs[idx++] = ": ";
260 			strPrefs[idx++] = ("" + ZmSetting.INIT[i][3]);
261 			strPrefs[idx++] = "\n";
262 		}
263 	}
264 	return strPrefs.join("");
265 };
266 
267 // Callbacks
268 
269 /**
270  * @private
271  */
272 ZmErrorDialog.prototype._reportCallback =
273 function() {
274 	this._iframe = document.createElement("iframe");
275 	this._iframe.style.width = this._iframe.style.height = 0;
276 	this._iframe.style.visibility = "hidden";
277 
278 	var contentDiv = this._getContentDiv();
279 	contentDiv.appendChild(this._iframe);
280 
281 	var strPrefs = this._getUserPrefs();
282 	var formId = Dwt.getNextId();
283 
284 	// generate html form for submission via POST
285 	var html = [];
286 	var idx = 0;
287 	var subject = this._subjPfx + this._detailStr.substring(0,40);
288 	var scheme = (location.protocol == 'https:') ? "https:" : "http:";
289 	html[idx++] = "<html><head></head><body><form id='";
290 	html[idx++] = formId;
291 	html[idx++] = "' method='POST' action='";
292 	html[idx++] = scheme;
293 	html[idx++] = appCtxt.get(ZmSetting.ERROR_REPORT_URL) || ZmErrorDialog.DEFAULT_REPORT_URL;
294 	html[idx++] = "'>";
295 	html[idx++] = "<textarea name='details'>";
296 	html[idx++] = this._detailStr;
297 	html[idx++] = "version - ";
298 	html[idx++] = appCtxt.get(ZmSetting.CLIENT_VERSION);
299 	html[idx++] = "\n";
300 	html[idx++] = "release - ";
301 	html[idx++] = appCtxt.get(ZmSetting.CLIENT_RELEASE);
302 	html[idx++] = "\n";
303 	html[idx++] = "date - ";
304 	html[idx++] = appCtxt.get(ZmSetting.CLIENT_DATETIME);
305 	html[idx++] = "</textarea><textarea name='navigator'>";
306 	html[idx++] = this._strNav;
307 	html[idx++] = "</textarea><textarea name='prefs'>";
308 	html[idx++] = strPrefs;
309 	html[idx++] = "</textarea><textarea name='subject'>";
310 	html[idx++] = subject;
311 	html[idx++] = "</textarea></form></body></html>";
312 
313 	var idoc = Dwt.getIframeDoc(this._iframe);
314 	idoc.open();
315 	idoc.write(html.join(""));
316 	idoc.close();
317 
318 	// submit the form!
319 	var form = idoc.getElementById(formId);
320 	if (form) {
321 		form.submit();
322 		appCtxt.setStatusMsg(ZmMsg.errorReportSent);
323 	}
324 
325 	this.popdown();
326 };
327 
328 /**
329  * Displays the detail text
330  */
331 ZmErrorDialog.prototype.showDetail = 
332 function() {
333 	this._detailsVisible = !this._detailsVisible;
334 	this._updateContent();
335 	this._button[ZmErrorDialog.DETAIL_BUTTON].setText(this._detailsVisible ? this._hideDetailsMsg : this._showDetailsMsg);
336 };
337