001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: *
013: * @created Jul 22, 2005 @author James Dixon
014: *
015: */
016:
017: package org.pentaho.core.util;
018:
019: import java.text.NumberFormat;
020: import java.util.ArrayList;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Set;
024:
025: import javax.servlet.http.HttpServletRequest;
026: import javax.servlet.http.HttpSession;
027:
028: import org.pentaho.commons.connection.IPentahoResultSet;
029: import org.pentaho.core.runtime.IRuntimeContext;
030: import org.pentaho.core.session.BaseSession;
031: import org.pentaho.core.session.IPentahoSession;
032: import org.pentaho.core.session.PentahoHttpSession;
033: import org.pentaho.core.solution.ActionResource;
034: import org.pentaho.core.solution.IActionResource;
035: import org.pentaho.core.system.PentahoMessenger;
036: import org.pentaho.core.system.PentahoSystem;
037: import org.pentaho.messages.Messages;
038: import org.pentaho.messages.util.LocaleHelper;
039: import org.pentaho.util.IVersionHelper;
040:
041: public class UIUtil implements IUITemplater {
042: //TODO sbarkdull, find occurances in code of these mime types, and where appropriate, use these static final value
043: public static final String HTML_MIME_TYPE = "text/html"; //$NON-NLS-1$
044:
045: public static final String TEXT_MIME_TYPE = "text/plain"; //$NON-NLS-1$
046:
047: public static final int MAX_RESULT_THRESHOLD = 100;
048:
049: private static String footerTemplate = null;
050:
051: private static final String FOOTER_TEMPLATE_FILENAME = "template-footer.html"; //$NON-NLS-1$
052:
053: public String processTemplate(String template, String title,
054: String content, IPentahoSession session) {
055:
056: template = processTemplate(template, title, session);
057: template = template.replaceFirst("\\{content\\}", content); //$NON-NLS-1$
058:
059: return template;
060: }
061:
062: /*
063: * TODO: This needs to be architected to be more performant
064: */
065: public String processTemplate(String template, String title,
066: IPentahoSession session) {
067:
068: if (footerTemplate == null) {
069: footerTemplate = getTemplate(FOOTER_TEMPLATE_FILENAME,
070: session);
071: }
072: template = template
073: .replaceFirst("\\{footer\\}", footerTemplate); //$NON-NLS-1$
074: template = template.replaceAll("\\{title\\}", title); //$NON-NLS-1$
075: template = template.replaceAll(
076: "\\{home\\}", Messages.getString("UI.USER_HOME")); //$NON-NLS-1$ //$NON-NLS-2$
077: template = template
078: .replaceAll(
079: "\\{navigate\\}", Messages.getString("UI.USER_NAVIGATE")); //$NON-NLS-1$ //$NON-NLS-2$
080: template = template
081: .replaceAll(
082: "\\{solutions\\}", Messages.getString("UI.USER_SOLUTIONS")); //$NON-NLS-1$ //$NON-NLS-2$
083: template = template
084: .replaceAll(
085: "\\{file-cache\\}", Messages.getString("UI.USER_FILE_CACHE")); //$NON-NLS-1$ //$NON-NLS-2$
086: template = template
087: .replaceAll(
088: "\\{new-content\\}", Messages.getString("UI.USER_NEW_CONTENT")); //$NON-NLS-1$ //$NON-NLS-2$
089:
090: template = template.replaceAll(
091: "\\{admin\\}", Messages.getString("UI.USER_ADMIN")); //$NON-NLS-1$ //$NON-NLS-2$
092: template = template.replaceAll(
093: "\\{about\\}", Messages.getString("UI.USER_ABOUT")); //$NON-NLS-1$ //$NON-NLS-2$
094: template = template
095: .replaceAll(
096: "\\{pentaho.org\\}", Messages.getString("UI.USER_PENTAHO.ORG")); //$NON-NLS-1$ //$NON-NLS-2$
097: template = template.replaceAll(
098: "\\{close\\}", Messages.getString("UI.USER_CLOSE")); //$NON-NLS-1$ //$NON-NLS-2$
099: template = template.replaceAll(
100: "\\{forums\\}", Messages.getString("UI.USER_FORUMS")); //$NON-NLS-1$ //$NON-NLS-2$
101: template = template.replaceAll(
102: "\\{demos\\}", Messages.getString("UI.USER_DEMOS")); //$NON-NLS-1$ //$NON-NLS-2$
103: template = template
104: .replaceAll(
105: "\\{downloads\\}", Messages.getString("UI.USER_DOWNLOADS")); //$NON-NLS-1$ //$NON-NLS-2$
106: template = template
107: .replaceAll(
108: "text/html; charset=utf-8", "text/html; charset=" + LocaleHelper.getSystemEncoding()); //$NON-NLS-1$//$NON-NLS-2$
109: template = template
110: .replaceAll(
111: "\\{text-direction\\}", LocaleHelper.getTextDirection()); //$NON-NLS-1$
112: template = template.replaceAll(
113: "\\{logout\\}", Messages.getString("UI.USER_LOGOUT")); //$NON-NLS-1$ //$NON-NLS-2$
114: template = template.replaceAll(
115: "\\{portal\\}", Messages.getString("UI.USER_PORTAL")); //$NON-NLS-1$ //$NON-NLS-2$
116: IVersionHelper versionHelper = PentahoSystem
117: .getVersionHelper(session);
118: template = template.replaceAll(
119: "\\{system\\}", PentahoSystem.getSystemName()); //$NON-NLS-1$
120: template = template
121: .replaceAll(
122: "\\{version\\}", "Version: " + versionHelper.getVersionInformation()); //$NON-NLS-1$ //$NON-NLS-2$
123: template = template
124: .replaceAll(
125: "\\{isLoggedIn\\}", session.isAuthenticated() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
126: template = template
127: .replaceAll(
128: "\\{background-alert\\}", session.getBackgroundExecutionAlert() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
129: template = template.replaceAll("\\{header-content\\}", ""); //$NON-NLS-1$ //$NON-NLS-2$
130: template = template.replaceAll("\\{body-tag\\}", ""); //$NON-NLS-1$ //$NON-NLS-2$
131: template = template
132: .replaceAll(
133: "\\{isAdmin\\}", PentahoSystem.isAdministrator(session) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
134:
135: return template;
136: }
137:
138: public String getTemplate(String templateName,
139: IPentahoSession session) {
140:
141: ActionResource resource = new ActionResource(
142: "", IActionResource.SOLUTION_FILE_RESOURCE, "text/xml", "system/custom/" + templateName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
143:
144: String template = null;
145: try {
146: template = PentahoSystem.getSolutionRepository(session)
147: .getResourceAsString(resource);
148: } catch (Throwable t) {
149: }
150:
151: if (template == null) {
152: return Messages
153: .getString(
154: "UI.ERROR_0001_BAD_TEMPLATE", resource.getAddress()); //$NON-NLS-1$
155: } else {
156: return template;
157: }
158: }
159:
160: public String[] breakTemplate(String templateName, String title,
161: IPentahoSession session) {
162: String template = getTemplate(templateName, session);
163: return breakTemplateString(template, title, session);
164: }
165:
166: public String[] breakTemplateString(String template, String title,
167: IPentahoSession session) {
168: String token = "{content}"; //$NON-NLS-1$
169: template = processTemplate(template, title, session);
170: int index = template.indexOf(token);
171: if (index == -1) {
172: return new String[] { template };
173: } else {
174: String sections[] = new String[2];
175: sections[0] = template.substring(0, index);
176: sections[1] = template.substring(index + token.length());
177: return sections;
178: }
179: }
180:
181: public static IPentahoSession getPentahoSession(
182: HttpServletRequest request) {
183:
184: HttpSession session = request.getSession();
185: IPentahoSession userSession = (IPentahoSession) session
186: .getAttribute(BaseSession.PENTAHO_SESSION_KEY);
187: LocaleHelper.setLocale(request.getLocale());
188: if (userSession != null) {
189: return userSession;
190: }
191: userSession = new PentahoHttpSession(request.getRemoteUser(),
192: request.getSession(), request.getLocale());
193: // TODO sbarkdull delete following line?
194: LocaleHelper.setLocale(request.getLocale());
195:
196: session.setAttribute(BaseSession.PENTAHO_SESSION_KEY,
197: userSession);
198: return userSession;
199: }
200:
201: public static void formatErrorMessage(String mimeType,
202: String title, String message, StringBuffer messageBuffer) {
203: ArrayList messages = new ArrayList(1);
204: messages.add(message);
205: formatErrorMessage(mimeType, title, messages, messageBuffer);
206: }
207:
208: /**
209: * If PentahoMessenger.getUserString("ERROR") returns the string:
210: * "Error: {0} ({1})" (which is the case for English)
211: * Find the substring before the first "{". In this case, that
212: * would be: "Error: ".
213: * Return the first string in the messages list that contains
214: * the string "Error: ". If no string in the list contains
215: * "Error: ", return null;
216: * @param messages
217: * @return
218: */
219: public static String getFirstError(List messages) {
220: // returns something like: "Error: {0} ({1})"
221: String errorStart = PentahoMessenger.getUserString("ERROR"); //$NON-NLS-1$
222: int pos = errorStart.indexOf('{');
223: if (pos != -1) {
224: errorStart = errorStart.substring(0, pos);
225: }
226: Iterator messageIterator = messages.iterator();
227: while (messageIterator.hasNext()) {
228: String message = (String) messageIterator.next();
229: if (message.indexOf(errorStart) == 0) {
230: return message;
231: }
232: }
233: return null;
234: }
235:
236: public static void formatErrorMessage(String mimeType,
237: String title, List messages, StringBuffer messageBuffer) {
238: // TODO make this template or XSL based
239: if ("text/html".equals(mimeType)) { //$NON-NLS-1$
240: messageBuffer
241: .append("<html><head><title>") //$NON-NLS-1$
242: .append(
243: Messages
244: .getString("UIUtil.ERROR_PAGE_TITLE")) //$NON-NLS-1$
245: .append(
246: "</title><link rel=\"stylesheet\" type=\"text/css\" href=\"/pentaho-style/active/default.css\"></head>") //$NON-NLS-1$
247: .append("<body dir=\"").append(LocaleHelper.getTextDirection()).append("\"><table cellspacing=\"10\"><tr><td class=\"portlet-section\" colspan=\"3\">") //$NON-NLS-1$ //$NON-NLS-2$
248: .append(title)
249: .append(
250: "<hr size=\"1\"/></td></tr><tr><td class=\"portlet-font\" valign=\"top\">"); //$NON-NLS-1$
251: Iterator messageIterator = messages.iterator();
252: String errorStart = PentahoMessenger.getUserString("ERROR"); //$NON-NLS-1$
253: int pos = errorStart.indexOf('{');
254: if (pos != -1) {
255: errorStart = errorStart.substring(0, pos);
256: }
257: String firstMessage = getFirstError(messages);
258: if (firstMessage != null) {
259: messageBuffer
260: .append("<span style=\"color:red\">").append(firstMessage).append("</span><p/>"); //$NON-NLS-1$ //$NON-NLS-2$
261: }
262: messageIterator = messages.iterator();
263: while (messageIterator.hasNext()) {
264: messageBuffer.append((String) messageIterator.next())
265: .append("<br/>"); //$NON-NLS-1$
266: }
267: messageBuffer.append("</td></tr></table><p>"); //$NON-NLS-1$
268: IVersionHelper versionHelper = PentahoSystem
269: .getVersionHelper(null);
270: messageBuffer
271: .append(" " + Messages.getString("UIUtil.USER_SERVER_VERSION", versionHelper.getVersionInformation())); //$NON-NLS-1$ //$NON-NLS-2$
272: messageBuffer.append("</body></html>"); //$NON-NLS-1$
273: } else {
274: // TODO support other mime types
275: }
276:
277: }
278:
279: public static void formatFailureMessage(String mimeType,
280: IRuntimeContext context, StringBuffer messageBuffer,
281: List defaultMessages) {
282:
283: // TODO handle error messages from the runtime context
284:
285: if ((context == null) && (defaultMessages == null)) {
286: // something went badly wrong
287: formatErrorMessage(
288: mimeType,
289: Messages
290: .getString("UIUtil.ERROR_0001_REQUEST_FAILED"), Messages.getString("UIUtil.ERROR_0002_COULD_NOT_PROCESS"), messageBuffer); //$NON-NLS-1$ //$NON-NLS-2$
291: } else {
292: List theMessages = (context == null) ? defaultMessages
293: : context.getMessages();
294: // TODO handle error messages from the runtime context
295: formatErrorMessage(mimeType,
296: "Failed", theMessages, messageBuffer); //$NON-NLS-1$
297: // formatErrorMessage( mimeType,
298: // Messages.getString("UIUtil.ERROR_0001_REQUEST_FAILED"), messages,
299: // messageBuffer ); //$NON-NLS-1$
300: }
301: }
302:
303: public static void formatFailureMessage(String mimeType,
304: IRuntimeContext context, StringBuffer messageBuffer) {
305: formatFailureMessage(mimeType, context, messageBuffer, null);
306: }
307:
308: public static void formatResultSetAsHTMLRows(
309: IPentahoResultSet resultSet, StringBuffer messageBuffer) {
310: boolean hasColumnHeaders = false;
311: boolean hasRowHeaders = false;
312: Object[][] columnHeaders = null;
313: Object[][] rowHeaders = null;
314: if (resultSet.getMetaData() != null) {
315: columnHeaders = resultSet.getMetaData().getColumnHeaders();
316: rowHeaders = resultSet.getMetaData().getRowHeaders();
317: hasColumnHeaders = columnHeaders != null;
318: hasRowHeaders = rowHeaders != null;
319: if (hasColumnHeaders) {
320: for (int row = 0; row < columnHeaders.length; row++) {
321: messageBuffer.append("<tr>"); //$NON-NLS-1$
322: if (hasRowHeaders) {
323: for (int indent = 0; indent < rowHeaders[0].length; indent++) {
324: messageBuffer.append("<th></th>"); //$NON-NLS-1$
325: }
326: }
327: for (int column = 0; column < columnHeaders[row].length; column++) {
328: messageBuffer
329: .append("<th>").append(columnHeaders[row][column]).append("</th>"); //$NON-NLS-1$//$NON-NLS-2$
330: }
331: messageBuffer.append("</tr>"); //$NON-NLS-1$
332: }
333: }
334:
335: }
336: Object[] dataRow = resultSet.next();
337: int currentRow = 0;
338: while (dataRow != null && currentRow < MAX_RESULT_THRESHOLD) {
339: messageBuffer.append("<tr>"); //$NON-NLS-1$
340: if (hasRowHeaders) {
341: for (int rowHeaderCol = rowHeaders[currentRow].length - 1; rowHeaderCol >= 0; rowHeaderCol--) {
342: messageBuffer
343: .append("<th>").append(rowHeaders[currentRow][rowHeaderCol].toString()).append("</th>"); //$NON-NLS-1$//$NON-NLS-2$
344: }
345: }
346: NumberFormat nf = NumberFormat.getInstance();
347: for (int column = 0; column < dataRow.length; column++) {
348: if (dataRow[column] != null) {
349: Object value = dataRow[column];
350: if (value instanceof Number) {
351: Number numVal = (Number) value;
352: value = nf.format(numVal);
353: messageBuffer
354: .append("<td align=\"right\">").append(value.toString()).append("</td>"); //$NON-NLS-1$//$NON-NLS-2$
355: } else {
356: messageBuffer
357: .append("<td>").append(value.toString()).append("</td>"); //$NON-NLS-1$ //$NON-NLS-2$
358: }
359: } else {
360: messageBuffer.append("<td>---</td>"); //$NON-NLS-1$
361: }
362: }
363: messageBuffer.append("</tr>"); //$NON-NLS-1$
364: dataRow = resultSet.next();
365: currentRow++;
366: }
367: }
368:
369: public static void formatSuccessMessage(String mimeType,
370: IRuntimeContext context, StringBuffer messageBuffer,
371: boolean doMessages) {
372: formatSuccessMessage(mimeType, context, messageBuffer,
373: doMessages, true);
374: }
375:
376: public static void formatSuccessMessage(String mimeType,
377: IRuntimeContext context, StringBuffer messageBuffer,
378: boolean doMessages, boolean doWrapper) {
379:
380: if (context == null) {
381: // something went badly wrong
382: formatFailureMessage(mimeType, context, messageBuffer);
383: } else if (mimeType.equalsIgnoreCase(HTML_MIME_TYPE)) {
384: // TODO make this template or XSL based
385: if (doWrapper) {
386: messageBuffer
387: .append("<html><head><title>") //$NON-NLS-1$
388: .append(
389: Messages
390: .getString("UIUtil.USER_START_ACTION")) //$NON-NLS-1$
391: .append(
392: "</title><link rel=\"stylesheet\" type=\"text/css\" href=\"/pentaho-style/active/default.css\"></head>") //$NON-NLS-1$
393: .append("<body dir=\"").append(LocaleHelper.getTextDirection()).append("\"><table cellspacing=\"10\"><tr><td class=\"portlet-section\" colspan=\"3\">") //$NON-NLS-1$ //$NON-NLS-2$
394: .append(
395: Messages
396: .getString("UIUtil.USER_ACTION_SUCCESSFUL")) //$NON-NLS-1$
397: .append(
398: "<hr size=\"1\"/></td></tr><tr><td class=\"portlet-font\" valign=\"top\">"); //$NON-NLS-1$
399: }
400:
401: // hmm do we need this to be ordered?
402: Set outputNames = context.getOutputNames();
403: Iterator outputNameIterator = outputNames.iterator();
404: while (outputNameIterator.hasNext()) {
405: String outputName = (String) outputNameIterator.next();
406: Object value = context.getOutputParameter(outputName)
407: .getValue();
408: if (value == null) {
409: value = ""; //$NON-NLS-1$
410: } else if (value instanceof IPentahoResultSet) {
411: formatResultSetAsHTMLRows(
412: (IPentahoResultSet) value, messageBuffer);
413: } else {
414: if (doWrapper) {
415: messageBuffer.append(outputName).append("="); //$NON-NLS-1$
416: }
417: messageBuffer.append(value.toString());
418: if (doWrapper) {
419: messageBuffer.append("<br/>"); //$NON-NLS-1$
420: }
421: }
422: }
423: if (doMessages) {
424: if (doWrapper) {
425: messageBuffer.append("<p><br size=\"1\">"); //$NON-NLS-1$
426: }
427: List messages = context.getMessages();
428: Iterator messageIterator = messages.iterator();
429: while (messageIterator.hasNext()) {
430: messageBuffer.append((String) messageIterator
431: .next());
432: if (doWrapper) {
433: messageBuffer.append("<br/>"); //$NON-NLS-1$
434: }
435: }
436: }
437:
438: if (doWrapper) {
439: messageBuffer
440: .append("</td></tr></table></body></html>"); //$NON-NLS-1$
441: }
442: } else if (mimeType.equalsIgnoreCase(TEXT_MIME_TYPE)) {
443: messageBuffer
444: .append(
445: Messages
446: .getString("UIUtil.USER_START_ACTION" + "\n")) //$NON-NLS-1$ //$NON-NLS-2$
447: .append(
448: Messages
449: .getString("UIUtil.USER_ACTION_SUCCESSFUL" + "\n")); //$NON-NLS-1$ //$NON-NLS-2$
450:
451: // hmm do we need this to be ordered?
452: Set outputNames = context.getOutputNames();
453: Iterator outputNameIterator = outputNames.iterator();
454: while (outputNameIterator.hasNext()) {
455: String outputName = (String) outputNameIterator.next();
456: Object value = context.getOutputParameter(outputName)
457: .getValue();
458: if (value == null) {
459: value = ""; //$NON-NLS-1$
460: } else if (value instanceof IPentahoResultSet) {
461: IPentahoResultSet resultSet = (IPentahoResultSet) value;
462: Object[][] columnHeaders = resultSet.getMetaData()
463: .getColumnHeaders();
464: Object[][] rowHeaders = resultSet.getMetaData()
465: .getRowHeaders();
466: boolean hasColumnHeaders = columnHeaders != null;
467: boolean hasRowHeaders = rowHeaders != null;
468: if (hasColumnHeaders) {
469: for (int row = 0; row < columnHeaders.length; row++) {
470: for (int column = 0; column < columnHeaders[row].length; column++) {
471: if (hasRowHeaders) {
472: for (int indent = 0; indent < rowHeaders[0].length; indent++) {
473: messageBuffer.append("\t"); //$NON-NLS-1$
474: }
475: }
476: messageBuffer.append(
477: columnHeaders[row][column])
478: .append("\t"); //$NON-NLS-1$
479: }
480: messageBuffer.append("\n"); //$NON-NLS-1$
481: }
482: }
483: int headerRow = 0;
484: Object[] dataRow = resultSet.next();
485: int currentRow = 0;
486: while (dataRow != null
487: && currentRow < MAX_RESULT_THRESHOLD) {
488: if (hasRowHeaders) {
489: for (int rowHeaderCol = 0; rowHeaderCol < rowHeaders[headerRow].length; rowHeaderCol++) {
490: messageBuffer
491: .append(
492: rowHeaders[headerRow][rowHeaderCol]
493: .toString())
494: .append("\t"); //$NON-NLS-1$
495: }
496: }
497: for (int column = 0; column < dataRow.length; column++) {
498: messageBuffer.append(
499: dataRow[column].toString()).append(
500: "\t"); //$NON-NLS-1$
501: }
502: dataRow = resultSet.next();
503: currentRow++;
504: }
505: } else {
506: messageBuffer
507: .append(outputName)
508: .append("=").append(value.toString()).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
509: }
510: }
511: if (doMessages) {
512: List messages = context.getMessages();
513: Iterator messageIterator = messages.iterator();
514: while (messageIterator.hasNext()) {
515: messageBuffer.append(
516: (String) messageIterator.next()).append(
517: "\n"); //$NON-NLS-1$
518: }
519: }
520: }
521: }
522: }
|