mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-21 20:45:51 +03:00
Introduce private link sharing #5023
* SocketAPI has COPL_LOCAL_LINK / EMAIL_LOCAL_LINK commands * The nautilus and dolphing shell integrations show a submenu from which one can share as well as access the private link. * The SocketAPI provides a new GET_STRINGS command to access localized strings. * The private link can also be accessed from the user/group sharing dialog. * The numeric file id is extracted from the full id to create the private link url.
This commit is contained in:
parent
d01065b9a1
commit
0238a29c7c
25 changed files with 386 additions and 103 deletions
|
@ -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 };
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
return []
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
return items
|
||||
# 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 menu_share(self, menu, file):
|
||||
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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
56
src/gui/guiutility.cpp
Normal file
56
src/gui/guiutility.cpp
Normal file
|
@ -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;
|
||||
}
|
41
src/gui/guiutility.h
Normal file
41
src/gui/guiutility.h
Normal file
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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><html><head/><body><p>You can direct people to this shared file or folder <a href="private link menu"><span style=" text-decoration: underline; color:#0000ff;">by giving them a private link</span></a>.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}")
|
||||
|
|
Loading…
Reference in a new issue