/* * Copyright (C) 2008 Jive Software. All rights reserved. * * 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. */ package org.jivesoftware.openfire.archive; import java.awt.Color; import java.io.ByteArrayOutputStream; import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Future; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.plugin.MonitoringPlugin; import org.jivesoftware.openfire.user.UserManager; import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.NotFoundException; import org.jivesoftware.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; import com.lowagie.text.Chunk; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Font; import com.lowagie.text.FontFactory; import com.lowagie.text.Image; import com.lowagie.text.PageSize; import com.lowagie.text.Paragraph; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfPageEventHelper; import com.lowagie.text.pdf.PdfWriter; /** * Utility class for asynchronous web calls for archiving tasks. * * @author Derek DeMoro */ public class ConversationUtils { private static final Logger Log = LoggerFactory.getLogger(ConversationUtils.class); /** * Returns the status of the rebuilding of the messaging/metadata archives. This is done * asynchronously. * * @return the status the rebuilding (0 - 100) where 100 is complete. */ public int getBuildProgress() { // Get handle on the Monitoring plugin MonitoringPlugin plugin = (MonitoringPlugin)XMPPServer.getInstance().getPluginManager().getPlugin( MonitoringConstants.NAME); ArchiveIndexer archiveIndexer = (ArchiveIndexer)plugin.getModule(ArchiveIndexer.class); Future<Integer> future = archiveIndexer.getIndexRebuildProgress(); if (future != null) { try { return future.get(); } catch (Exception e) { Log.error(e.getMessage(), e); } } return -1; } public ConversationInfo getConversationInfo(long conversationID, boolean formatParticipants) { // Create ConversationInfo bean ConversationInfo info = new ConversationInfo(); // Get handle on the Monitoring plugin MonitoringPlugin plugin = (MonitoringPlugin)XMPPServer.getInstance().getPluginManager().getPlugin( MonitoringConstants.NAME); ConversationManager conversationmanager = (ConversationManager)plugin.getModule(ConversationManager.class); try { Conversation conversation = conversationmanager.getConversation(conversationID); info = toConversationInfo(conversation, formatParticipants); } catch (NotFoundException e) { Log.error(e.getMessage(), e); } return info; } /** * Retrieves all the existing conversations from the system. * * @return a Map of ConversationInfo objects. */ public Map<String, ConversationInfo> getConversations(boolean formatParticipants) { Map<String, ConversationInfo> cons = new HashMap<String, ConversationInfo>(); MonitoringPlugin plugin = (MonitoringPlugin)XMPPServer.getInstance().getPluginManager() .getPlugin(MonitoringConstants.NAME); ConversationManager conversationManager = (ConversationManager)plugin.getModule(ConversationManager.class); Collection<Conversation> conversations = conversationManager.getConversations(); List<Conversation> lConversations = Arrays.asList(conversations.toArray(new Conversation[conversations.size()])); for (Iterator<Conversation> i = lConversations.iterator(); i.hasNext();) { Conversation con = i.next(); ConversationInfo info = toConversationInfo(con, formatParticipants); cons.put(Long.toString(con.getConversationID()), info); } return cons; } public ByteArrayOutputStream getConversationPDF(Conversation conversation) { Font red = FontFactory .getFont(FontFactory.HELVETICA, 12f, Font.BOLD, new Color(0xFF, 0x00, 0x00)); Font blue = FontFactory .getFont(FontFactory.HELVETICA, 12f, Font.ITALIC, new Color(0x00, 0x00, 0xFF)); Font black = FontFactory.getFont(FontFactory.HELVETICA, 12f, Font.BOLD, Color.BLACK); Map<String, Font> colorMap = new HashMap<String, Font>(); if (conversation != null) { Collection<JID> set = conversation.getParticipants(); int count = 0; for (JID jid : set) { if (conversation.getRoom() == null) { if (count == 0) { colorMap.put(jid.toString(), blue); } else { colorMap.put(jid.toString(), red); } count++; } else { colorMap.put(jid.toString(), black); } } } return buildPDFContent(conversation, colorMap); } private ByteArrayOutputStream buildPDFContent(Conversation conversation, Map<String, Font> colorMap) { Font roomEvent = FontFactory .getFont(FontFactory.HELVETICA, 12f, Font.ITALIC, new Color(0xFF, 0x00, 0xFF)); try { Document document = new Document(PageSize.A4, 50, 50, 50, 50); ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfWriter writer = PdfWriter.getInstance(document, baos); writer.setPageEvent(new PDFEventListener()); document.open(); Paragraph p = new Paragraph( LocaleUtils.getLocalizedString("archive.search.pdf.title", MonitoringConstants.NAME), FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLD)); document.add(p); document.add(Chunk.NEWLINE); ConversationInfo coninfo = new ConversationUtils() .getConversationInfo(conversation.getConversationID(), false); String participantsDetail; if (coninfo.getAllParticipants() == null) { participantsDetail = coninfo.getParticipant1() + ", " + coninfo.getParticipant2(); } else { participantsDetail = String.valueOf(coninfo.getAllParticipants().length); } Paragraph chapterTitle = new Paragraph( LocaleUtils .getLocalizedString("archive.search.pdf.participants", MonitoringConstants.NAME) + " " + participantsDetail, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD)); document.add(chapterTitle); Paragraph startDate = new Paragraph( LocaleUtils.getLocalizedString("archive.search.pdf.startdate", MonitoringConstants.NAME) + " " + coninfo.getDate(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD)); document.add(startDate); Paragraph duration = new Paragraph( LocaleUtils.getLocalizedString("archive.search.pdf.duration", MonitoringConstants.NAME) + " " + coninfo.getDuration(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD)); document.add(duration); Paragraph messageCount = new Paragraph( LocaleUtils .getLocalizedString("archive.search.pdf.messagecount", MonitoringConstants.NAME) + " " + conversation.getMessageCount(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD)); document.add(messageCount); document.add(Chunk.NEWLINE); Paragraph messageParagraph; for (ArchivedMessage message : conversation.getMessages()) { String time = JiveGlobals.formatTime(message.getSentDate()); String from = message.getFromJID().getNode(); if (conversation.getRoom() != null) { from = message.getToJID().getResource(); } String body = message.getBody(); String prefix; if (!message.isRoomEvent()) { prefix = "[" + time + "] " + from + ": "; Font font = colorMap.get(message.getFromJID().toString()); if (font == null) { font = colorMap.get(message.getFromJID().toBareJID()); } if (font == null) { font = FontFactory.getFont(FontFactory.HELVETICA, 12f, Font.BOLD, Color.BLACK); } messageParagraph = new Paragraph(new Chunk(prefix, font)); } else { prefix = "[" + time + "] "; messageParagraph = new Paragraph(new Chunk(prefix, roomEvent)); } messageParagraph.add(body); messageParagraph.add(" "); document.add(messageParagraph); } document.close(); return baos; } catch (DocumentException e) { Log.error("error creating PDF document: " + e.getMessage(), e); return null; } } private ConversationInfo toConversationInfo(Conversation conversation, boolean formatParticipants) { final ConversationInfo info = new ConversationInfo(); // Set participants Collection<JID> col = conversation.getParticipants(); if (conversation.getRoom() == null) { JID user1 = (JID)col.toArray()[0]; info.setParticipant1(formatJID(formatParticipants, user1)); JID user2 = (JID)col.toArray()[1]; info.setParticipant2(formatJID(formatParticipants, user2)); } else { info.setConversationID(conversation.getConversationID()); JID[] occupants = col.toArray(new JID[col.size()]); String[] jids = new String[col.size()]; for (int i = 0; i < occupants.length; i++) { jids[i] = formatJID(formatParticipants, occupants[i]); } info.setAllParticipants(jids); } Map<String, String> cssLabels = new HashMap<String, String>(); int count = 0; for (JID jid : col) { if (!cssLabels.containsKey(jid.toString())) { if (conversation.getRoom() == null) { if (count % 2 == 0) { cssLabels.put(jid.toBareJID(), "conversation-label2"); } else { cssLabels.put(jid.toBareJID(), "conversation-label1"); } count++; } else { cssLabels.put(jid.toString(), "conversation-label4"); } } } // Set date info.setDate(JiveGlobals.formatDateTime(conversation.getStartDate())); info.setLastActivity(JiveGlobals.formatTime(conversation.getLastActivity())); // Create body. final StringBuilder builder = new StringBuilder(); builder.append("<table width=100%>"); for (ArchivedMessage message : conversation.getMessages()) { String time = JiveGlobals.formatTime(message.getSentDate()); String from = message.getFromJID().getNode(); if (conversation.getRoom() != null) { from = message.getToJID().getResource(); } from = StringUtils.escapeHTMLTags(from); String cssLabel = cssLabels.get(message.getFromJID().toBareJID()); String body = StringUtils.escapeHTMLTags(message.getBody()); builder.append("<tr valign=top>"); if (!message.isRoomEvent()) { builder.append("<td width=1% nowrap class=" + cssLabel + ">").append("[") .append(time).append("]").append("</td>"); builder.append("<td width=1% class=" + cssLabel + ">").append(from).append(": ") .append("</td>"); builder.append("<td class=conversation-body>").append(body).append("</td"); } else { builder.append("<td width=1% nowrap class=conversation-label3>").append("[") .append(time).append("]").append("</td>"); builder.append("<td colspan=2 class=conversation-label3><i>").append(body) .append("</i></td"); } builder.append("</tr>"); } if (conversation.getMessages().size() == 0) { builder.append("<span class=small-description>" + LocaleUtils.getLocalizedString("archive.search.results.archive_disabled", MonitoringConstants.NAME) + "</a>"); } info.setBody(builder.toString()); // Set message count info.setMessageCount(conversation.getMessageCount()); long duration = (conversation.getLastActivity().getTime() - conversation.getStartDate().getTime()); info.setDuration(duration); return info; } private String formatJID(boolean html, JID jid) { String formattedJID; if (html) { UserManager userManager = UserManager.getInstance(); if (XMPPServer.getInstance().isLocal(jid) && userManager.isRegisteredUser(jid.getNode())) { formattedJID = "<a href='/user-properties.jsp?username=" + jid.getNode() + "'>" + jid.toBareJID() + "</a>"; } else { formattedJID = jid.toBareJID(); } } else { formattedJID = jid.toBareJID(); } return formattedJID; } class PDFEventListener extends PdfPageEventHelper { @Override public void onEndPage(PdfWriter writer, Document document) { PdfContentByte cb = writer.getDirectContent(); try { cb.setColorStroke(new Color(156, 156, 156)); cb.setLineWidth(2); cb.moveTo(document.leftMargin(), document.bottomMargin() - 5); cb.lineTo(document.getPageSize().width() - document.rightMargin(), document.bottomMargin() - 5); cb.stroke(); ClassLoader classLoader = ConversationUtils.class.getClassLoader(); Enumeration<URL> providerEnum = classLoader.getResources("images/pdf_generatedbyof.gif"); while (providerEnum.hasMoreElements()) { Image gif = Image.getInstance(providerEnum.nextElement()); cb.addImage(gif, 221, 0, 0, 28, (int)document.leftMargin(), (int)document.bottomMargin() - 35); } } catch (Exception e) { Log.error("error drawing PDF footer: " + e.getMessage()); } cb.saveState(); } } }