From b7943793644e4ca9149c52f5ccb87c3fa1c6855b Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Tue, 9 Jun 2026 23:53:10 +0300 Subject: [PATCH] Make File Upload plugin as internal and skip it's loading from plugins dir --- .../org/jivesoftware/spark/PluginManager.java | 3 +- .../sparkimpl/plugin/InternalPlugins.java | 1 + .../plugin/fileupload/ChatRoomDecorator.java | 29 +-- .../fileupload/FileUploadChatComponent.java | 228 ++++++++++++++++++ .../fileupload/SparkFileUploadPlugin.java | 10 +- plugins/fileupload/pom.xml | 26 -- .../src/main/plugin-metadata/plugin.xml | 12 - .../src/main/resources/images/icon16.png | Bin 413 -> 0 bytes pom.xml | 1 - 9 files changed, 252 insertions(+), 58 deletions(-) rename {plugins/fileupload/src/main/java/org/jivesoftware/spark => core/src/main/java/org/jivesoftware/sparkimpl}/plugin/fileupload/ChatRoomDecorator.java (87%) create mode 100644 core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/FileUploadChatComponent.java rename {plugins/fileupload/src/main/java/org/jivesoftware/spark => core/src/main/java/org/jivesoftware/sparkimpl}/plugin/fileupload/SparkFileUploadPlugin.java (93%) delete mode 100644 plugins/fileupload/pom.xml delete mode 100644 plugins/fileupload/src/main/plugin-metadata/plugin.xml delete mode 100644 plugins/fileupload/src/main/resources/images/icon16.png diff --git a/core/src/main/java/org/jivesoftware/spark/PluginManager.java b/core/src/main/java/org/jivesoftware/spark/PluginManager.java index 8e9e64a2d..b0a5d1912 100644 --- a/core/src/main/java/org/jivesoftware/spark/PluginManager.java +++ b/core/src/main/java/org/jivesoftware/spark/PluginManager.java @@ -77,6 +77,7 @@ public class PluginManager implements MainWindowListener { public static final Map INCOMPATIBLE_PLUGINS = Map.of( // normalizePluginName("OTR Plugin"), "0.4 Beta" + normalizePluginName("Http File Upload Plugin"), "" ); private final List plugins = new ArrayList<>(); @@ -318,7 +319,7 @@ public class PluginManager implements MainWindowListener String lower = normalizePluginName(publicPlugin.getName()); // Don't load the plugin if it's known to be incompatible with this version of Spark. String incompatibleVersion = INCOMPATIBLE_PLUGINS.get(lower); - if (incompatibleVersion != null && incompatibleVersion.compareTo(publicPlugin.getVersion()) >= 0) { + if (incompatibleVersion != null && (incompatibleVersion.isEmpty() || incompatibleVersion.compareTo(publicPlugin.getVersion()) >= 0)) { Log.warning("Not loading plugin " + publicPlugin.getName() + " (version " + publicPlugin.getVersion() + ") as it is incompatible with this version of Spark."); return null; } diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/plugin/InternalPlugins.java b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/InternalPlugins.java index a143f4fbf..0d9e0af0b 100644 --- a/core/src/main/java/org/jivesoftware/sparkimpl/plugin/InternalPlugins.java +++ b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/InternalPlugins.java @@ -19,6 +19,7 @@ public class InternalPlugins { new PublicPlugin("Emoticon Plugin", "org.jivesoftware.sparkimpl.plugin.emoticons.EmoticonPlugin", "1.1"), new PublicPlugin("Notes Plugin", "org.jivesoftware.sparkimpl.plugin.scratchpad.ScratchPadPlugin", "1.1"), new PublicPlugin("Buzz Plugin", "org.jivesoftware.sparkimpl.plugin.alerts.BuzzPlugin", "1.1"), + new PublicPlugin("Http File Upload Plugin", "org.jivesoftware.sparkimpl.plugin.fileupload.SparkFileUploadPlugin", "1.1"), new PublicPlugin("Notifications Plugin", "org.jivesoftware.sparkimpl.preference.notifications.NotificationPlugin", "1.1"), new PublicPlugin("Shortcut Plugin", "org.jivesoftware.sparkimpl.plugin.chat.ShortcutPlugin", "1.1"), new PublicPlugin("History Plugin", "org.jivesoftware.sparkimpl.plugin.history.ConversationHistoryPlugin", "1.1"), diff --git a/plugins/fileupload/src/main/java/org/jivesoftware/spark/plugin/fileupload/ChatRoomDecorator.java b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/ChatRoomDecorator.java similarity index 87% rename from plugins/fileupload/src/main/java/org/jivesoftware/spark/plugin/fileupload/ChatRoomDecorator.java rename to core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/ChatRoomDecorator.java index 820a70a64..565f793c1 100644 --- a/plugins/fileupload/src/main/java/org/jivesoftware/spark/plugin/fileupload/ChatRoomDecorator.java +++ b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/ChatRoomDecorator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jivesoftware.spark.plugin.fileupload; +package org.jivesoftware.sparkimpl.plugin.fileupload; import org.jivesoftware.resource.Res; import org.jivesoftware.resource.SparkRes; @@ -27,22 +27,19 @@ import org.jivesoftware.smackx.httpfileupload.UploadService; import org.jivesoftware.spark.component.RolloverButton; import org.jivesoftware.spark.ui.ChatRoom; import org.jivesoftware.spark.util.GraphicUtils; +import org.jivesoftware.spark.util.SwingWorker; import org.jivesoftware.spark.util.log.Log; import org.jivesoftware.sparkimpl.plugin.filetransfer.transfer.ui.TransferUtils; -import javax.swing.SwingUtilities; import java.awt.FileDialog; import java.awt.Frame; import java.io.File; import java.net.URL; -import java.time.Duration; -import java.time.Instant; import static org.jivesoftware.smack.XMPPException.XMPPErrorException; import static org.jivesoftware.smack.packet.StanzaError.Condition.not_acceptable; import static org.jivesoftware.smack.packet.StanzaError.Condition.resource_constraint; import static org.jivesoftware.spark.ChatManager.ERROR_COLOR; -import static org.jivesoftware.spark.ChatManager.NOTIFICATION_COLOR; public class ChatRoomDecorator { private final HttpFileUploadManager httpFileUploadManager; @@ -74,9 +71,16 @@ public class ChatRoomDecorator { fd.setVisible(true); File[] files = fd.getFiles(); - for (File file : files) { - SwingUtilities.invokeLater(() -> handleUpload(file, room)); - } + SwingWorker worker = new SwingWorker() { + @Override + public Object construct() { + for (File file : files) { + handleUpload(file, room); + } + return null; + } + }; + worker.start(); } private void handleUpload(File file, ChatRoom room) { @@ -97,14 +101,7 @@ public class ChatRoomDecorator { } } try { - Instant start = Instant.now(); - URL uploadedFile = httpFileUploadManager.uploadFile(file, (uploadedBytes, totalBytes) -> { - if (Duration.between(start, Instant.now()).toSeconds() < 10) { - return; - } - long progress = totalBytes > 0 ? uploadedBytes / totalBytes : 100; - room.getTranscriptWindow().insertNotificationMessage(progress + "%", NOTIFICATION_COLOR); - }); + URL uploadedFile = httpFileUploadManager.uploadFile(file); broadcastUploadUrl(uploadedFile.toString()); } catch (Exception e) { String errMsg = e.getMessage(); diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/FileUploadChatComponent.java b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/FileUploadChatComponent.java new file mode 100644 index 000000000..01518299e --- /dev/null +++ b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/FileUploadChatComponent.java @@ -0,0 +1,228 @@ +/** + * Copyright (C) 2004-2011 Jive Software. 2026 Ignite Realtime Foundation. 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.fileupload; + +import org.jivesoftware.resource.Res; +import org.jivesoftware.resource.SparkRes; +import org.jivesoftware.smackx.filetransfer.FileTransfer.Status; +import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer; +import org.jivesoftware.spark.SparkManager; +import org.jivesoftware.spark.component.FileDragLabel; +import org.jivesoftware.spark.ui.ContactItem; +import org.jivesoftware.spark.ui.ContactList; +import org.jivesoftware.spark.util.ByteFormat; +import org.jivesoftware.spark.util.GraphicUtils; +import org.jivesoftware.spark.util.SwingWorker; +import org.jivesoftware.spark.util.log.Log; +import org.jivesoftware.sparkimpl.plugin.filetransfer.transfer.ui.TransferUtils; +import org.jivesoftware.sparkimpl.settings.Sizes; +import org.jxmpp.jid.EntityFullJid; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.NORTHWEST; + +public class FileUploadChatComponent extends JPanel { + private final FileDragLabel imageLabel = new FileDragLabel(); + private final JLabel titleLabel = new JLabel(); + private final JLabel fileLabel = new JLabel(); + + private final JProgressBar progressBar = new JProgressBar(); + private File fileToSend; + private final JLabel progressLabel = new JLabel(); + private long _startTime; + + public FileUploadChatComponent() { + setLayout(new GridBagLayout()); + setBackground(new Color(250, 249, 242)); + Insets insets = new Insets(5, 5, 5, 5); + add(imageLabel, new GridBagConstraints(0, 0, 1, 3, 0, 0, NORTHWEST, NONE, insets, 0, 0)); + + titleLabel.setFont(new Font("Dialog", Font.BOLD, 11)); + titleLabel.setForeground(new Color(211, 174, 102)); + add(titleLabel, new GridBagConstraints(1, 0, 2, 1, 1, 0, NORTHWEST, NONE, insets, 0, 0)); + add(fileLabel, new GridBagConstraints(1, 1, 2, 1, 1, 0, GridBagConstraints.WEST, NONE, new Insets(0, 5, 5, 5), 0, 0)); + + setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.white)); + } + + public void sendFile(final OutgoingFileTransfer transfer, final EntityFullJid jid, final String nickname) { + String fileName = transfer.getFileName(); + String filePath = transfer.getFilePath(); + long fileSize = transfer.getFileSize(); + ByteFormat format = new ByteFormat(); + String fileSizeString = format.format(fileSize); + + fileToSend = new File(transfer.getFilePath()); + imageLabel.setFile(fileToSend); + + fileLabel.setText(fileName + " (" + fileSizeString + ")"); + + ContactList contactList = SparkManager.getWorkspace().getContactList(); + ContactItem contactItem = contactList.getContactItemByJID(jid); + + titleLabel.setText(Res.getString("message.transfer.waiting.on.user", contactItem.getDisplayName())); + + if (isImage(fileName)) { + try { + URL imageURL = new File(transfer.getFilePath()).toURI().toURL(); + ImageIcon image = new ImageIcon(imageURL); + image = GraphicUtils.scaleImageIcon(image, Sizes.Transfer.THUMBNAIL, Sizes.Transfer.THUMBNAIL); + imageLabel.setIcon(image); + } catch (MalformedURLException e) { + Log.error("Could not locate image.", e); + imageLabel.setIcon(SparkRes.getImageIcon(SparkRes.Icon.DOCUMENT_INFO_32x32)); + } + } else { + File file = new File(transfer.getFilePath()); + Icon icon = GraphicUtils.getIcon(file); + imageLabel.setIcon(icon); + } + + progressBar.setMaximum(100); + progressBar.setVisible(false); + progressBar.setStringPainted(true); + add(progressBar, new GridBagConstraints(1, 2, 2, 1, 1, 0, GridBagConstraints.WEST, NONE, new Insets(0, 5, 0, 5), 150, 0)); + add(progressLabel, new GridBagConstraints(1, 3, 2, 1, 1, 0, GridBagConstraints.WEST, NONE, new Insets(0, 5, 0, 5), 150, 0)); + + SwingWorker worker = new SwingWorker() { + @Override + public Object construct() { + while (true) { + try { + if (transfer.getBytesSent() > 0 && _startTime == 0) { + _startTime = System.currentTimeMillis(); + } + long startTime = System.currentTimeMillis(); + long startByte = transfer.getBytesSent(); + Thread.sleep(500); + long endTime = System.currentTimeMillis(); + long endByte = transfer.getBytesSent(); + + long timeDiff = endTime - startTime; + long byteDiff = endByte - startByte; + + updateBar(transfer, nickname, TransferUtils.calculateSpeed(byteDiff, timeDiff)); + } catch (InterruptedException e) { + Log.error("Unable to sleep thread.", e); + return null; + } + } + } + + @Override + public void finished() { + updateBar(transfer, nickname, "??MB/s"); + } + }; + + worker.start(); + + makeClickable(imageLabel); + makeClickable(titleLabel); + } + + private void makeClickable(final JLabel label) { + label.setToolTipText(Res.getString("message.click.to.open")); + label.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + openFile(fileToSend); + } + + @Override + public void mouseEntered(MouseEvent e) { + label.setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + + @Override + public void mouseExited(MouseEvent e) { + label.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + }); + } + + private void openFile(File downloadedFile) { + try { + Desktop.getDesktop().open(downloadedFile); + } catch (IOException e) { + Log.error("An error occurred while trying to open downloaded file: " + downloadedFile, e); + } + } + + private void updateBar(final OutgoingFileTransfer transfer, String nickname, String kBperSecond) { + + } + + + private static class TransferButton extends JButton { + public TransferButton() { + decorate(); + } + + /** + * Decorates the button with the appropriate UI configurations. + */ + private void decorate() { + setBorderPainted(false); + setOpaque(true); + setContentAreaFilled(false); + setMargin(new Insets(1, 1, 1, 1)); + } + } + + private boolean isImage(String fileName) { + fileName = fileName.toLowerCase(); + String[] imageTypes = {"jpeg", "gif", "jpg", "png"}; + for (String imageType : imageTypes) { + if (fileName.endsWith(imageType)) { + return true; + } + } + return false; + } + + private void showAlert(boolean alert) { + if (alert) { + titleLabel.setForeground(new Color(211, 174, 102)); + setBackground(new Color(250, 249, 242)); + } else { + setBackground(new Color(239, 245, 250)); + titleLabel.setForeground(new Color(65, 139, 179)); + } + } +} diff --git a/plugins/fileupload/src/main/java/org/jivesoftware/spark/plugin/fileupload/SparkFileUploadPlugin.java b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/SparkFileUploadPlugin.java similarity index 93% rename from plugins/fileupload/src/main/java/org/jivesoftware/spark/plugin/fileupload/SparkFileUploadPlugin.java rename to core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/SparkFileUploadPlugin.java index d5f101427..f07309148 100644 --- a/plugins/fileupload/src/main/java/org/jivesoftware/spark/plugin/fileupload/SparkFileUploadPlugin.java +++ b/core/src/main/java/org/jivesoftware/sparkimpl/plugin/fileupload/SparkFileUploadPlugin.java @@ -15,7 +15,7 @@ */ -package org.jivesoftware.spark.plugin.fileupload; +package org.jivesoftware.sparkimpl.plugin.fileupload; import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager; import org.jivesoftware.spark.ChatManager; @@ -30,7 +30,13 @@ import java.util.HashMap; import java.util.Map; -public class SparkFileUploadPlugin implements Plugin, ChatRoomListener { +/** + * Http File Upload Plugin. + * Allows users to share files by uploading to a server (via XEP-0363). + * + * @author Dele Olajide + */ +public class SparkFileUploadPlugin implements Plugin, ChatRoomListener { private HttpFileUploadManager httpFileUploadManager; private final Map decorators = new HashMap<>(); diff --git a/plugins/fileupload/pom.xml b/plugins/fileupload/pom.xml deleted file mode 100644 index ea929cd98..000000000 --- a/plugins/fileupload/pom.xml +++ /dev/null @@ -1,26 +0,0 @@ - - 4.0.0 - - - org.igniterealtime.spark.plugins - plugin - 3.1.0-SNAPSHOT - ../plugin/pom.xml - - - fileupload - 0.1 - - Http File Upload Plugin - Allows users to share files by uploading to a server (via XEP-0363). - - - - Dele Olajide - - Author - - - - diff --git a/plugins/fileupload/src/main/plugin-metadata/plugin.xml b/plugins/fileupload/src/main/plugin-metadata/plugin.xml deleted file mode 100644 index e9d484731..000000000 --- a/plugins/fileupload/src/main/plugin-metadata/plugin.xml +++ /dev/null @@ -1,12 +0,0 @@ - - ${project.name} - ${project.version} - ${project.description} - Dele Olajide - https://IgniteRealtime.org - support@IgniteRealtime.org - org.jivesoftware.spark.plugin.fileupload.SparkFileUploadPlugin - 3.1.0 - Windows,Linux,Mac - 11 - diff --git a/plugins/fileupload/src/main/resources/images/icon16.png b/plugins/fileupload/src/main/resources/images/icon16.png deleted file mode 100644 index 58b702ba3409e0dc34eab0214906a02ee7238d8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 413 zcmV;O0b>4%P)uBQLe!R05NC&ilS^rB?cnab}$IyxCbq!ZwJcJQB=f&3x;*?sFUjYc^iqD+%DuOT>JjiFRDin+K_@g*XLTqa+}N z@WEu?_s^3gd4Q!uOlms}-ir%o_52G;DLhB4Comk2^uaQqhL7wXTvG^DG#YmHT ze9tjs-TyCyLRAB?r*kWk9MYPUB{AND?h z;FGBhmIRh-wc+Dn(9MK6s|UgMT%|JnNYgt0i(R;K&m1&}FEr^7Mp3jtT3?{EPTxy` zczoSz9ioH|o|8a-lFdM)(Ga6vZ$&xIA~QcUo6VOQN>P3R5foj)9tHX_00000NkvXX Hu0mjfx*@nO diff --git a/pom.xml b/pom.xml index 450a1792a..4a625587d 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,6 @@ plugins/apple plugins/fastpath - plugins/fileupload plugins/flashing plugins/growl