Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell_integration/dolphin/ownclouddolphinactionplugin.cpp28
-rw-r--r--shell_integration/dolphin/ownclouddolphinpluginhelper.cpp10
-rw-r--r--shell_integration/dolphin/ownclouddolphinpluginhelper.h16
-rw-r--r--shell_integration/nautilus/syncstate.py67
-rw-r--r--src/gui/CMakeLists.txt2
-rw-r--r--src/gui/application.cpp4
-rw-r--r--src/gui/clipboard.mm14
-rw-r--r--src/gui/guiutility.cpp56
-rw-r--r--src/gui/guiutility.h41
-rw-r--r--src/gui/owncloudgui.cpp16
-rw-r--r--src/gui/owncloudgui.h11
-rw-r--r--src/gui/sharedialog.cpp4
-rw-r--r--src/gui/sharedialog.h3
-rw-r--r--src/gui/sharelinkwidget.cpp48
-rw-r--r--src/gui/shareusergroupwidget.cpp40
-rw-r--r--src/gui/shareusergroupwidget.h9
-rw-r--r--src/gui/shareusergroupwidget.ui14
-rw-r--r--src/gui/socketapi.cpp66
-rw-r--r--src/gui/socketapi.h14
-rw-r--r--src/libsync/account.cpp6
-rw-r--r--src/libsync/account.h3
-rw-r--r--src/libsync/propagatorjobs.cpp5
-rw-r--r--src/libsync/syncjournalfilerecord.cpp11
-rw-r--r--src/libsync/syncjournalfilerecord.h8
-rw-r--r--test/CMakeLists.txt1
25 files changed, 390 insertions, 107 deletions
diff --git a/shell_integration/dolphin/ownclouddolphinactionplugin.cpp b/shell_integration/dolphin/ownclouddolphinactionplugin.cpp
index dfb2bdff7..c47cb783d 100644
--- a/shell_integration/dolphin/ownclouddolphinactionplugin.cpp
+++ b/shell_integration/dolphin/ownclouddolphinactionplugin.cpp
@@ -24,6 +24,7 @@
#include <KIOCore/kfileitem.h>
#include <KIOCore/KFileItemListProperties>
#include <QtWidgets/QAction>
+#include <QtWidgets/QMenu>
#include <QtCore/QDir>
#include <QtCore/QTimer>
#include "ownclouddolphinpluginhelper.h"
@@ -53,12 +54,31 @@ public:
} ))
return {};
- auto act = new QAction(parentWidget);
- act->setText(helper->shareActionString());
- connect(act, &QAction::triggered, this, [localFile, helper] {
+ auto menuaction = new QAction(parentWidget);
+ menuaction->setText(helper->contextMenuTitle());
+ auto menu = new QMenu(parentWidget);
+ menuaction->setMenu(menu);
+
+ auto shareAction = menu->addAction(helper->shareActionTitle());
+ connect(shareAction, &QAction::triggered, this, [localFile, helper] {
helper->sendCommand(QByteArray("SHARE:"+localFile.toUtf8()+"\n"));
} );
- return { act };
+
+ if (!helper->copyPrivateLinkTitle().isEmpty()) {
+ auto copyPrivateLinkAction = menu->addAction(helper->copyPrivateLinkTitle());
+ connect(copyPrivateLinkAction, &QAction::triggered, this, [localFile, helper] {
+ helper->sendCommand(QByteArray("COPY_PRIVATE_LINK:" + localFile.toUtf8() + "\n"));
+ });
+ }
+
+ if (!helper->emailPrivateLinkTitle().isEmpty()) {
+ auto emailPrivateLinkAction = menu->addAction(helper->emailPrivateLinkTitle());
+ connect(emailPrivateLinkAction, &QAction::triggered, this, [localFile, helper] {
+ helper->sendCommand(QByteArray("EMAIL_PRIVATE_LINK:" + localFile.toUtf8() + "\n"));
+ });
+ }
+
+ return { menuaction };
}
};
diff --git a/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp b/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp
index 68c2a9c29..ae1705220 100644
--- a/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp
+++ b/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp
@@ -59,7 +59,7 @@ void OwncloudDolphinPluginHelper::sendCommand(const char* data)
void OwncloudDolphinPluginHelper::slotConnected()
{
- sendCommand("SHARE_MENU_TITLE:\n");
+ sendCommand("GET_STRINGS:\n");
}
void OwncloudDolphinPluginHelper::tryConnect()
@@ -92,9 +92,11 @@ void OwncloudDolphinPluginHelper::slotReadyRead()
QString file = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1);
_paths.append(file);
continue;
- } else if (line.startsWith("SHARE_MENU_TITLE:")) {
- auto col = line.indexOf(':');
- _shareActionString = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1);
+ } else if (line.startsWith("STRING:")) {
+ auto args = QString::fromUtf8(line).split(QLatin1Char(':'));
+ if (args.size() >= 3) {
+ _strings[args[1]] = args.mid(2).join(QLatin1Char(':'));
+ }
continue;
}
emit commandRecieved(line);
diff --git a/shell_integration/dolphin/ownclouddolphinpluginhelper.h b/shell_integration/dolphin/ownclouddolphinpluginhelper.h
index 26762caaf..7d4e7ba14 100644
--- a/shell_integration/dolphin/ownclouddolphinpluginhelper.h
+++ b/shell_integration/dolphin/ownclouddolphinpluginhelper.h
@@ -28,11 +28,22 @@ class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QO
public:
static OwncloudDolphinPluginHelper *instance();
- QString shareActionString() const { return _shareActionString; }
bool isConnected() const;
void sendCommand(const char *data);
QVector<QString> paths() const { return _paths; }
+ QString contextMenuTitle() const
+ {
+ return _strings.value("CONTEXT_MENU_TITLE", "ownCloud");
+ }
+ QString shareActionTitle() const
+ {
+ return _strings.value("SHARE_MENU_TITLE", "Share...");
+ }
+
+ QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_TITLE"]; }
+ QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_TITLE"]; }
+
signals:
void commandRecieved(const QByteArray &cmd);
@@ -47,6 +58,7 @@ private:
QLocalSocket _socket;
QByteArray _line;
QVector<QString> _paths;
- QString _shareActionString;
QBasicTimer _connectTimer;
+
+ QMap<QString, QString> _strings;
};
diff --git a/shell_integration/nautilus/syncstate.py b/shell_integration/nautilus/syncstate.py
index 7fb35b80c..9a0f35ed7 100644
--- a/shell_integration/nautilus/syncstate.py
+++ b/shell_integration/nautilus/syncstate.py
@@ -95,6 +95,9 @@ class SocketConnect(GObject.GObject):
print("Setting connected to %r." % self.connected )
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
print("Socket watch id: " + str(self._watch_id))
+
+ self.sendCommand('GET_STRINGS:\n')
+
return False # Don't run again
except Exception as e:
print("Could not connect to unix socket. " + str(e))
@@ -153,6 +156,13 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def __init__(self):
GObject.GObject.__init__(self)
+ self.strings = {}
+ socketConnect.addListener(self.handle_commands)
+
+ def handle_commands(self, action, args):
+ if action == 'STRING':
+ self.strings[args[0]] = ':'.join(args[1:])
+
def check_registered_paths(self, filename):
topLevelFolder = False
internalFile = False
@@ -178,7 +188,6 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
if len(files) != 1:
return
file = files[0]
- items = []
filename = get_local_path(file.get_uri())
# Check if its a folder (ends with an /), if yes add a "/"
@@ -190,12 +199,14 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
# Check if toplevel folder, we need to ignore those as they cannot be shared
topLevelFolder, internalFile = self.check_registered_paths(filename)
if topLevelFolder or not internalFile:
- return items
+ return []
entry = socketConnect.nautilusVFSFile_table.get(filename)
if not entry:
- return items
+ return []
+ # Currently 'sharable' also controls access to private link actions,
+ # and we definitely don't want to show them for IGNORED.
shareable = False
state = entry['state']
state_ok = state.startswith('OK')
@@ -212,22 +223,42 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
break
if not shareable:
- return items
-
- # Create a menu item
- labelStr = "Share with " + appname + "..."
- item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
- tip='Share file {} through {}'.format(file.get_name(), appname) )
- item.connect("activate", self.menu_share, file)
- items.append(item)
-
- return items
-
-
- def menu_share(self, menu, file):
+ return []
+
+ # Set up the 'ownCloud...' submenu
+ item_owncloud = Nautilus.MenuItem(
+ name='IntegrationMenu', label=self.strings.get('CONTEXT_MENU_TITLE', appname))
+ menu = Nautilus.Menu()
+ item_owncloud.set_submenu(menu)
+
+ # Add share menu option
+ item = Nautilus.MenuItem(
+ name='NautilusPython::ShareItem',
+ label=self.strings.get('SHARE_MENU_TITLE', 'Share...'))
+ item.connect("activate", self.context_menu_action, 'SHARE', file)
+ menu.append_item(item)
+
+ # Add permalink menu options, but hide these options for older clients
+ # that don't have these actions.
+ if 'COPY_PRIVATE_LINK_TITLE' in self.strings:
+ item_copyprivatelink = Nautilus.MenuItem(
+ name='CopyPrivateLink', label=self.strings.get('COPY_PRIVATE_LINK_TITLE', 'Copy private link to clipboard'))
+ item_copyprivatelink.connect("activate", self.context_menu_action, 'COPY_PRIVATE_LINK', file)
+ menu.append_item(item_copyprivatelink)
+
+ if 'EMAIL_PRIVATE_LINK_TITLE' in self.strings:
+ item_emailprivatelink = Nautilus.MenuItem(
+ name='EmailPrivateLink', label=self.strings.get('EMAIL_PRIVATE_LINK_TITLE', 'Send private link by email...'))
+ item_emailprivatelink.connect("activate", self.context_menu_action, 'EMAIL_PRIVATE_LINK', file)
+ menu.append_item(item_emailprivatelink)
+
+ return [item_owncloud]
+
+
+ def context_menu_action(self, menu, action, file):
filename = get_local_path(file.get_uri())
- print("Share file " + filename)
- socketConnect.sendCommand("SHARE:" + filename + "\n")
+ print("Context menu: " + action + ' ' + filename)
+ socketConnect.sendCommand(action + ":" + filename + "\n")
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index e40c03034..d123d58c5 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -94,6 +94,7 @@ set(client_SRCS
notificationwidget.cpp
notificationconfirmjob.cpp
servernotificationhandler.cpp
+ guiutility.cpp
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/oauth.cpp
@@ -129,7 +130,6 @@ IF( APPLE )
list(APPEND client_SRCS settingsdialogmac.cpp)
list(APPEND client_SRCS socketapisocket_mac.mm)
list(APPEND client_SRCS systray.mm)
- list(APPEND client_SRCS clipboard.mm)
if(SPARKLE_FOUND)
# Define this, we need to check in updater.cpp
diff --git a/src/gui/application.cpp b/src/gui/application.cpp
index 40bbfb1d7..2cb11ae30 100644
--- a/src/gui/application.cpp
+++ b/src/gui/application.cpp
@@ -206,8 +206,8 @@ Application::Application(int &argc, char **argv)
slotAccountStateAdded(ai.data());
}
- connect(FolderMan::instance()->socketApi(), SIGNAL(shareCommandReceived(QString, QString, bool)),
- _gui, SLOT(slotShowShareDialog(QString, QString, bool)));
+ connect(FolderMan::instance()->socketApi(), SIGNAL(shareCommandReceived(QString, QString)),
+ _gui, SLOT(slotShowShareDialog(QString, QString)));
// startup procedure.
connect(&_checkConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCheckConnection()));
diff --git a/src/gui/clipboard.mm b/src/gui/clipboard.mm
deleted file mode 100644
index 48ea81387..000000000
--- a/src/gui/clipboard.mm
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <QString>
-#import <Cocoa/Cocoa.h>
-
-namespace OCC {
-
-// https://github.com/owncloud/client/issues/3300
-void copyToPasteboard(const QString &string)
-{
- [[NSPasteboard generalPasteboard] clearContents];
- [[NSPasteboard generalPasteboard] setString:[NSString stringWithUTF8String:string.toUtf8().data()]
- forType:NSStringPboardType];
-}
-
-}
diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp
new file mode 100644
index 000000000..27fe54897
--- /dev/null
+++ b/src/gui/guiutility.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) by Christian Kamm <mail@ckamm.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "guiutility.h"
+
+#include <QClipboard>
+#include <QApplication>
+#include <QDesktopServices>
+#include <QMessageBox>
+
+using namespace OCC;
+
+bool Utility::openBrowser(const QUrl &url, QWidget *errorWidgetParent)
+{
+ if (!QDesktopServices::openUrl(url) && errorWidgetParent) {
+ QMessageBox::warning(
+ errorWidgetParent,
+ QCoreApplication::translate("utility", "Could not open browser"),
+ QCoreApplication::translate("utility",
+ "There was an error when launching the browser to go to "
+ "URL %1. Maybe no default browser is configured?")
+ .arg(url.toString()));
+ return false;
+ }
+ return true;
+}
+
+bool Utility::openEmailComposer(const QString &subject, const QString &body, QWidget *errorWidgetParent)
+{
+ QUrl url(QLatin1String("mailto: "));
+ url.setQueryItems({ { QLatin1String("subject"), subject },
+ { QLatin1String("body"), body } });
+
+ if (!QDesktopServices::openUrl(url) && errorWidgetParent) {
+ QMessageBox::warning(
+ errorWidgetParent,
+ QCoreApplication::translate("utility", "Could not open email client"),
+ QCoreApplication::translate("utility",
+ "There was an error when launching the email client to "
+ "create a new message. Maybe no default email client is "
+ "configured?"));
+ return false;
+ }
+ return true;
+}
diff --git a/src/gui/guiutility.h b/src/gui/guiutility.h
new file mode 100644
index 000000000..55f808ac2
--- /dev/null
+++ b/src/gui/guiutility.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) by Christian Kamm <mail@ckamm.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef GUIUTILITY_H
+#define GUIUTILITY_H
+
+#include <QString>
+#include <QUrl>
+#include <QWidget>
+
+namespace OCC {
+namespace Utility {
+
+ /** Open an url in the browser.
+ *
+ * If launching the browser fails, display a message.
+ */
+ bool openBrowser(const QUrl &url, QWidget *errorWidgetParent);
+
+ /** Start composing a new email message.
+ *
+ * If launching the email program fails, display a message.
+ */
+ bool openEmailComposer(const QString &subject, const QString &body,
+ QWidget *errorWidgetParent);
+
+} // namespace Utility
+} // namespace OCC
+
+#endif
diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp
index 684689dae..db005f422 100644
--- a/src/gui/owncloudgui.cpp
+++ b/src/gui/owncloudgui.cpp
@@ -33,6 +33,7 @@
#include "accountstate.h"
#include "openfilemanager.h"
#include "accountmanager.h"
+#include "syncjournalfilerecord.h"
#include "creds/abstractcredentials.h"
#include <QDesktopServices>
@@ -1039,7 +1040,7 @@ void ownCloudGui::raiseDialog(QWidget *raiseWidget)
}
-void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &localPath, bool resharingAllowed)
+void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &localPath)
{
const auto folder = FolderMan::instance()->folderForPath(localPath);
if (!folder) {
@@ -1052,6 +1053,17 @@ void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &l
const auto accountState = folder->accountState();
+ const QString file = localPath.mid(folder->cleanPath().length() + 1);
+ SyncJournalFileRecord fileRecord = folder->journalDb()->getFileRecord(file);
+
+ bool resharingAllowed = true; // lets assume the good
+ if (fileRecord.isValid()) {
+ // check the permission: Is resharing allowed?
+ if (!fileRecord._remotePerm.contains('R')) {
+ resharingAllowed = false;
+ }
+ }
+
// As a first approximation, set the set of permissions that can be granted
// either to everything (resharing allowed) or nothing (no resharing).
//
@@ -1072,7 +1084,7 @@ void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &l
w = _shareDialogs[localPath];
} else {
qCInfo(lcApplication) << "Opening share dialog" << sharePath << localPath << maxSharingPermissions;
- w = new ShareDialog(accountState, sharePath, localPath, maxSharingPermissions);
+ w = new ShareDialog(accountState, sharePath, localPath, maxSharingPermissions, fileRecord.numericFileId());
w->setAttribute(Qt::WA_DeleteOnClose, true);
_shareDialogs[localPath] = w;
diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h
index c0238a761..c24fa173a 100644
--- a/src/gui/owncloudgui.h
+++ b/src/gui/owncloudgui.h
@@ -86,7 +86,16 @@ public slots:
void slotOpenPath(const QString &path);
void slotAccountStateChanged();
void slotTrayMessageIfServerUnsupported(Account *account);
- void slotShowShareDialog(const QString &sharePath, const QString &localPath, bool resharingAllowed);
+
+ /**
+ * Open a share dialog for a file or folder.
+ *
+ * sharePath is the full remote path to the item,
+ * localPath is the absolute local path to it (so not relative
+ * to the folder).
+ */
+ void slotShowShareDialog(const QString &sharePath, const QString &localPath);
+
void slotRemoveDestroyedShareDialogs();
private slots:
diff --git a/src/gui/sharedialog.cpp b/src/gui/sharedialog.cpp
index 17399d1a4..24a912fdc 100644
--- a/src/gui/sharedialog.cpp
+++ b/src/gui/sharedialog.cpp
@@ -38,6 +38,7 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
const QString &sharePath,
const QString &localPath,
SharePermissions maxSharingPermissions,
+ const QByteArray &numericFileId,
QWidget *parent)
: QDialog(parent)
, _ui(new Ui::ShareDialog)
@@ -45,6 +46,7 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
, _sharePath(sharePath)
, _localPath(localPath)
, _maxSharingPermissions(maxSharingPermissions)
+ , _numericFileId(numericFileId)
, _linkWidget(NULL)
, _userGroupWidget(NULL)
, _progressIndicator(NULL)
@@ -192,7 +194,7 @@ void ShareDialog::showSharingUi()
&& _accountState->account()->serverVersionInt() >= Account::makeServerVersion(8, 2, 0);
if (userGroupSharing) {
- _userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this);
+ _userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _numericFileId, this);
_ui->shareWidgets->addTab(_userGroupWidget, tr("Users and Groups"));
_userGroupWidget->getShares();
}
diff --git a/src/gui/sharedialog.h b/src/gui/sharedialog.h
index dac7b9841..50aeea2e1 100644
--- a/src/gui/sharedialog.h
+++ b/src/gui/sharedialog.h
@@ -43,6 +43,7 @@ public:
const QString &sharePath,
const QString &localPath,
SharePermissions maxSharingPermissions,
+ const QByteArray &numericFileId,
QWidget *parent = 0);
~ShareDialog();
@@ -60,8 +61,8 @@ private:
QPointer<AccountState> _accountState;
QString _sharePath;
QString _localPath;
-
SharePermissions _maxSharingPermissions;
+ QByteArray _numericFileId;
ShareLinkWidget *_linkWidget;
ShareUserGroupWidget *_userGroupWidget;
diff --git a/src/gui/sharelinkwidget.cpp b/src/gui/sharelinkwidget.cpp
index b2f6d7c7d..f3b8e33da 100644
--- a/src/gui/sharelinkwidget.cpp
+++ b/src/gui/sharelinkwidget.cpp
@@ -19,6 +19,7 @@
#include "capabilities.h"
#include "sharemanager.h"
+#include "guiutility.h"
#include "QProgressIndicator.h"
#include <QBuffer>
@@ -494,51 +495,18 @@ void ShareLinkWidget::slotCheckBoxExpireClicked()
}
}
-#ifdef Q_OS_MAC
-extern void copyToPasteboard(const QString &string);
-#endif
-
-void ShareLinkWidget::copyShareLink(const QUrl &url)
-{
-#ifdef Q_OS_MAC
- copyToPasteboard(url.toString());
-#else
- QClipboard *clipboard = QApplication::clipboard();
- clipboard->setText(url.toString());
-#endif
-}
-
void ShareLinkWidget::emailShareLink(const QUrl &url)
{
QString fileName = _sharePath.mid(_sharePath.lastIndexOf('/') + 1);
-
- if (!QDesktopServices::openUrl(QUrl(QString(
- "mailto: "
- "?subject=I shared %1 with you"
- "&body=%2")
- .arg(
- fileName,
- url.toString()),
- QUrl::TolerantMode))) {
- QMessageBox::warning(
- this,
- tr("Could not open email client"),
- tr("There was an error when launching the email client to "
- "create a new message. Maybe no default email client is "
- "configured?"));
- }
+ Utility::openEmailComposer(
+ QString("I shared %1 with you").arg(fileName),
+ url.toString(),
+ this);
}
void ShareLinkWidget::openShareLink(const QUrl &url)
{
- if (!QDesktopServices::openUrl(url)) {
- QMessageBox::warning(
- this,
- tr("Could not open browser"),
- tr("There was an error when launching the browser to "
- "view the public link share. Maybe no default browser is "
- "configured?"));
- }
+ Utility::openBrowser(url, this);
}
void ShareLinkWidget::slotShareLinkButtonTriggered(QAction *action)
@@ -546,9 +514,9 @@ void ShareLinkWidget::slotShareLinkButtonTriggered(QAction *action)
auto share = sender()->property(propertyShareC).value<QSharedPointer<LinkShare>>();
if (action == _copyLinkAction) {
- copyShareLink(share->getLink());
+ QApplication::clipboard()->setText(share->getLink().toString());
} else if (action == _copyDirectLinkAction) {
- copyShareLink(share->getDirectDownloadLink());
+ QApplication::clipboard()->setText(share->getDirectDownloadLink().toString());
} else if (action == _emailLinkAction) {
emailShareLink(share->getLink());
} else if (action == _emailDirectLinkAction) {
diff --git a/src/gui/shareusergroupwidget.cpp b/src/gui/shareusergroupwidget.cpp
index 1351f888f..8cecac2a4 100644
--- a/src/gui/shareusergroupwidget.cpp
+++ b/src/gui/shareusergroupwidget.cpp
@@ -22,7 +22,7 @@
#include "theme.h"
#include "configfile.h"
#include "capabilities.h"
-
+#include "guiutility.h"
#include "thumbnailjob.h"
#include "sharee.h"
#include "sharemanager.h"
@@ -39,6 +39,8 @@
#include <QPropertyAnimation>
#include <QMenu>
#include <QAction>
+#include <QDesktopServices>
+#include <QMessageBox>
namespace OCC {
@@ -46,6 +48,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
const QString &sharePath,
const QString &localPath,
SharePermissions maxSharingPermissions,
+ const QByteArray &numericFileId,
QWidget *parent)
: QWidget(parent)
, _ui(new Ui::ShareUserGroupWidget)
@@ -53,6 +56,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
, _sharePath(sharePath)
, _localPath(localPath)
, _maxSharingPermissions(maxSharingPermissions)
+ , _numericFileId(numericFileId)
, _disableCompleterActivated(false)
{
setAttribute(Qt::WA_DeleteOnClose);
@@ -80,6 +84,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
connect(_manager, SIGNAL(shareCreated(QSharedPointer<Share>)), SLOT(getShares()));
connect(_manager, SIGNAL(serverError(int, QString)), this, SLOT(displayError(int, QString)));
connect(_ui->shareeLineEdit, SIGNAL(returnPressed()), SLOT(slotLineEditReturn()));
+ connect(_ui->privateLinkText, SIGNAL(linkActivated(QString)), SLOT(slotPrivateLinkShare()));
// By making the next two QueuedConnections we can override
// the strings the completer sets on the line edit.
@@ -222,6 +227,21 @@ void ShareUserGroupWidget::slotAdjustScrollWidgetSize()
}
}
+void ShareUserGroupWidget::slotPrivateLinkShare()
+{
+ auto menu = new QMenu(this);
+ menu->setAttribute(Qt::WA_DeleteOnClose);
+
+ menu->addAction(tr("Open link in browser"),
+ this, SLOT(slotPrivateLinkOpenBrowser()));
+ menu->addAction(tr("Copy link to clipboard"),
+ this, SLOT(slotPrivateLinkCopy()));
+ menu->addAction(tr("Send link by email"),
+ this, SLOT(slotPrivateLinkEmail()));
+
+ menu->exec(QCursor::pos());
+}
+
void ShareUserGroupWidget::slotShareesReady()
{
_pi_sharee.stopAnimation();
@@ -301,6 +321,24 @@ void ShareUserGroupWidget::displayError(int code, const QString &message)
_ui->shareeLineEdit->setEnabled(true);
}
+void ShareUserGroupWidget::slotPrivateLinkOpenBrowser()
+{
+ Utility::openBrowser(_account->filePermalinkUrl(_numericFileId), this);
+}
+
+void ShareUserGroupWidget::slotPrivateLinkCopy()
+{
+ QApplication::clipboard()->setText(_account->filePermalinkUrl(_numericFileId).toString());
+}
+
+void ShareUserGroupWidget::slotPrivateLinkEmail()
+{
+ Utility::openEmailComposer(
+ tr("I shared something with you"),
+ _account->filePermalinkUrl(_numericFileId).toString(),
+ this);
+}
+
ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
SharePermissions maxSharingPermissions,
bool isFile,
diff --git a/src/gui/shareusergroupwidget.h b/src/gui/shareusergroupwidget.h
index 36639196d..fbc2bdfa4 100644
--- a/src/gui/shareusergroupwidget.h
+++ b/src/gui/shareusergroupwidget.h
@@ -57,6 +57,7 @@ public:
const QString &sharePath,
const QString &localPath,
SharePermissions maxSharingPermissions,
+ const QByteArray &numericFileId,
QWidget *parent = 0);
~ShareUserGroupWidget();
@@ -75,19 +76,25 @@ private slots:
void slotCompleterHighlighted(const QModelIndex &index);
void slotShareesReady();
void slotAdjustScrollWidgetSize();
+ void slotPrivateLinkShare();
void displayError(int code, const QString &message);
+ void slotPrivateLinkOpenBrowser();
+ void slotPrivateLinkCopy();
+ void slotPrivateLinkEmail();
+
private:
Ui::ShareUserGroupWidget *_ui;
AccountPtr _account;
QString _sharePath;
QString _localPath;
+ SharePermissions _maxSharingPermissions;
+ QByteArray _numericFileId;
QCompleter *_completer;
ShareeModel *_completerModel;
QTimer _completionTimer;
- SharePermissions _maxSharingPermissions;
bool _isFile;
bool _disableCompleterActivated; // in order to avoid that we share the contents twice
ShareManager *_manager;
diff --git a/src/gui/shareusergroupwidget.ui b/src/gui/shareusergroupwidget.ui
index 615c5b2f1..028d897ce 100644
--- a/src/gui/shareusergroupwidget.ui
+++ b/src/gui/shareusergroupwidget.ui
@@ -94,14 +94,24 @@
<rect>
<x>0</x>
<y>0</y>
- <width>395</width>
- <height>221</height>
+ <width>377</width>
+ <height>169</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3"/>
</widget>
</widget>
</item>
+ <item>
+ <widget class="QLabel" name="privateLinkText">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;You can direct people to this shared file or folder &lt;a href=&quot;private link menu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;by giving them a private link&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp
index 82705c59a..bf4e75d3b 100644
--- a/src/gui/socketapi.cpp
+++ b/src/gui/socketapi.cpp
@@ -32,7 +32,9 @@
#include "account.h"
#include "capabilities.h"
#include "asserts.h"
+#include "guiutility.h"
+#include <array>
#include <QBitArray>
#include <QUrl>
#include <QMetaMethod>
@@ -45,6 +47,8 @@
#include <QLocalSocket>
#include <QStringBuilder>
+#include <QClipboard>
+
#include <sqlite3.h>
@@ -436,19 +440,10 @@ void SocketApi::command_SHARE(const QString &localFile, SocketListener *listener
return;
}
- SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(localFileClean);
-
- bool allowReshare = true; // lets assume the good
- if (rec.isValid()) {
- // check the permission: Is resharing allowed?
- if (!rec._remotePerm.contains('R')) {
- allowReshare = false;
- }
- }
const QString message = QLatin1String("SHARE:OK:") + QDir::toNativeSeparators(localFile);
listener->sendMessage(message);
- emit shareCommandReceived(remotePath, localFileClean, allowReshare);
+ emit shareCommandReceived(remotePath, localFileClean);
}
}
@@ -514,6 +509,39 @@ void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketListener *listen
listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()));
}
+void SocketApi::command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *)
+{
+ auto url = getPrivateLinkUrl(localFile);
+ if (!url.isEmpty()) {
+ QApplication::clipboard()->setText(url.toString());
+ }
+}
+
+void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *)
+{
+ auto url = getPrivateLinkUrl(localFile);
+ if (!url.isEmpty()) {
+ Utility::openEmailComposer(
+ tr("I shared something with you"),
+ url.toString(),
+ 0);
+ }
+}
+
+void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener)
+{
+ static std::array<std::pair<const char *, QString>, 5> strings { {
+ { "SHARE_MENU_TITLE", tr("Share with %1...", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()) },
+ { "APPNAME", Theme::instance()->appNameGUI() },
+ { "CONTEXT_MENU_TITLE", Theme::instance()->appNameGUI() },
+ { "COPY_PRIVATE_LINK_TITLE", tr("Copy private link to clipboard") },
+ { "EMAIL_PRIVATE_LINK_TITLE", tr("Send private link by email...") },
+ } };
+ for (auto key_value : strings) {
+ listener->sendMessage(QString("STRING:%1:%2").arg(key_value.first, key_value.second));
+ }
+}
+
QString SocketApi::buildRegisterPathMessage(const QString &path)
{
QFileInfo fi(path);
@@ -522,4 +550,22 @@ QString SocketApi::buildRegisterPathMessage(const QString &path)
return message;
}
+QUrl SocketApi::getPrivateLinkUrl(const QString &localFile) const
+{
+ Folder *shareFolder = FolderMan::instance()->folderForPath(localFile);
+ if (!shareFolder) {
+ qCWarning(lcSocketApi) << "Unknown path" << localFile;
+ return QUrl();
+ }
+
+ const QString localFileClean = QDir::cleanPath(localFile);
+ const QString file = localFileClean.mid(shareFolder->cleanPath().length() + 1);
+
+ SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(file);
+ if (rec.isValid()) {
+ return shareFolder->accountState()->account()->filePermalinkUrl(rec.numericFileId());
+ }
+ return QUrl();
+}
+
} // namespace OCC
diff --git a/src/gui/socketapi.h b/src/gui/socketapi.h
index e0b4c3ada..d4f6e7bf8 100644
--- a/src/gui/socketapi.h
+++ b/src/gui/socketapi.h
@@ -55,8 +55,7 @@ public slots:
void slotRegisterPath(const QString &alias);
signals:
- void shareCommandReceived(const QString &sharePath, const QString &localPath, bool resharingAllowed);
- void shareUserGroupCommandReceived(const QString &sharePath, const QString &localPath, bool resharingAllowed);
+ void shareCommandReceived(const QString &sharePath, const QString &localPath);
private slots:
void slotNewConnection();
@@ -70,13 +69,22 @@ private:
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString &argument, SocketListener *listener);
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString &argument, SocketListener *listener);
- Q_INVOKABLE void command_SHARE(const QString &localFile, SocketListener *listener);
Q_INVOKABLE void command_VERSION(const QString &argument, SocketListener *listener);
Q_INVOKABLE void command_SHARE_STATUS(const QString &localFile, SocketListener *listener);
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString &argument, SocketListener *listener);
+
+ // The context menu actions
+ Q_INVOKABLE void command_SHARE(const QString &localFile, SocketListener *listener);
+ 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 */
+ Q_INVOKABLE void command_GET_STRINGS(const QString &argument, SocketListener *listener);
+
QString buildRegisterPathMessage(const QString &path);
+ QUrl getPrivateLinkUrl(const QString &localFile) const;
QSet<QString> _registeredAliases;
QList<SocketListener> _listeners;
diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp
index 19ace5ed0..ee83e3dee 100644
--- a/src/libsync/account.cpp
+++ b/src/libsync/account.cpp
@@ -160,6 +160,12 @@ QUrl Account::davUrl() const
return Utility::concatUrlPath(url(), davPath());
}
+QUrl Account::filePermalinkUrl(const QByteArray &numericFileId) const
+{
+ return Utility::concatUrlPath(url(),
+ QLatin1String("/index.php/f/") + QUrl::toPercentEncoding(QString::fromLatin1(numericFileId)));
+}
+
/**
* clear all cookies. (Session cookies or not)
*/
diff --git a/src/libsync/account.h b/src/libsync/account.h
index 66e0d1be6..d48b27b15 100644
--- a/src/libsync/account.h
+++ b/src/libsync/account.h
@@ -107,6 +107,9 @@ public:
/** Returns webdav entry URL, based on url() */
QUrl davUrl() const;
+ /** Returns a permalink url for a file */
+ QUrl filePermalinkUrl(const QByteArray &numericFileId) const;
+
/** Holds the accounts credentials */
AbstractCredentials *credentials() const;
void setCredentials(AbstractCredentials *cred);
diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp
index 5cf17b26a..65d7ef86a 100644
--- a/src/libsync/propagatorjobs.cpp
+++ b/src/libsync/propagatorjobs.cpp
@@ -42,6 +42,11 @@ Q_LOGGING_CATEGORY(lcPropagateLocalRemove, "sync.propagator.localremove", QtInfo
Q_LOGGING_CATEGORY(lcPropagateLocalMkdir, "sync.propagator.localmkdir", QtInfoMsg)
Q_LOGGING_CATEGORY(lcPropagateLocalRename, "sync.propagator.localrename", QtInfoMsg)
+QByteArray localFileIdFromFullId(const QByteArray &id)
+{
+ return id.left(8);
+}
+
/**
* Code inspired from Qt5's QDir::removeRecursively
* The code will update the database in case of error.
diff --git a/src/libsync/syncjournalfilerecord.cpp b/src/libsync/syncjournalfilerecord.cpp
index b2c65931d..96bde3eb6 100644
--- a/src/libsync/syncjournalfilerecord.cpp
+++ b/src/libsync/syncjournalfilerecord.cpp
@@ -109,6 +109,17 @@ SyncFileItem SyncJournalFileRecord::toSyncFileItem()
return item;
}
+QByteArray SyncJournalFileRecord::numericFileId() const
+{
+ // Use the id up until the first non-numeric character
+ for (int i = 0; i < _fileId.size(); ++i) {
+ if (_fileId[i] < '0' || _fileId[i] > '9') {
+ return _fileId.left(i);
+ }
+ }
+ return _fileId;
+}
+
bool SyncJournalErrorBlacklistRecord::isValid() const
{
return !_file.isEmpty()
diff --git a/src/libsync/syncjournalfilerecord.h b/src/libsync/syncjournalfilerecord.h
index c7579683b..8f3333180 100644
--- a/src/libsync/syncjournalfilerecord.h
+++ b/src/libsync/syncjournalfilerecord.h
@@ -48,6 +48,14 @@ public:
return !_path.isEmpty();
}
+ /** Returns the numeric part of the full id in _fileId.
+ *
+ * On the server this is sometimes known as the internal file id.
+ *
+ * It is used in the construction of private links.
+ */
+ QByteArray numericFileId() const;
+
QString _path;
quint64 _inode;
QDateTime _modtime;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ac0f1dca1..860f140e9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -55,6 +55,7 @@ list(APPEND FolderMan_SRC ../src/gui/socketapi.cpp )
list(APPEND FolderMan_SRC ../src/gui/accountstate.cpp )
list(APPEND FolderMan_SRC ../src/gui/syncrunfilelog.cpp )
list(APPEND FolderMan_SRC ../src/gui/lockwatcher.cpp )
+list(APPEND FolderMan_SRC ../src/gui/guiutility.cpp )
list(APPEND FolderMan_SRC ${FolderWatcher_SRC})
list(APPEND FolderMan_SRC stub.cpp )
owncloud_add_test(FolderMan "${FolderMan_SRC}")