Explorar el Código

SocketAPI: Make it easier to add or remove item in the action menu

By making it dynamic.
So far only the dolphin shell extension have been ported
Olivier Goffart hace 8 años
padre
commit
1782ae3c08

+ 64 - 16
shell_integration/dolphin/ownclouddolphinactionplugin.cpp

@@ -17,8 +17,8 @@
  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA               *
  ******************************************************************************/
 
-#include <KPluginFactory>
-#include <KPluginLoader>
+#include <KCoreAddons/KPluginFactory>
+#include <KCoreAddons/KPluginLoader>
 #include <KIOWidgets/kabstractfileitemactionplugin.h>
 #include <QtNetwork/QLocalSocket>
 #include <KIOCore/kfileitem.h>
@@ -27,6 +27,7 @@
 #include <QtWidgets/QMenu>
 #include <QtCore/QDir>
 #include <QtCore/QTimer>
+#include <QtCore/QEventLoop>
 #include "ownclouddolphinpluginhelper.h"
 
 class OwncloudDolphinPluginAction : public KAbstractFileItemActionPlugin
@@ -39,22 +40,70 @@ public:
     QList<QAction*> actions(const KFileItemListProperties& fileItemInfos, QWidget* parentWidget) Q_DECL_OVERRIDE
     {
         auto helper = OwncloudDolphinPluginHelper::instance();
-        QList<QUrl> urls = fileItemInfos.urlList();
-        if (urls.count() != 1 || !helper->isConnected())
+        if (!helper->isConnected() || !fileItemInfos.isLocal())
             return {};
 
-        auto url = urls.first();
-        if (!url.isLocalFile())
+        // If any of the url is outside of a sync folder, return an empty menu.
+        const QList<QUrl> urls = fileItemInfos.urlList();
+        const auto paths = helper->paths();
+        QByteArray files;
+        for (const auto &url : urls) {
+            QDir localPath(url.toLocalFile());
+            auto localFile = localPath.canonicalPath();
+            if (!std::any_of(paths.begin(), paths.end(), [&](const QString &s) {
+                    return localFile.startsWith(s);
+                }))
+                return {};
+
+            if (!files.isEmpty())
+                files += '\x1e'; // Record separator
+            files += localFile.toUtf8();
+        }
+
+        if (helper->version() < "1.1") { // in this case, lexicographic order works
+            return legacyActions(fileItemInfos, parentWidget);
+        }
+
+        auto menu = new QMenu(parentWidget);
+        QEventLoop loop;
+        auto con = connect(helper, &OwncloudDolphinPluginHelper::commandRecieved, this, [&](const QByteArray &cmd) {
+            if (cmd.startsWith("GET_MENU_ITEMS:END")) {
+                loop.quit();
+            } else if (cmd.startsWith("MENU_ITEM:")) {
+                auto args = QString::fromUtf8(cmd).split(QLatin1Char(':'));
+                if (args.size() < 3)
+                    return;
+                auto action = menu->addAction(args.mid(2).join(QLatin1Char(':')));
+                auto call = args.value(1).toLatin1();
+                connect(action, &QAction::triggered, [helper, call, files] {
+                    helper->sendCommand(QByteArray(call + ":" + files + "\n"));
+                });
+            }
+        });
+        QTimer::singleShot(100, &loop, SLOT(quit())); // add a timeout to be sure we don't freeze dolphin
+        helper->sendCommand(QByteArray("GET_MENU_ITEMS:" + files + "\n"));
+        loop.exec(QEventLoop::ExcludeUserInputEvents);
+        disconnect(con);
+        if (menu->actions().isEmpty()) {
+            delete menu;
             return {};
-        QDir localPath(url.toLocalFile());
-        auto localFile = localPath.canonicalPath();
+        }
+
+        auto menuaction = new QAction(parentWidget);
+        menuaction->setText(helper->contextMenuTitle());
+        menuaction->setMenu(menu);
+        return { menuaction };
+    }
 
-        const auto paths = helper->paths();
-        if (!std::any_of(paths.begin(), paths.end(), [&](const QString &s) {
-                                return localFile.startsWith(s);
-                        } ))
-             return {};
 
+    QList<QAction *> legacyActions(const KFileItemListProperties &fileItemInfos, QWidget *parentWidget)
+    {
+        QList<QUrl> urls = fileItemInfos.urlList();
+        if (urls.count() != 1)
+            return {};
+        QDir localPath(urls.first().toLocalFile());
+        auto localFile = localPath.canonicalPath();
+        auto helper = OwncloudDolphinPluginHelper::instance();
         auto menuaction = new QAction(parentWidget);
         menuaction->setText(helper->contextMenuTitle());
         auto menu = new QMenu(parentWidget);
@@ -62,8 +111,8 @@ public:
 
         auto shareAction = menu->addAction(helper->shareActionTitle());
         connect(shareAction, &QAction::triggered, this, [localFile, helper] {
-            helper->sendCommand(QByteArray("SHARE:"+localFile.toUtf8()+"\n"));
-        } );
+            helper->sendCommand(QByteArray("SHARE:" + localFile.toUtf8() + "\n"));
+        });
 
         if (!helper->copyPrivateLinkTitle().isEmpty()) {
             auto copyPrivateLinkAction = menu->addAction(helper->copyPrivateLinkTitle());
@@ -78,7 +127,6 @@ public:
                 helper->sendCommand(QByteArray("EMAIL_PRIVATE_LINK:" + localFile.toUtf8() + "\n"));
             });
         }
-
         return { menuaction };
     }
 

+ 11 - 0
shell_integration/dolphin/ownclouddolphinpluginhelper.cpp

