/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 2004-2011 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.sparkimpl.plugin.alerts; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import org.jivesoftware.resource.Default; import org.jivesoftware.resource.Res; import org.jivesoftware.resource.SparkRes; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.packet.DelayInformation; import org.jivesoftware.spark.ChatManager; import org.jivesoftware.spark.SparkManager; import org.jivesoftware.spark.UserManager; import org.jivesoftware.spark.component.RolloverButton; import org.jivesoftware.spark.component.tabbedPane.SparkTab; import org.jivesoftware.spark.plugin.ContextMenuListener; import org.jivesoftware.spark.plugin.Plugin; import org.jivesoftware.spark.ui.ChatContainer; import org.jivesoftware.spark.ui.ChatRoom; import org.jivesoftware.spark.ui.ChatRoomNotFoundException; import org.jivesoftware.spark.ui.ContactGroup; import org.jivesoftware.spark.ui.ContactItem; import org.jivesoftware.spark.ui.ContactList; import org.jivesoftware.spark.ui.MessageListener; import org.jivesoftware.spark.ui.SparkTabHandler; import org.jivesoftware.spark.ui.TranscriptWindow; import org.jivesoftware.spark.ui.rooms.ChatRoomImpl; import org.jivesoftware.spark.ui.status.StatusBar; import org.jivesoftware.spark.util.ModelUtil; import org.jivesoftware.spark.util.ResourceUtils; import org.jivesoftware.spark.util.log.Log; import org.jivesoftware.sparkimpl.plugin.manager.Enterprise; import org.jivesoftware.sparkimpl.settings.local.LocalPreferences; import org.jivesoftware.sparkimpl.settings.local.SettingsManager; /** * Handles broadcasts from server and allows for roster wide broadcasts. */ public class BroadcastPlugin extends SparkTabHandler implements Plugin, PacketListener { private Set<ChatRoom> broadcastRooms = new HashSet<ChatRoom>(); public void initialize() { boolean enabled = Enterprise.containsFeature(Enterprise.BROADCAST_FEATURE); if (!enabled) { return; } // Add as ContainerDecoratr SparkManager.getChatManager().addSparkTabHandler(this); PacketFilter serverFilter = new PacketTypeFilter(Message.class); SparkManager.getConnection().addPacketListener(this, serverFilter); // Register with action menu final JMenu actionsMenu = SparkManager.getMainWindow().getMenuByName(Res.getString("menuitem.actions")); JMenuItem broadcastMenu = new JMenuItem(Res.getString("title.broadcast.message"), SparkRes.getImageIcon(SparkRes.MEGAPHONE_16x16)); ResourceUtils.resButton(broadcastMenu, Res.getString("title.broadcast.message")); actionsMenu.add(broadcastMenu); broadcastMenu.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { broadcastToRoster(); } }); // Register with action menu JMenuItem startConversationtMenu = new JMenuItem("", SparkRes.getImageIcon(SparkRes.SMALL_MESSAGE_IMAGE)); ResourceUtils.resButton(startConversationtMenu, Res.getString("menuitem.start.a.chat")); actionsMenu.add(startConversationtMenu,0); startConversationtMenu.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ContactList contactList = SparkManager.getWorkspace().getContactList(); Collection<ContactItem> selectedUsers = contactList.getSelectedUsers(); String selectedUser = ""; Iterator<ContactItem> selectedUsersIterator = selectedUsers.iterator(); if (selectedUsersIterator.hasNext()) { ContactItem contactItem = selectedUsersIterator.next(); selectedUser = contactItem.getJID(); } String jid = (String)JOptionPane.showInputDialog(SparkManager.getMainWindow(), Res.getString("label.enter.address"), Res.getString("title.start.chat"), JOptionPane.QUESTION_MESSAGE, null, null, selectedUser); if (ModelUtil.hasLength(jid) && ModelUtil.hasLength(StringUtils.parseServer(jid))) { if (ModelUtil.hasLength(jid) && jid.indexOf('@') == -1) { // Append server address jid = jid + "@" + SparkManager.getConnection().getServiceName(); } String nickname = SparkManager.getUserManager().getUserNicknameFromJID(jid); jid = UserManager.escapeJID(jid); ChatRoom chatRoom = SparkManager.getChatManager().createChatRoom(jid, nickname, nickname); SparkManager.getChatManager().getChatContainer().activateChatRoom(chatRoom); } } }); // Add send to selected users. final ContactList contactList = SparkManager.getWorkspace().getContactList(); contactList.addContextMenuListener(new ContextMenuListener() { public void poppingUp(Object component, JPopupMenu popup) { if (component instanceof ContactGroup) { final ContactGroup group = (ContactGroup)component; Action broadcastMessageAction = new AbstractAction() { private static final long serialVersionUID = -6411248110270296726L; public void actionPerformed(ActionEvent e) { broadcastToGroup(group); } }; broadcastMessageAction.putValue(Action.NAME, Res.getString("menuitem.broadcast.to.group")); broadcastMessageAction.putValue(Action.SMALL_ICON, SparkRes.getImageIcon(SparkRes.MEGAPHONE_16x16)); popup.add(broadcastMessageAction); } } public void poppingDown(JPopupMenu popup) { } public boolean handleDefaultAction(MouseEvent e) { return false; } }); // Add Broadcast to roster StatusBar statusBar = SparkManager.getWorkspace().getStatusBar(); RolloverButton broadcastToRosterButton = new RolloverButton(SparkRes.getImageIcon(SparkRes.MEGAPHONE_16x16)); broadcastToRosterButton.setToolTipText(Res.getString("message.send.a.broadcast")); statusBar.invalidate(); statusBar.validate(); statusBar.repaint(); broadcastToRosterButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { broadcastToRoster(); } }); } public void shutdown() { } public boolean canShutDown() { return false; } public void processPacket(final Packet packet) { SwingUtilities.invokeLater(new Runnable() { public void run() { try { final Message message = (Message)packet; // Do not handle errors or offline messages final DelayInformation offlineInformation = (DelayInformation)message.getExtension("x", "jabber:x:delay"); if (offlineInformation != null || message.getError() != null) { return; } boolean broadcast = message.getProperty("broadcast") != null; if ((broadcast || message.getType() == Message.Type.normal || message.getType() == Message.Type.headline) && message.getBody() != null) { showAlert((Message)packet); } else { String host = SparkManager.getSessionManager().getServerAddress(); String from = packet.getFrom() != null ? packet.getFrom() : ""; if (host.equalsIgnoreCase(from) || !ModelUtil.hasLength(from)) { showAlert((Message)packet); } } } catch (Exception e) { Log.error(e); } } }); } /** * Show Server Alert. * * @param message the message to show. * @param type */ private void showAlert(Message message) { Type type = message.getType(); // Do not show alert if the message is an error. if (message.getError() != null) { return; } final String body = message.getBody(); String subject = message.getSubject(); StringBuffer buf = new StringBuffer(); if (subject != null) { buf.append(Res.getString("subject")).append(": ").append(subject); buf.append("\n\n"); } buf.append(body); String from = message.getFrom() != null ? message.getFrom() : ""; final TranscriptWindow window = new TranscriptWindow(); window.insertPrefixAndMessage(null, buf.toString(), ChatManager.TO_COLOR); JPanel p = new JPanel(); p.setLayout(new BorderLayout()); p.add(window, BorderLayout.CENTER); p.setBorder(BorderFactory.createLineBorder(Color.lightGray)); // Count the number of linebreaks <br> and \n String s = message.getBody(); s = s.replace("<br/>", "\n"); s = s.replace("<br>", "\n"); int linebreaks = org.jivesoftware.spark.util.StringUtils. countNumberOfOccurences(s,'\n'); // Currently Serverbroadcasts dont contain Subjects, so this might be a MOTD message boolean mightbeMOTD = message.getSubject()!=null; if (!from.contains("@")) { // if theres no "@" it means the message came from the server if (Default.getBoolean(Default.BROADCAST_IN_CHATWINDOW) || linebreaks > 20 || message.getBody().length() > 1000 || mightbeMOTD) { // if we have more than 20 linebreaks or the message is longer // than 1000characters we should broadcast // in a normal chatwindow broadcastInChat(message); } else { broadcastWithPanel(message); } } else if (message.getFrom() != null) { userToUserBroadcast(message, type, from); } } /** * Handles Broadcasts made from a user to another user * * @param message * the message * @param type * the message type * @param from * the sender */ private void userToUserBroadcast(Message message, Type type, String from) { String jid = StringUtils.parseBareAddress(from); String nickname = SparkManager.getUserManager().getUserNicknameFromJID(jid); ChatManager chatManager = SparkManager.getChatManager(); ChatContainer container = chatManager.getChatContainer(); ChatRoomImpl chatRoom; try { chatRoom = (ChatRoomImpl)container.getChatRoom(jid); } catch (ChatRoomNotFoundException e) { chatRoom = new ChatRoomImpl(jid, nickname, nickname); SparkManager.getChatManager().getChatContainer().addChatRoom(chatRoom); } Message m = new Message(); m.setBody(message.getBody()); m.setTo(message.getTo()); String name = StringUtils.parseName(message.getFrom()); String broadcasttype = type == Message.Type.normal ? Res.getString("broadcast") : Res.getString("message.alert.notify"); m.setFrom(name +" "+broadcasttype); chatRoom.getTranscriptWindow().insertMessage(m.getFrom(), message, ChatManager.FROM_COLOR, new Color(0,0,0,0)); chatRoom.addToTranscript(m,true); broadcastRooms.add(chatRoom); LocalPreferences pref = SettingsManager.getLocalPreferences(); if (pref.getShowToasterPopup()) { SparkToaster toaster = new SparkToaster(); toaster.setDisplayTime(30000); toaster.setBorder(BorderFactory.createBevelBorder(0)); toaster.setTitle(broadcasttype); toaster.showToaster(message.getBody()); } chatRoom.addMessageListener(new MessageListener() { boolean waiting = true; public void messageReceived(ChatRoom room, Message message) { removeAsBroadcast(room); } public void messageSent(ChatRoom room, Message message) { removeAsBroadcast(room); } private void removeAsBroadcast(ChatRoom room) { if (waiting) { broadcastRooms.remove(room); // Notify decorators SparkManager.getChatManager().notifySparkTabHandlers(room); waiting = false; } } }); } /** * Broadcasts a message to all in the roster. */ private void broadcastToRoster() { final BroadcastDialog broadcastDialog = new BroadcastDialog(); broadcastDialog.invokeDialog(); } /** * Broadcasts a message to all selected users. * * @param group the Contact Group to send the messages to. */ private void broadcastToGroup(ContactGroup group) { final BroadcastDialog broadcastDialog = new BroadcastDialog(); broadcastDialog.invokeDialog(group); } public void uninstall() { // Do nothing. } public boolean isTabHandled(SparkTab tab, Component component, boolean isSelectedTab, boolean chatFrameFocused) { if (component instanceof ChatRoom) { ChatRoom chatroom = (ChatRoom)component; if (broadcastRooms.contains(chatroom)) { final ChatRoomImpl room = (ChatRoomImpl)component; tab.setIcon(SparkRes.getImageIcon(SparkRes.INFORMATION_IMAGE)); String nickname = room.getTabTitle(); nickname = Res.getString("message.broadcast.from", nickname); tab.setTabTitle(nickname); if ((!chatFrameFocused || !isSelectedTab) && room.getUnreadMessageCount() > 0) { // Make tab red. tab.setTitleColor(Color.red); tab.setTabBold(true); } else { tab.setTitleColor(Color.black); tab.setTabFont(tab.getDefaultFont()); room.clearUnreadMessageCount(); } return true; } } return false; } /** * Displays the Serverbroadcast like all other messages * in its on chatcontainer with transcript history * @param message * @param from */ private void broadcastInChat(Message message) { String from = message.getFrom() != null ? message.getFrom() : ""; ChatManager chatManager = SparkManager.getChatManager(); ChatContainer container = chatManager.getChatContainer(); ChatRoomImpl chatRoom; try { chatRoom = (ChatRoomImpl)container.getChatRoom(from); } catch (ChatRoomNotFoundException e) { String windowtitle = message.getSubject()!=null ? message.getSubject() : Res.getString("administrator"); chatRoom = new ChatRoomImpl("serveralert@" + from, Res.getString("broadcast"), windowtitle); chatRoom.getBottomPanel().setVisible(false); chatRoom.hideToolbar(); SparkManager.getChatManager().getChatContainer().addChatRoom(chatRoom); } chatRoom.getTranscriptWindow().insertNotificationMessage(message.getBody(), ChatManager.NOTIFICATION_COLOR); broadcastRooms.add(chatRoom); } /** * Displays a Serverbroadcast within a JFrame<br> * Messages can contain html-tags * @param message */ private void broadcastWithPanel(Message message) { String title = Res.getString("message.broadcast.from", Res.getString("administrator")); final JFrame alert = new JFrame(title); alert.setLayout(new GridBagLayout()); alert.setIconImage(SparkRes.getImageIcon(SparkRes.MAIN_IMAGE) .getImage()); String msg = "<html>" + message.getBody().replace("\n", "<br/>")+ "</html>"; JLabel icon = new JLabel(SparkRes.getImageIcon(SparkRes.ALERT)); JLabel alertlabel = new JLabel(msg); JButton close = new JButton(Res.getString("close")); close.addActionListener(new AbstractAction() { private static final long serialVersionUID = -3822361866008590946L; @Override public void actionPerformed(ActionEvent e) { alert.setVisible(false); alert.dispose(); } }); alert.add(icon,new GridBagConstraints(0,0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5,5,5,5), 0, 0)); alert.add(alertlabel, new GridBagConstraints(1,0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5,5,5,5), 0, 0)); alert.add(close, new GridBagConstraints(1,1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5,5,5,5), 0, 0)); alert.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); alert.setVisible(true); alert.setMinimumSize(new Dimension(340, 200)); alert.pack(); Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int x = (dim.width - alert.getSize().width) / 2; int y = (dim.height - alert.getSize().height) / 2; alert.setLocation(x, y); alert.toFront(); alert.requestFocus(); } }