mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-23 13:35:58 +03:00
Implement URI handler for local file editing
Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
parent
4f85f7a45d
commit
d42d3c057f
18 changed files with 269 additions and 29 deletions
|
@ -5,6 +5,7 @@ set( APPLICATION_DOMAIN "nextcloud.com" )
|
|||
set( APPLICATION_VENDOR "Nextcloud GmbH" )
|
||||
set( APPLICATION_UPDATE_URL "https://updates.nextcloud.org/client/" CACHE STRING "URL for updater" )
|
||||
set( APPLICATION_HELP_URL "" CACHE STRING "URL for the help menu" )
|
||||
set( APPLICATION_URI_HANDLER_SCHEME "nc")
|
||||
|
||||
if(APPLE AND APPLICATION_NAME STREQUAL "Nextcloud" AND EXISTS "${CMAKE_SOURCE_DIR}/theme/colored/Nextcloud-macOS-icon.svg")
|
||||
set( APPLICATION_ICON_NAME "Nextcloud-macOS" )
|
||||
|
|
|
@ -190,6 +190,19 @@
|
|||
<!-- Property to disable update checks -->
|
||||
<RegistryValue Type="integer" Name="skipUpdateCheck" Value="[SKIPAUTOUPDATE]" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
<!-- Register URI handler -->
|
||||
<Component Id="RegistryUriHandler" Guid="*" Win64="$(var.PlatformWin64)">
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\$(var.AppCommandOpenUrlScheme)" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes">
|
||||
<RegistryValue Type="string" Value="URL:$(var.AppName) Protocol" />
|
||||
<RegistryValue Type="string" Name="URL Protocol" Value="" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\$(var.AppCommandOpenUrlScheme)\DefaultIcon" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes">
|
||||
<RegistryValue Type="string" Value="[INSTALLDIR]$(var.AppExe)" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\$(var.AppCommandOpenUrlScheme)\shell\open\command" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes">
|
||||
<RegistryValue Type="string" Value=""[INSTALLDIR]$(var.AppExe)" "%1"" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
|
@ -200,6 +213,7 @@
|
|||
|
||||
<ComponentRef Id="RegistryVersionInfo" />
|
||||
<ComponentRef Id="RegistryDefaultSettings" />
|
||||
<ComponentRef Id="RegistryUriHandler" />
|
||||
|
||||
<Feature Id="ShellExtensions" Title="Integration for Windows Explorer"
|
||||
Description="This feature requires a reboot." >
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
<?define AppHelpLink = "https://@APPLICATION_DOMAIN@/" ?>
|
||||
<?define AppInfoLink = "$(var.AppHelpLink)" ?>
|
||||
|
||||
<?define AppCommandOpenUrlScheme = "@APPLICATION_URI_HANDLER_SCHEME@" ?>
|
||||
|
||||
<!-- Custom license: To use it, also remove the "Skip the license page" stuff in the <UI> section
|
||||
and uncomment <WixVariable Id="WixUILicenseRtf"...
|
||||
|
|
|
@ -76,6 +76,17 @@
|
|||
</dict>
|
||||
</array>
|
||||
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>@APPLICATION_NAME@ Edit Locally</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>@APPLICATION_URI_HANDLER_SCHEME@</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#cmakedefine APPLICATION_OCSP_STAPLING_ENABLED "@APPLICATION_OCSP_STAPLING_ENABLED@"
|
||||
#cmakedefine APPLICATION_FORBID_BAD_SSL "@APPLICATION_FORBID_BAD_SSL@"
|
||||
#define APPLICATION_DOTVIRTUALFILE_SUFFIX "." APPLICATION_VIRTUALFILE_SUFFIX
|
||||
#define APPLICATION_URI_HANDLER_SCHEME "@APPLICATION_URI_HANDLER_SCHEME@"
|
||||
#cmakedefine01 ENFORCE_VIRTUAL_FILES_SYNC_FOLDER
|
||||
#cmakedefine DO_NOT_USE_PROXY "@DO_NOT_USE_PROXY@"
|
||||
|
||||
|
|
|
@ -440,3 +440,23 @@ Files that must be removed from the local storage only, need to be dehydrated vi
|
|||
|
||||
.. note::
|
||||
* End-to-end Encryption works with Virtual Files (VFS) but only on a per-folder level. Folders with E2EE can be made available offline in their entirety, but the individual files in them can not be retrieved on demand. This is mainly due to two technical reasons. First, the Windows VFS API is not designed for handling encrypted files. Second, while the VFS is designed to deal mostly with large files, E2EE is mostly recommended for use with small files as encrypting and decrypting large files puts large demands on the computer infrastructure.
|
||||
|
||||
Local file editing
|
||||
------------------
|
||||
|
||||
The Nextcloud desktop GUI client supports local editing when opening a URL that starts with
|
||||
a scheme ``nc://`` followed by an ``open`` command, followed by a user email (with port when needed),
|
||||
followed by file path relative to remote root.
|
||||
|
||||
Examples of URLs that Nextcloud can handle if the user email and a path to a file is correct:
|
||||
- ``nc://open/admin@example.cloud:8080/Photos/lovely.jpg``
|
||||
- ``nc://open/user@example.cloud/Photos/lovely.jpg``
|
||||
- ``nc://open/user@example.cloud/Documents/sheets/report.xlsx``
|
||||
- ``nc://open/user@example.cloud/Documents/docs/document.docx``
|
||||
|
||||
.. note::
|
||||
* All the file paths that begin after user email are relative to remote root (``/``).
|
||||
* The server is responsible for generating a correct URL that a user then clicks to edit file locally.
|
||||
* The Nextcloud desktop client is registered in macOS, Linux, and Windows as a custom URI handler for the ``nc://`` scheme.
|
||||
* The URL is parsed and validated by Nextcloud desktop client, so, opening an incorrectly formatted URL will not have any effect.
|
||||
* The port after user email is necessary if the default :80 or :443 is not used. The rule of thumb is to always have a port added if you need it when accessing your server via Web UI
|
|
@ -1,14 +1,14 @@
|
|||
[Desktop Entry]
|
||||
Categories=Utility;X-SuSE-SyncUtility;
|
||||
Type=Application
|
||||
Exec=@APPLICATION_EXECUTABLE@
|
||||
Exec=@APPLICATION_EXECUTABLE@ %u
|
||||
Name=@APPLICATION_NAME@ Desktop
|
||||
Comment=@APPLICATION_NAME@ desktop synchronization client
|
||||
GenericName=Folder Sync
|
||||
Icon=@APPLICATION_ICON_NAME@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||
X-GNOME-Autostart-Delay=3
|
||||
MimeType=application/vnd.@APPLICATION_EXECUTABLE@;
|
||||
MimeType=application/vnd.@APPLICATION_EXECUTABLE@;x-scheme-handler/@APPLICATION_URI_HANDLER_SCHEME@;
|
||||
Actions=Quit;
|
||||
|
||||
# Translations
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <qtlockedfile.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileOpenEvent>
|
||||
#include <QSharedMemory>
|
||||
#include <QWidget>
|
||||
|
||||
|
@ -119,16 +118,6 @@ QtSingleApplication::~QtSingleApplication()
|
|||
lockfile.unlock();
|
||||
}
|
||||
|
||||
bool QtSingleApplication::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
auto *foe = static_cast<QFileOpenEvent*>(event);
|
||||
emit fileOpenRequest(foe->file());
|
||||
return true;
|
||||
}
|
||||
return QApplication::event(event);
|
||||
}
|
||||
|
||||
bool QtSingleApplication::isRunning(qint64 pid)
|
||||
{
|
||||
if (pid == -1) {
|
||||
|
|
|
@ -50,7 +50,6 @@ public:
|
|||
|
||||
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
|
||||
QWidget* activationWindow() const;
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
QString applicationId() const;
|
||||
void setBlock(bool value);
|
||||
|
|
|
@ -247,6 +247,11 @@ namespace Utility {
|
|||
*/
|
||||
OCSYNC_EXPORT QString getCurrentUserName();
|
||||
|
||||
/**
|
||||
* @brief Registers the desktop app as a handler for a custom URI to enable local editing
|
||||
*/
|
||||
OCSYNC_EXPORT void registerUriHandlerForLocalEditing();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
OCSYNC_EXPORT bool registryKeyExists(HKEY hRootKey, const QString &subKey);
|
||||
OCSYNC_EXPORT QVariant registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName);
|
||||
|
|
|
@ -141,4 +141,6 @@ QString Utility::getCurrentUserName()
|
|||
return {};
|
||||
}
|
||||
|
||||
void Utility::registerUriHandlerForLocalEditing() { /* URI handler is registered via MacOSXBundleInfo.plist.in */ }
|
||||
|
||||
} // namespace OCC
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <QStandardPaths>
|
||||
#include <QtGlobal>
|
||||
#include <QProcess>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
|
@ -113,4 +114,26 @@ QString Utility::getCurrentUserName()
|
|||
return {};
|
||||
}
|
||||
|
||||
void Utility::registerUriHandlerForLocalEditing()
|
||||
{
|
||||
const auto appImagePath = qEnvironmentVariable("APPIMAGE");
|
||||
const auto runningInsideAppImage = !appImagePath.isNull() && QFile::exists(appImagePath);
|
||||
|
||||
if (!runningInsideAppImage) {
|
||||
// only register x-scheme-handler if running inside appImage
|
||||
return;
|
||||
}
|
||||
|
||||
// mirall.desktop.in must have an x-scheme-handler mime type specified
|
||||
const QString desktopFileName = QLatin1String(LINUX_APPLICATION_ID) + QLatin1String(".desktop");
|
||||
QProcess process;
|
||||
const QStringList args = {
|
||||
QLatin1String("default"),
|
||||
desktopFileName,
|
||||
QStringLiteral("x-scheme-handler/%1").arg(QStringLiteral(APPLICATION_URI_HANDLER_SCHEME))
|
||||
};
|
||||
process.start(QStringLiteral("xdg-mime"), args, QIODevice::ReadOnly);
|
||||
process.waitForFinished();
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
|
|
@ -448,6 +448,8 @@ QString Utility::getCurrentUserName()
|
|||
return QString::fromWCharArray(username);
|
||||
}
|
||||
|
||||
void Utility::registerUriHandlerForLocalEditing() { /* URI handler is registered via Nextcloud.wxs */ }
|
||||
|
||||
Utility::NtfsPermissionLookupRAII::NtfsPermissionLookupRAII()
|
||||
{
|
||||
qt_ntfs_permission_lookup++;
|
||||
|
|
|
@ -406,6 +406,8 @@ Application::Application(int &argc, char **argv)
|
|||
connect(_gui.data(), &ownCloudGui::isShowingSettingsDialog, this, &Application::slotGuiIsShowingSettings);
|
||||
|
||||
_gui->createTray();
|
||||
|
||||
handleEditLocallyFromOptions();
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
|
@ -572,6 +574,8 @@ void Application::slotParseMessage(const QString &msg, QObject *)
|
|||
qApp->quit();
|
||||
}
|
||||
|
||||
handleEditLocallyFromOptions();
|
||||
|
||||
} else if (msg.startsWith(QLatin1String("MSG_SHOWMAINDIALOG"))) {
|
||||
qCInfo(lcApplication) << "Running for" << _startedAt.elapsed() / 1000.0 << "sec";
|
||||
if (_startedAt.elapsed() < 10 * 1000) {
|
||||
|
@ -647,7 +651,17 @@ void Application::parseOptions(const QStringList &options)
|
|||
} else if (option.endsWith(QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX))) {
|
||||
// virtual file, open it after the Folder were created (if the app is not terminated)
|
||||
QTimer::singleShot(0, this, [this, option] { openVirtualFile(option); });
|
||||
} else {
|
||||
} else if (option.startsWith(QStringLiteral(APPLICATION_URI_HANDLER_SCHEME "://open"))) {
|
||||
// see the section Local file editing of the Architecture page of the user documenation
|
||||
_editFileLocallyUrl = QUrl::fromUserInput(option);
|
||||
if (!_editFileLocallyUrl.isValid()) {
|
||||
_editFileLocallyUrl.clear();
|
||||
const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(option);
|
||||
qCInfo(lcApplication) << errorParsingLocalFileEditingUrl;
|
||||
showHint(errorParsingLocalFileEditingUrl.toStdString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
showHint("Unrecognized option '" + option.toStdString() + "'");
|
||||
}
|
||||
}
|
||||
|
@ -728,6 +742,32 @@ void Application::setHelp()
|
|||
_helpOnly = true;
|
||||
}
|
||||
|
||||
void Application::handleEditLocallyFromOptions()
|
||||
{
|
||||
if (!_editFileLocallyUrl.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleEditLocally(_editFileLocallyUrl);
|
||||
_editFileLocallyUrl.clear();
|
||||
}
|
||||
|
||||
void Application::handleEditLocally(const QUrl &url) const
|
||||
{
|
||||
auto pathSplit = url.path().split('/', Qt::SkipEmptyParts);
|
||||
|
||||
if (pathSplit.size() < 2) {
|
||||
qCWarning(lcApplication) << "Invalid URL for file local editing: " + pathSplit.join('/');
|
||||
return;
|
||||
}
|
||||
|
||||
// for a sample URL "nc://open/admin@nextcloud.lan:8080/Photos/lovely.jpg", QUrl::path would return "admin@nextcloud.lan:8080/Photos/lovely.jpg"
|
||||
const auto accountDisplayName = pathSplit.takeFirst();
|
||||
const auto fileRemotePath = pathSplit.join('/');
|
||||
|
||||
FolderMan::instance()->editFileLocally(accountDisplayName, fileRemotePath);
|
||||
}
|
||||
|
||||
QString substLang(const QString &lang)
|
||||
{
|
||||
// Map the more appropriate script codes
|
||||
|
@ -855,15 +895,26 @@ void Application::tryTrayAgain()
|
|||
|
||||
bool Application::event(QEvent *event)
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
|
||||
qCDebug(lcApplication) << "QFileOpenEvent" << openEvent->file();
|
||||
// virtual file, open it after the Folder were created (if the app is not terminated)
|
||||
QString fn = openEvent->file();
|
||||
QTimer::singleShot(0, this, [this, fn] { openVirtualFile(fn); });
|
||||
const auto openEvent = static_cast<QFileOpenEvent *>(event);
|
||||
qCDebug(lcApplication) << "macOS: Received a QFileOpenEvent";
|
||||
|
||||
if(!openEvent->file().isEmpty()) {
|
||||
qCDebug(lcApplication) << "QFileOpenEvent" << openEvent->file();
|
||||
// virtual file, open it after the Folder were created (if the app is not terminated)
|
||||
const auto fn = openEvent->file();
|
||||
QTimer::singleShot(0, this, [this, fn] { openVirtualFile(fn); });
|
||||
} else if (!openEvent->url().isEmpty() && openEvent->url().isValid()) {
|
||||
// On macOS, Qt does not handle receiving a custom URI as it does on other systems (as an application argument).
|
||||
// Instead, it sends out a QFileOpenEvent. We therefore need custom handling for our URI handling on macOS.
|
||||
qCInfo(lcApplication) << "macOS: Opening local file for editing: " << openEvent->url();
|
||||
handleEditLocally(openEvent->url());
|
||||
} else {
|
||||
const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(openEvent->url().toString());
|
||||
qCInfo(lcApplication) << errorParsingLocalFileEditingUrl;
|
||||
showHint(errorParsingLocalFileEditingUrl.toStdString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return SharedTools::QtSingleApplication::event(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@ public:
|
|||
|
||||
ownCloudGui *gui() const;
|
||||
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
public slots:
|
||||
// TODO: this should not be public
|
||||
void slotownCloudWizardDone(int);
|
||||
|
@ -85,11 +87,12 @@ public slots:
|
|||
/// Attempt to show() the tray icon again. Used if no systray was available initially.
|
||||
void tryTrayAgain();
|
||||
|
||||
void handleEditLocally(const QUrl &url) const;
|
||||
|
||||
protected:
|
||||
void parseOptions(const QStringList &);
|
||||
void setupTranslations();
|
||||
void setupLogging();
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
signals:
|
||||
void folderRemoved();
|
||||
|
@ -109,6 +112,8 @@ protected slots:
|
|||
private:
|
||||
void setHelp();
|
||||
|
||||
void handleEditLocallyFromOptions();
|
||||
|
||||
/**
|
||||
* Maybe a newer version of the client was used with this config file:
|
||||
* if so, backup, confirm with user and remove the config that can't be read.
|
||||
|
@ -135,6 +140,7 @@ private:
|
|||
bool _userTriggeredConnect;
|
||||
bool _debugMode;
|
||||
bool _backgroundMode;
|
||||
QUrl _editFileLocallyUrl;
|
||||
|
||||
ClientProxy _proxy;
|
||||
|
||||
|
|
|
@ -17,23 +17,72 @@
|
|||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <AppKit/NSApplication.h>
|
||||
|
||||
#include "application.h"
|
||||
|
||||
/* In theory, we should be able to just capture QFileOpenEvents
|
||||
* when we open our custom URLs in our Application class and be
|
||||
* done with it, but in practice the QFileOpenEvent often doesn't
|
||||
* get sent for our URLs. We have this in place to work around
|
||||
* the issue.
|
||||
*
|
||||
* This class sets a callback selector on URL-related events
|
||||
* before the application is fully done launching. This lets us
|
||||
* properly receive and process "open url" events even if the
|
||||
* client was closed when these events were sent. */
|
||||
|
||||
@interface URLEventHandler : NSObject
|
||||
@end
|
||||
|
||||
@implementation URLEventHandler
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(applicationWillFinishLaunching:)
|
||||
name:NSApplicationWillFinishLaunchingNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
|
||||
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self
|
||||
andSelector:@selector(handleURLEvent:withReplyEvent:)
|
||||
forEventClass:kInternetEventClass
|
||||
andEventID:kAEGetURL];
|
||||
}
|
||||
|
||||
- (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
|
||||
const auto app = qobject_cast<OCC::Application *>(QApplication::instance());
|
||||
const auto qtUrl = QUrl::fromNSURL(url);
|
||||
app->handleEditLocally(qtUrl);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace OCC {
|
||||
namespace Mac {
|
||||
|
||||
class CocoaInitializer::Private {
|
||||
public:
|
||||
public:
|
||||
NSAutoreleasePool* autoReleasePool;
|
||||
URLEventHandler* handler;
|
||||
};
|
||||
|
||||
CocoaInitializer::CocoaInitializer() {
|
||||
d = new CocoaInitializer::Private();
|
||||
NSApplicationLoad();
|
||||
d->autoReleasePool = [[NSAutoreleasePool alloc] init];
|
||||
d = new CocoaInitializer::Private();
|
||||
d->handler = [[URLEventHandler alloc] init];
|
||||
NSApplicationLoad();
|
||||
d->autoReleasePool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
|
||||
CocoaInitializer::~CocoaInitializer() {
|
||||
[d->autoReleasePool release];
|
||||
delete d;
|
||||
[d->autoReleasePool release];
|
||||
delete d;
|
||||
}
|
||||
|
||||
} // namespace Mac
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include <QMutableSetIterator>
|
||||
#include <QSet>
|
||||
#include <QNetworkProxy>
|
||||
#include <QDesktopServices>
|
||||
#include <QtConcurrent>
|
||||
|
||||
static const char versionC[] = "version";
|
||||
static const int maxFoldersVersion = 1;
|
||||
|
@ -163,6 +165,8 @@ void FolderMan::registerFolderWithSocketApi(Folder *folder)
|
|||
|
||||
int FolderMan::setupFolders()
|
||||
{
|
||||
Utility::registerUriHandlerForLocalEditing();
|
||||
|
||||
unloadAndDeleteAllFolders();
|
||||
|
||||
QStringList skipSettingsKeys;
|
||||
|
@ -1402,6 +1406,64 @@ void FolderMan::setDirtyNetworkLimits()
|
|||
}
|
||||
}
|
||||
|
||||
void FolderMan::editFileLocally(const QString &accountDisplayName, const QString &relPath)
|
||||
{
|
||||
const auto showError = [this](const OCC::AccountStatePtr accountState, const QString &errorMessage, const QString &subject) {
|
||||
if (accountState && accountState->account()) {
|
||||
const auto foundFolder = std::find_if(std::cbegin(map()), std::cend(map()), [accountState](const auto &folder) {
|
||||
return accountState->account()->davUrl() == folder->remoteUrl();
|
||||
});
|
||||
|
||||
if (foundFolder != std::cend(map())) {
|
||||
(*foundFolder)->syncEngine().addErrorToGui(SyncFileItem::SoftError, errorMessage, subject);
|
||||
}
|
||||
}
|
||||
|
||||
// to make sure the error is not missed, show a message box in addition
|
||||
const auto messageBox = new QMessageBox;
|
||||
messageBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
messageBox->setText(errorMessage);
|
||||
messageBox->setInformativeText(subject);
|
||||
messageBox->setIcon(QMessageBox::Warning);
|
||||
messageBox->addButton(QMessageBox::StandardButton::Ok);
|
||||
messageBox->show();
|
||||
messageBox->activateWindow();
|
||||
messageBox->raise();
|
||||
};
|
||||
|
||||
const auto accountFound = AccountManager::instance()->account(accountDisplayName);
|
||||
|
||||
if (!accountFound) {
|
||||
qCWarning(lcFolderMan) << "Could not find an account " << accountDisplayName << " to edit file " << relPath << " locally.";
|
||||
showError(accountFound, tr("Could not find an account for local editing"), accountDisplayName);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto foundFiles = findFileInLocalFolders(relPath, accountFound->account());
|
||||
|
||||
if (foundFiles.isEmpty()) {
|
||||
for (const auto &folder : map()) {
|
||||
bool result = false;
|
||||
const auto excludedThroughSelectiveSync = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &result);
|
||||
for (const auto &excludedPath : excludedThroughSelectiveSync) {
|
||||
if (relPath.startsWith(excludedPath)) {
|
||||
showError(accountFound, tr("Could not find a file for local editing. Make sure it is not excluded via selective sync."), relPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showError(accountFound, tr("Could not find a file for local editing. Make sure its path is valid and it is synced locally."), relPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// In case the VFS mode is enabled and a file is not yet hydrated, we must call QDesktopServices::openUrl from a separate thread, or, there will be a freeze.
|
||||
// To avoid searching for a specific folder and checking if the VFS is enabled - we just always call it from a separate thread.
|
||||
QtConcurrent::run([foundFiles] {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(foundFiles.first()));
|
||||
});
|
||||
}
|
||||
|
||||
void FolderMan::trayOverallStatus(const QList<Folder *> &folders,
|
||||
SyncResult::Status *status, bool *unresolvedConflicts)
|
||||
{
|
||||
|
|
|
@ -202,6 +202,9 @@ public:
|
|||
void setDirtyProxy();
|
||||
void setDirtyNetworkLimits();
|
||||
|
||||
/** opens a file with default app, if the file is present **/
|
||||
void editFileLocally(const QString &accountDisplayName, const QString &relPath);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* signal to indicate a folder has changed its sync state.
|
||||
|
|
Loading…
Reference in a new issue