@@ -59,6 +59,7 @@ void OwncloudDolphinPluginHelper::sendCommand(const char* data)
 
 void OwncloudDolphinPluginHelper::slotConnected()
 {
+    sendCommand("VERSION:\n");
     sendCommand("GET_STRINGS:\n");
 }
 
@@ -98,6 +99,16 @@ void OwncloudDolphinPluginHelper::slotReadyRead()
                 _strings[args[1]] = args.mid(2).join(QLatin1Char(':'));
             }
             continue;
+        } else if (line.startsWith("VERSION:")) {
+            auto args = line.split(':');
+            auto version = args.value(2);
+            _version = version;
+            if (!version.startsWith("1.")) {
+                // Incompatible version, disconnect forever
+                _connectTimer.stop();
+                _socket.disconnectFromServer();
+                return;
+            }
         }
         emit commandRecieved(line);
     }

+ 4 - 0
shell_integration/dolphin/ownclouddolphinpluginhelper.h

@@ -21,6 +21,7 @@
 #include <QObject>
 #include <QBasicTimer>
 #include <QLocalSocket>
+#include <QRegularExpression>
 #include "ownclouddolphinpluginhelper_export.h"
 
 class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QObject {
@@ -44,6 +45,8 @@ public:
     QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }
     QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_MENU_TITLE"]; }
 
+    QByteArray version() { return _version; }
+
 signals:
     void commandRecieved(const QByteArray &cmd);
 
@@ -61,4 +64,5 @@ private:
     QBasicTimer _connectTimer;
 
     QMap<QString, QString> _strings;
+    QByteArray _version;
 };

+ 29 - 3
src/gui/socketapi.cpp

@@ -54,7 +54,7 @@
 // This is the version that is returned when the client asks for the VERSION.
 // The first number should be changed if there is an incompatible change that breaks old clients.
 // The second number should be changed when there are new features.
-#define MIRALL_SOCKET_API_VERSION "1.0"
+#define MIRALL_SOCKET_API_VERSION "1.1"
 
 static inline QString removeTrailingSlash(QString path)
 {
@@ -535,7 +535,7 @@ void SocketApi::emailPrivateLink(const QString &link) const
         0);
 }
 
-void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener)
+void SocketApi::command_GET_STRINGS(const QString &argument, SocketListener *listener)
 {
     static std::array<std::pair<const char *, QString>, 5> strings { {
         { "SHARE_MENU_TITLE", tr("Share...") },
@@ -545,11 +545,37 @@ void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener)
     } };
     listener->sendMessage(QString("GET_STRINGS:BEGIN"));
     for (auto key_value : strings) {
-        listener->sendMessage(QString("STRING:%1:%2").arg(key_value.first, key_value.second));
+        if (argument.isEmpty() || argument == QLatin1String(key_value.first)) {
+            listener->sendMessage(QString("STRING:%1:%2").arg(key_value.first, key_value.second));
+        }
     }
     listener->sendMessage(QString("GET_STRINGS:END"));
 }
 
+void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListener *listener)
+{
+    listener->sendMessage(QString("GET_MENU_ITEMS:BEGIN"));
+    bool hasSeveralFiles = argument.contains(QLatin1Char('\x1e')); // Record Separator
+    Folder *syncFolder = hasSeveralFiles ? nullptr : FolderMan::instance()->folderForPath(argument);
+    if (syncFolder) {
+        QString systemPath = QDir::cleanPath(argument);
+        if (systemPath.endsWith(QLatin1Char('/'))) {
+            systemPath.truncate(systemPath.length() - 1);
+        }
+        QString relativePath = systemPath.mid(syncFolder->cleanPath().length() + 1);
+
+        SyncJournalFileRecord rec;
+        if (syncFolder->accountState()->isConnected() && syncFolder->journalDb()->getFileRecord(relativePath, &rec) && rec.isValid()) {
+            // If the file is on the DB, it is on the server
+            // TODO: check if sharing is allowed
+            listener->sendMessage(QLatin1String("MENU_ITEM:SHARE:") + tr("Share..."));
+            listener->sendMessage(QLatin1String("MENU_ITEM:COPY_PRIVATE_LINK:") + tr("Copy private link to clipboard"));
+            listener->sendMessage(QLatin1String("MENU_ITEM:EMAIL_PRIVATE_LINK:") + tr("Send private link by email..."));
+        }
+    }
+    listener->sendMessage(QString("GET_MENU_ITEMS:END"));
+}
+
 QString SocketApi::buildRegisterPathMessage(const QString &path)
 {
     QFileInfo fi(path);

+ 11 - 1
src/gui/socketapi.h

@@ -83,9 +83,19 @@ private:
     Q_INVOKABLE void command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
     Q_INVOKABLE void command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
 
-    /** Sends translated/branded strings that may be useful to the integration */
+    /** Sends translated/branded strings that may be useful to the integration
+     * Note: Deprecated, only used for compatibility with older shell_integrations (version 1.0)
+     */
     Q_INVOKABLE void command_GET_STRINGS(const QString &argument, SocketListener *listener);
 
+    /** Send the list of menu item. (added in version 1.1)
+     * argument is a list of files for which the menu should be shown, separated by '\x1e'
+     * Reply with  GET_MENU_ITEMS:BEGIN
+     * followed by several MENU_ITEM:[Action]::[Text]
+     * and ends with GET_MENU_ITEMS:END
+     */
+    Q_INVOKABLE void command_GET_MENU_ITEMS(const QString &argument, SocketListener *listener);
+
     QString buildRegisterPathMessage(const QString &path);
 
     QSet<QString> _registeredAliases;