001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.ui; 029 030import org.opencms.gwt.CmsRpcException; 031import org.opencms.gwt.client.Messages; 032import org.opencms.gwt.client.rpc.CmsLog; 033import org.opencms.gwt.client.ui.css.I_CmsConstantsBundle; 034import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 035import org.opencms.gwt.client.util.CmsClientStringUtil; 036import org.opencms.gwt.client.util.CmsDomUtil; 037 038import java.util.Date; 039import java.util.HashSet; 040import java.util.Set; 041 042import com.google.gwt.dom.client.Style.Unit; 043import com.google.gwt.event.dom.client.ClickEvent; 044import com.google.gwt.event.dom.client.ClickHandler; 045import com.google.gwt.event.logical.shared.OpenEvent; 046import com.google.gwt.event.logical.shared.OpenHandler; 047import com.google.gwt.user.client.ui.FlowPanel; 048import com.google.gwt.user.client.ui.HTML; 049import com.google.gwt.user.client.ui.Panel; 050 051/** 052 * Provides a generic error dialog.<p> 053 * 054 * @since 8.0.0 055 */ 056public class CmsErrorDialog extends CmsPopup { 057 058 /** The active error dialog ids. */ 059 private static Set<String> m_activeErrorDialogIds = new HashSet<String>(); 060 061 /** The stack trace line break. */ 062 private static final String LINE_BREAK = "\n"; 063 064 /** The 'close' button. */ 065 private CmsPushButton m_closeButton; 066 067 /** The details fieldset. */ 068 private CmsFieldSet m_detailsFieldset; 069 070 /** String which identifies the error dialog. */ 071 private String m_errorDialogId; 072 073 /** The message HTML. */ 074 private CmsMessageWidget m_messageWidget; 075 076 /** 077 * Constructor.<p> 078 * 079 * @param message the error message 080 * @param details the error details, will be 'pre' formatted 081 */ 082 public CmsErrorDialog(String message, String details) { 083 084 super(Messages.get().key(Messages.GUI_ERROR_0), WIDE_WIDTH); 085 m_errorDialogId = new Date() + " " + message; 086 setAutoHideEnabled(false); 087 setModal(true); 088 setGlassEnabled(true); 089 addDialogClose(null); 090 m_closeButton = new CmsPushButton(); 091 m_closeButton.setText(Messages.get().key(Messages.GUI_CLOSE_0)); 092 m_closeButton.setUseMinWidth(true); 093 m_closeButton.addClickHandler(new ClickHandler() { 094 095 /** 096 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 097 */ 098 public void onClick(ClickEvent event) { 099 100 onClose(); 101 } 102 }); 103 addButton(m_closeButton); 104 105 Panel content = new FlowPanel(); 106 m_messageWidget = createMessageWidget(message); 107 content.add(m_messageWidget); 108 if (details != null) { 109 // prepend the message 110 details = message + LINE_BREAK + LINE_BREAK + details; 111 m_detailsFieldset = createDetailsFieldSet(details); 112 m_detailsFieldset.addOpenHandler(new OpenHandler<CmsFieldSet>() { 113 114 /** 115 * On open.<p> 116 * 117 * @param event the open event 118 */ 119 public void onOpen(OpenEvent<CmsFieldSet> event) { 120 121 center(); 122 } 123 }); 124 content.add(m_detailsFieldset); 125 m_detailsFieldset.setVisible(false); 126 CmsPushButton detailsB = new CmsPushButton(); 127 detailsB.setText(Messages.get().key(Messages.GUI_DETAILS_0)); 128 detailsB.setUseMinWidth(true); 129 detailsB.addClickHandler(new ClickHandler() { 130 131 public void onClick(ClickEvent event) { 132 133 toggleDetails(); 134 } 135 }); 136 addButton(detailsB, 0); 137 if (CmsDomUtil.isCopyToClipboardSupported()) { 138 final String id = "details" + CmsClientStringUtil.randomUUID(); 139 m_detailsFieldset.getElement().setId(id); 140 CmsPushButton copy = new CmsPushButton(); 141 copy.setText(Messages.get().key(Messages.GUI_COPY_TO_CLIPBOARD_0)); 142 copy.setUseMinWidth(true); 143 copy.addClickHandler(new ClickHandler() { 144 145 public void onClick(ClickEvent event) { 146 147 CmsDomUtil.copyToClipboard("#" + id + " .gwt-HTML"); 148 } 149 }); 150 copy.getElement().getStyle().setFloat(com.google.gwt.dom.client.Style.Float.LEFT); 151 copy.getElement().getStyle().setMarginLeft(0, Unit.PX); 152 copy.setTitle(Messages.get().key(Messages.GUI_COPY_TO_CLIPBOARD_DESCRIPTION_0)); 153 addButton(copy, 0); 154 } 155 } 156 setMainContent(content); 157 } 158 159 /** 160 * Handles the exception by logging the exception to the server log and displaying an error dialog on the client.<p> 161 * 162 * @param message the error message 163 * @param t the throwable 164 */ 165 public static void handleException(String message, Throwable t) { 166 167 StackTraceElement[] trace; 168 String className; 169 if (t instanceof CmsRpcException) { 170 CmsRpcException ex = (CmsRpcException)t; 171 trace = ex.getOriginalStackTrace(); 172 className = ex.getOriginalClassName(); 173 } else { 174 message = CmsClientStringUtil.getMessage(t); 175 trace = t.getStackTrace(); 176 className = t.getClass().getName(); 177 } 178 // send the ticket to the server 179 String ticket = CmsLog.log(message + LINE_BREAK + CmsClientStringUtil.getStackTraceAsString(trace, LINE_BREAK)); 180 181 String errorMessage = message == null 182 ? className + ": " + Messages.get().key(Messages.GUI_NO_DESCIPTION_0) 183 : message; 184 errorMessage += LINE_BREAK + Messages.get().key(Messages.GUI_REASON_0) + ":" + t; 185 186 String details = Messages.get().key(Messages.GUI_TICKET_MESSAGE_3, ticket, className, message) 187 + CmsClientStringUtil.getStackTraceAsString(trace, LINE_BREAK); 188 new CmsErrorDialog(errorMessage, details).center(); 189 } 190 191 /** 192 * Handles the exception by logging the exception to the server log and displaying an error dialog on the client.<p> 193 * 194 * @param t the throwable 195 */ 196 public static void handleException(Throwable t) { 197 198 String message; 199 StackTraceElement[] trace; 200 String cause = null; 201 String className; 202 if (t instanceof CmsRpcException) { 203 CmsRpcException ex = (CmsRpcException)t; 204 message = ex.getOriginalMessage(); 205 trace = ex.getOriginalStackTrace(); 206 cause = ex.getOriginalCauseMessage(); 207 className = ex.getOriginalClassName(); 208 } else { 209 message = CmsClientStringUtil.getMessage(t); 210 trace = t.getStackTrace(); 211 if (t.getCause() != null) { 212 cause = CmsClientStringUtil.getMessage(t.getCause()); 213 } 214 className = t.getClass().getName(); 215 } 216 // send the ticket to the server 217 String ticket = CmsLog.log(message + LINE_BREAK + CmsClientStringUtil.getStackTraceAsString(trace, LINE_BREAK)); 218 219 String errorMessage = message == null 220 ? className + ": " + Messages.get().key(Messages.GUI_NO_DESCIPTION_0) 221 : message; 222 if (cause != null) { 223 errorMessage += LINE_BREAK + Messages.get().key(Messages.GUI_REASON_0) + ":" + cause; 224 } 225 226 String details = Messages.get().key(Messages.GUI_TICKET_MESSAGE_3, ticket, className, message) 227 + CmsClientStringUtil.getStackTraceAsString(trace, LINE_BREAK); 228 new CmsErrorDialog(errorMessage, details).center(); 229 } 230 231 /** 232 * Checks if any error dialogs are showing.<p> 233 * 234 * @return true if any error dialogs are showing 235 */ 236 public static boolean isShowingErrorDialogs() { 237 238 return m_activeErrorDialogIds.size() > 0; 239 } 240 241 /** 242 * @see org.opencms.gwt.client.ui.CmsPopup#center() 243 */ 244 @Override 245 public void center() { 246 247 m_activeErrorDialogIds.add(m_errorDialogId); 248 show(); 249 super.center(); 250 251 } 252 253 /** 254 * @see org.opencms.gwt.client.ui.CmsPopup#hide() 255 */ 256 @Override 257 public void hide() { 258 259 m_activeErrorDialogIds.remove(m_errorDialogId); 260 super.hide(); 261 } 262 263 /** 264 * @see org.opencms.gwt.client.ui.CmsPopup#show() 265 */ 266 @Override 267 public void show() { 268 269 m_activeErrorDialogIds.add(m_errorDialogId); 270 super.show(); 271 onShow(); 272 } 273 274 /** 275 * Executed on 'close' click. <p> 276 */ 277 protected void onClose() { 278 279 m_closeButton.setEnabled(false); 280 hide(); 281 } 282 283 /** 284 * Toggles the details visibility.<p> 285 */ 286 void toggleDetails() { 287 288 if (m_detailsFieldset != null) { 289 m_detailsFieldset.setVisible(!m_detailsFieldset.isVisible()); 290 center(); 291 } 292 } 293 294 /** 295 * Creates a field-set containing the error details.<p> 296 * 297 * @param details the error details 298 * 299 * @return the field-set widget 300 */ 301 private CmsFieldSet createDetailsFieldSet(String details) { 302 303 CmsFieldSet fieldset = new CmsFieldSet(); 304 fieldset.addStyleName(I_CmsLayoutBundle.INSTANCE.errorDialogCss().details()); 305 fieldset.setLegend(Messages.get().key(Messages.GUI_LABEL_STACKTRACE_0)); 306 fieldset.addContent(new HTML("<pre>" + details + "</pre>")); 307 fieldset.setOpen(true); 308 return fieldset; 309 } 310 311 /** 312 * Creates the message HTML widget containing error icon and message.<p> 313 * 314 * @param message the message 315 * 316 * @return the HTML widget 317 */ 318 private CmsMessageWidget createMessageWidget(String message) { 319 320 CmsMessageWidget widget = new CmsMessageWidget(); 321 widget.setIcon(FontOpenCms.ERROR, I_CmsConstantsBundle.INSTANCE.css().colorError()); 322 widget.setMessageHtml(message); 323 return widget; 324 } 325 326 /** 327 * Checks the available space and sets max-height to the details field-set. 328 */ 329 private void onShow() { 330 331 if (m_detailsFieldset != null) { 332 m_detailsFieldset.getContentPanel().getElement().getStyle().setPropertyPx( 333 "maxHeight", 334 getAvailableHeight(m_messageWidget.getOffsetHeight())); 335 } 336 } 337}