error_report.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /* vim: set expandtab sw=4 ts=4 sts=4: */
  2. /**
  3. * general function, usually for data manipulation pages
  4. *
  5. */
  6. var ErrorReport = {
  7. /**
  8. * @var object stores the last exception info
  9. */
  10. _last_exception: null,
  11. /**
  12. * handles thrown error exceptions based on user preferences
  13. *
  14. * @return void
  15. */
  16. error_handler: function (exception) {
  17. // issue: 14359
  18. if (JSON.stringify(ErrorReport._last_exception) === JSON.stringify(exception)) {
  19. return;
  20. }
  21. if (exception.name === null || typeof(exception.name) === 'undefined') {
  22. exception.name = ErrorReport._extractExceptionName(exception);
  23. }
  24. ErrorReport._last_exception = exception;
  25. $.post('error_report.php', {
  26. ajax_request: true,
  27. server: PMA_commonParams.get('server'),
  28. get_settings: true,
  29. exception_type: 'js'
  30. }, function (data) {
  31. if (data.success !== true) {
  32. PMA_ajaxShowMessage(data.error, false);
  33. return;
  34. }
  35. if (data.report_setting === 'ask') {
  36. ErrorReport._showErrorNotification();
  37. } else if (data.report_setting === 'always') {
  38. report_data = ErrorReport._get_report_data(exception);
  39. post_data = $.extend(report_data, {
  40. send_error_report: true,
  41. automatic: true
  42. });
  43. $.post('error_report.php', post_data, function (data) {
  44. if (data.success === false) {
  45. // in the case of an error, show the error message returned.
  46. PMA_ajaxShowMessage(data.error, false);
  47. } else {
  48. PMA_ajaxShowMessage(data.message, false);
  49. }
  50. });
  51. }
  52. });
  53. },
  54. /**
  55. * Shows the modal dialog previewing the report
  56. *
  57. * @param exception object error report info
  58. *
  59. * @return void
  60. */
  61. _showReportDialog: function (exception) {
  62. var report_data = ErrorReport._get_report_data(exception);
  63. /* Remove the hidden dialogs if there are*/
  64. if ($('#error_report_dialog').length !== 0) {
  65. $('#error_report_dialog').remove();
  66. }
  67. var $div = $('<div id="error_report_dialog"></div>');
  68. $div.css('z-index', '1000');
  69. var button_options = {};
  70. button_options[PMA_messages.strSendErrorReport] = function () {
  71. var $dialog = $(this);
  72. var post_data = $.extend(report_data, {
  73. send_error_report: true,
  74. description: $('#report_description').val(),
  75. always_send: $('#always_send_checkbox')[0].checked
  76. });
  77. $.post('error_report.php', post_data, function (data) {
  78. $dialog.dialog('close');
  79. if (data.success === false) {
  80. // in the case of an error, show the error message returned.
  81. PMA_ajaxShowMessage(data.error, false);
  82. } else {
  83. PMA_ajaxShowMessage(data.message, 3000);
  84. }
  85. });
  86. };
  87. button_options[PMA_messages.strCancel] = function () {
  88. $(this).dialog('close');
  89. };
  90. $.post('error_report.php', report_data, function (data) {
  91. if (data.success === false) {
  92. // in the case of an error, show the error message returned.
  93. PMA_ajaxShowMessage(data.error, false);
  94. } else {
  95. // Show dialog if the request was successful
  96. $div
  97. .append(data.message)
  98. .dialog({
  99. title: PMA_messages.strSubmitErrorReport,
  100. width: 650,
  101. modal: true,
  102. buttons: button_options,
  103. close: function () {
  104. $(this).remove();
  105. }
  106. });
  107. }
  108. });
  109. },
  110. /**
  111. * Shows the small notification that asks for user permission
  112. *
  113. * @return void
  114. */
  115. _showErrorNotification: function () {
  116. ErrorReport._removeErrorNotification();
  117. var $div = $(
  118. '<div style="position:fixed;bottom:0;left:0;right:0;margin:0;' +
  119. 'z-index:1000" class="error" id="error_notification"></div>'
  120. ).append(
  121. PMA_getImage('s_error') + PMA_messages.strErrorOccurred
  122. );
  123. var $buttons = $('<div class="floatright"></div>');
  124. var button_html = '<button id="show_error_report">';
  125. button_html += PMA_messages.strShowReportDetails;
  126. button_html += '</button>';
  127. button_html += '<a id="change_error_settings">';
  128. button_html += PMA_getImage('s_cog', PMA_messages.strChangeReportSettings);
  129. button_html += '</a>';
  130. button_html += '<a href="#" id="ignore_error">';
  131. button_html += PMA_getImage('b_close', PMA_messages.strIgnore);
  132. button_html += '</a>';
  133. $buttons.html(button_html);
  134. $div.append($buttons);
  135. $div.appendTo(document.body);
  136. $(document).on('click', '#change_error_settings', ErrorReport._redirect_to_settings);
  137. $(document).on('click', '#show_error_report', ErrorReport._createReportDialog);
  138. $(document).on('click', '#ignore_error', ErrorReport._removeErrorNotification);
  139. },
  140. /**
  141. * Removes the notification if it was displayed before
  142. *
  143. * @return void
  144. */
  145. _removeErrorNotification: function (e) {
  146. if (e) {
  147. // don't remove the hash fragment by navigating to #
  148. e.preventDefault();
  149. }
  150. $('#error_notification').fadeOut(function () {
  151. $(this).remove();
  152. });
  153. },
  154. /**
  155. * Extracts Exception name from message if it exists
  156. *
  157. * @return String
  158. */
  159. _extractExceptionName: function (exception) {
  160. if (exception.message === null || typeof(exception.message) === 'undefined') {
  161. return '';
  162. }
  163. var reg = /([a-zA-Z]+):/;
  164. var regex_result = reg.exec(exception.message);
  165. if (regex_result && regex_result.length === 2) {
  166. return regex_result[1];
  167. }
  168. return '';
  169. },
  170. /**
  171. * Shows the modal dialog previewing the report
  172. *
  173. * @return void
  174. */
  175. _createReportDialog: function () {
  176. ErrorReport._removeErrorNotification();
  177. ErrorReport._showReportDialog(ErrorReport._last_exception);
  178. },
  179. /**
  180. * Redirects to the settings page containing error report
  181. * preferences
  182. *
  183. * @return void
  184. */
  185. _redirect_to_settings: function () {
  186. window.location.href = 'prefs_forms.php';
  187. },
  188. /**
  189. * Returns the report data to send to the server
  190. *
  191. * @param exception object exception info
  192. *
  193. * @return object
  194. */
  195. _get_report_data: function (exception) {
  196. if (exception && exception.stack && exception.stack.length) {
  197. for (var i = 0; i < exception.stack.length; i++) {
  198. var stack = exception.stack[i];
  199. if (stack.context && stack.context.length) {
  200. for (var j = 0; j < stack.context.length; j++) {
  201. if (stack.context[j].length > 80) {
  202. stack.context[j] = stack.context[j].substring(-1, 75) + '//...';
  203. }
  204. }
  205. }
  206. }
  207. }
  208. var report_data = {
  209. 'server': PMA_commonParams.get('server'),
  210. 'ajax_request': true,
  211. 'exception': exception,
  212. 'url': window.location.href,
  213. 'exception_type': 'js'
  214. };
  215. if (AJAX.scriptHandler._scripts.length > 0) {
  216. report_data.scripts = AJAX.scriptHandler._scripts.map(
  217. function (script) {
  218. return script;
  219. }
  220. );
  221. }
  222. return report_data;
  223. },
  224. /**
  225. * Wraps all global functions that start with PMA_
  226. *
  227. * @return void
  228. */
  229. wrap_global_functions: function () {
  230. for (var key in window) {
  231. if (key.indexOf('PMA_') === 0) {
  232. var global = window[key];
  233. if (typeof(global) === 'function') {
  234. window[key] = ErrorReport.wrap_function(global);
  235. }
  236. }
  237. }
  238. },
  239. /**
  240. * Wraps given function in error reporting code and returns wrapped function
  241. *
  242. * @param func function to be wrapped
  243. *
  244. * @return function
  245. */
  246. wrap_function: function (func) {
  247. if (!func.wrapped) {
  248. var new_func = function () {
  249. try {
  250. return func.apply(this, arguments);
  251. } catch (x) {
  252. TraceKit.report(x);
  253. }
  254. };
  255. new_func.wrapped = true;
  256. // Set guid of wrapped function same as original function, so it can be removed
  257. // See bug#4146 (problem with jquery draggable and sortable)
  258. new_func.guid = func.guid = func.guid || new_func.guid || jQuery.guid++;
  259. return new_func;
  260. } else {
  261. return func;
  262. }
  263. },
  264. /**
  265. * Automatically wraps the callback in AJAX.registerOnload
  266. *
  267. * @return void
  268. */
  269. _wrap_ajax_onload_callback: function () {
  270. var oldOnload = AJAX.registerOnload;
  271. AJAX.registerOnload = function (file, func) {
  272. func = ErrorReport.wrap_function(func);
  273. oldOnload.call(this, file, func);
  274. };
  275. },
  276. /**
  277. * Automatically wraps the callback in $.fn.on
  278. *
  279. * @return void
  280. */
  281. _wrap_$_on_callback: function () {
  282. var oldOn = $.fn.on;
  283. $.fn.on = function () {
  284. for (var i = 1; i <= 3; i++) {
  285. if (typeof(arguments[i]) === 'function') {
  286. arguments[i] = ErrorReport.wrap_function(arguments[i]);
  287. break;
  288. }
  289. }
  290. return oldOn.apply(this, arguments);
  291. };
  292. },
  293. /**
  294. * Wraps all global functions that start with PMA_
  295. * also automatically wraps the callback in AJAX.registerOnload
  296. *
  297. * @return void
  298. */
  299. set_up_error_reporting: function () {
  300. ErrorReport.wrap_global_functions();
  301. ErrorReport._wrap_ajax_onload_callback();
  302. ErrorReport._wrap_$_on_callback();
  303. }
  304. };
  305. AJAX.registerOnload('error_report.js', function () {
  306. TraceKit.report.subscribe(ErrorReport.error_handler);
  307. ErrorReport.set_up_error_reporting();
  308. ErrorReport.wrap_global_functions();
  309. });