/*
* Copyright 2012 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A error message dialog box.
*
* Ametys.log.ErrorDialog.display({
* title: 'An error occurred',
* text: 'An unknown error as occurred while processing the request',
* details: 'The component failed to connect to database...',
* category: 'Ametys.my.Component'
* });
*
* You can use this to use the class name automatically
* category: this.self.getName()
*/
Ext.define(
"Ametys.log.ErrorDialog",
{
singleton: true,
/**
* @private
* @property {Ametys.log.ErrorDialog[]} _stack The stack of running errors
*/
_stack: [],
/**
* @private
* Change the title depending on the number of errors
* @param {Ametys.window.DialogBox} dialogBox The dialog box to adapt
*/
_adaptTitle: function(dialogBox)
{
var nb = Ametys.log.ErrorDialog._stack.length - 1;
if (nb == 0)
{
dialogBox.setTitle(dialogBox.originalTitle);
var buttons = dialogBox.getDockedItems('toolbar[dock="bottom"]')[0];
buttons.items.get(0).setVisible(true);
buttons.items.get(1).setVisible(false);
}
else
{
dialogBox.setTitle(dialogBox.originalTitle + " - " + nb + " "+ "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_OTHERSERRORS}}");
var buttons = dialogBox.getDockedItems('toolbar[dock="bottom"]')[0];
buttons.items.get(0).setVisible(false);
buttons.items.get(1).setVisible(true);
}
},
/**
* Prepare the details string
* @param {String/Error} details The detailed text of the box. Hidden by default. Can be technical.
*/
_prepareDetails: function(details)
{
details = details || '';
var text = details.toString();
if (details.stack)
{
text += "\n" + details.stack.toString();
}
return text.toString()
// Convert text to html
.replace(/&/g, '&').replace(/</g, '<')
.replace(/\n?\n/g, '<br/>').replace(/\t/g, '    ')
// Convert text to link (assuming ':' or ')' or whitespace are ending the link even if they are valid link characters)
.replace(/(https?:\/\/[^\s/]+(\/[^\s:)]*)?)/g, '<a href="$1" target="_blank">$1</a>');
},
/**
* Creates and display directly an error message dialog box
* @param {Object} config The box to display
* @param {String} config.title The title of the box
* @param {String} config.text The main text of the box. Should be localized
* @param {String/Error} [config.details] The detailed text of the box. Hidden by default. Can be technical.
* @param {String} [config.category] The log category. Not logged if null.
*/
display: function(config) {
if (Ext.AbstractComponent.runningLayoutContext && Ext.AbstractComponent.runningLayoutContext.state == 0)
{
// The layout seems to be broken
Ext.AbstractComponent.runningLayoutContext.run();
Ext.AbstractComponent.runningLayoutContext = null;
window.setTimeout(function() { while (Ext.AbstractComponent.layoutSuspendCount > 0){Ext.resumeLayouts(true);} }, 1);
}
var title = config.title;
var text = config.text;
var details = config.details;
var category = config.category;
var centralMsg = Ext.create('Ext.Component', {
cls: 'error-dialog-text',
scrollable: true,
border: false,
html: text
});
var detailedMsg = Ext.create('Ext.Component', {
cls: 'error-dialog-details',
html: this._prepareDetails(details),
scrollable: true,
border: true,
hidden: true
});
if (category)
{
Ametys.log.LoggerFactory.getLoggerFor(category).error({
message: text,
details: details
});
}
var me = this;
var errorDialog = Ext.create ('Ametys.window.DialogBox', {
title: "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_ERROR_MSG}}" + title,
originalTitle: "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_ERROR_MSG}}" + title,
bodyPadding: '0',
width: 530,
scrollable: false,
iconCls: 'ametysicon-cross',
items: [ centralMsg, detailedMsg ],
closeAction: 'close',
closable: false,
defaultFocus: 0,
buttons: [
{
text: "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_OK}}",
handler: Ametys.log.ErrorDialog._okMessage,
scope: me
},
{
// A bug of extjs causes to have to set space characters. If not the menu zone is too big.
text: "     " + "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_SPLITBUTTON_OK}}" + "     ",
xtype: 'splitbutton',
menu:
{
items:
[{
text: "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_IGNOREERRORS}}",
handler: function() { window.setTimeout(Ext.bind(Ametys.log.ErrorDialog._okMessages, me), 10); }
}]
},
handler: Ametys.log.ErrorDialog._okMessage,
scope: me
},
{
text: "{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_DETAILS}} >>",
disabled: details ? false : true,
handler: function()
{
if (detailedMsg.hidden)
{
detailedMsg.show();
me._loadDetailedMsg(details, detailedMsg);
}
else
{
this.setText("{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_DETAILS}} >>");
detailedMsg.hide();
}
}
}
]
});
Ametys.log.ErrorDialog._stack.push(errorDialog);
if (Ametys.log.ErrorDialog._stack.length == 1)
{
errorDialog.show();
}
var currentlyDisplayedDialog = Ametys.log.ErrorDialog._stack[0];
Ametys.log.ErrorDialog._adaptTitle(currentlyDisplayedDialog);
},
/**
* Load the "details" message, by parsing the stacktrace
* @param {Object} details The error details
* @param {Ext.Component} detailedMsgCmp The component displaying the detailed message
* @private
*/
_loadDetailedMsg: function(details, detailedMsgCmp)
{
var dialogBox = Ametys.log.ErrorDialog._stack[0];
var buttons = dialogBox.getDockedItems('toolbar[dock="bottom"]')[0];
if (details.stack)
{
var opts = {
cancelCode: this.self.getName()
};
this._loadingDetails = true;
buttons.items.get(2).setDisabled(true);
buttons.items.get(2).setText("{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_DETAILS_LOADING}}");
Ametys.plugins.coreui.system.devmode.StacktraceHelper.unminifyStacktrace(details.stack, Ext.bind(this._loadDetailedMsgCb, this, [detailedMsgCmp, details], true), opts);
}
else
{
buttons.items.get(2).setText("<< {{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_DETAILS}}");
}
},
/**
* Callback after unminifying the callstack
* @param {String} response The unminified callstack, or null
* @param {Ext.Component} detailedMsgCmp The component displaying the detailed message
* @param {Object} details The error details
* @private
*/
_loadDetailedMsgCb: function(response, detailedMsgCmp, details)
{
if (this._loadingDetails)
{
this._loadingDetails = false;
var dialogBox = Ametys.log.ErrorDialog._stack[0];
var buttons = dialogBox.getDockedItems('toolbar[dock="bottom"]')[0];
buttons.items.get(2).setDisabled(false);
buttons.items.get(2).setText("<< {{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_DETAILS}}");
if (response && details.stack)
{
details.stack = response;
detailedMsgCmp.setHtml(this._prepareDetails(details));
}
}
},
/**
* Cancel any pending message details loading request
* @private
*/
_cancelLoadingDetails: function()
{
if (this._loadingDetails === true)
{
this._loadingDetails = false;
Ametys.data.ServerComm.cancel(this.self.getName());
if (Ametys.log.ErrorDialog._stack.length > 0)
{
var dialogBox = Ametys.log.ErrorDialog._stack[0];
var buttons = dialogBox.getDockedItems('toolbar[dock="bottom"]')[0];
buttons.items.get(0).setDisabled(false);
buttons.items.get(1).setDisabled(false);
buttons.items.get(2).setDisabled(false);
buttons.items.get(2).setText("{{i18n PLUGINS_CORE_UI_MSG_ERRORDIALOG_DETAILS}} >>");
}
}
},
/**
* @private
* Validate current message
*/
_okMessage: function()
{
this._cancelLoadingDetails();
var currentErrorDialog = Ametys.log.ErrorDialog._stack[0];
currentErrorDialog.close();
Ext.Array.remove(Ametys.log.ErrorDialog._stack, currentErrorDialog);
if (Ametys.log.ErrorDialog._stack.length != 0)
{
var nextErrorDialog = Ametys.log.ErrorDialog._stack[0];
nextErrorDialog.show();
Ametys.log.ErrorDialog._adaptTitle(nextErrorDialog);
}
},
/**
* @private
* Validate all messages
*/
_okMessages: function()
{
this._cancelLoadingDetails();
if (Ametys.log.ErrorDialog._stack.length > 0)
{
Ametys.log.ErrorDialog._stack[0].close();
}
Ametys.log.ErrorDialog._stack = [];
}
}
);