Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/gui/connectionvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ void ConnectionValidator::slotCapabilitiesRecieved(const QJsonDocument &json)
return;
}

// Check for the directEditing capability
QUrl directEditingURL = QUrl(caps["files"].toObject()["directEditing"].toObject()["url"].toString());
QString directEditingETag = caps["files"].toObject()["directEditing"].toObject()["etag"].toString();
_account->fetchDirectEditors(directEditingURL, directEditingETag);

fetchUser();
}

Expand Down
69 changes: 66 additions & 3 deletions src/gui/socketapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,42 @@ void SocketApi::command_VERSION(const QString &, SocketListener *listener)

void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketListener *listener)
{
listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is Nextcloud").arg(Theme::instance()->appNameGUI()));
//listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is Nextcloud").arg(Theme::instance()->appNameGUI()));
listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + Theme::instance()->appNameGUI());
}

void SocketApi::command_EDIT(const QString &localFile, SocketListener *listener)
{
auto fileData = FileData::get(localFile);
if (!fileData.folder) {
qCWarning(lcSocketApi) << "Unknown path" << localFile;
return;
}

auto record = fileData.journalRecord();
if (!record.isValid())
return;

DirectEditor* editor = getDirectEditorForLocalFile(fileData.localPath);
if (!editor)
return;

JsonApiJob *job = new JsonApiJob(fileData.folder->accountState()->account(), QLatin1String("ocs/v2.php/apps/files/api/v1/directEditing/open"), this);

QUrlQuery params;
params.addQueryItem("path", fileData.accountRelativePath);
params.addQueryItem("editorId", editor->id());
job->addQueryParams(params);
job->usePOST();

QObject::connect(job, &JsonApiJob::jsonReceived, [](const QJsonDocument &json){
auto data = json.object().value("ocs").toObject().value("data").toObject();
auto url = QUrl(data.value("url").toString());

if(!url.isEmpty())
Utility::openBrowser(url, nullptr);
});
job->start();
}

// don't pull the share manager into socketapi unittests
Expand Down Expand Up @@ -644,7 +679,7 @@ void SocketApi::command_GET_STRINGS(const QString &argument, SocketListener *lis
{
static std::array<std::pair<const char *, QString>, 5> strings { {
{ "SHARE_MENU_TITLE", tr("Share options") },
{ "CONTEXT_MENU_TITLE", tr("Share via %1").arg(Theme::instance()->appNameGUI())},
{ "CONTEXT_MENU_TITLE", Theme::instance()->appNameGUI() },
{ "COPY_PRIVATE_LINK_MENU_TITLE", tr("Copy private link to clipboard") },
{ "EMAIL_PRIVATE_LINK_MENU_TITLE", tr("Send private link by email …") },
} };
Expand Down Expand Up @@ -738,13 +773,41 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
FileData fileData = hasSeveralFiles ? FileData{} : FileData::get(argument);
bool isOnTheServer = fileData.journalRecord().isValid();
auto flagString = isOnTheServer ? QLatin1String("::") : QLatin1String(":d:");
auto capabilities = fileData.folder->accountState()->account()->capabilities();

if (fileData.folder && fileData.folder->accountState()->isConnected()) {
DirectEditor* editor = getDirectEditorForLocalFile(fileData.localPath);
if (editor) {
//listener->sendMessage(QLatin1String("MENU_ITEM:EDIT") + flagString + tr("Edit via ") + editor->name());
listener->sendMessage(QLatin1String("MENU_ITEM:EDIT") + flagString + tr("Edit"));
} else {
listener->sendMessage(QLatin1String("MENU_ITEM:OPEN_PRIVATE_LINK") + flagString + tr("Open in browser"));
}

sendSharingContextMenuOptions(fileData, listener);
listener->sendMessage(QLatin1String("MENU_ITEM:OPEN_PRIVATE_LINK") + flagString + tr("Open in browser"));
}
listener->sendMessage(QString("GET_MENU_ITEMS:END"));
}

DirectEditor* SocketApi::getDirectEditorForLocalFile(const QString &localFile)
{
FileData fileData = FileData::get(localFile);
auto capabilities = fileData.folder->accountState()->account()->capabilities();

if (fileData.folder && fileData.folder->accountState()->isConnected()) {
QMimeDatabase db;
QMimeType type = db.mimeTypeForFile(localFile);

DirectEditor* editor = capabilities.getDirectEditorForMimetype(type);
if (!editor) {
editor = capabilities.getDirectEditorForOptionalMimetype(type);
}
return editor;
}

return nullptr;
}

QString SocketApi::buildRegisterPathMessage(const QString &path)
{
QFileInfo fi(path);
Expand Down
5 changes: 5 additions & 0 deletions src/gui/socketapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace OCC {
class SyncFileStatus;
class Folder;
class SocketListener;
class DirectEditor;

/**
* @brief The SocketApi class
Expand Down Expand Up @@ -123,6 +124,10 @@ private slots:
*/
Q_INVOKABLE void command_GET_MENU_ITEMS(const QString &argument, SocketListener *listener);

/// Direct Editing
Q_INVOKABLE void command_EDIT(const QString &localFile, SocketListener *listener);
DirectEditor* getDirectEditorForLocalFile(const QString &localFile);

QString buildRegisterPathMessage(const QString &path);

QSet<QString> _registeredAliases;
Expand Down
48 changes: 48 additions & 0 deletions src/libsync/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include <QSslKey>
#include <QAuthenticator>
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

#include <keychain.h>
#include "creds/abstractcredentials.h"
Expand Down Expand Up @@ -600,4 +603,49 @@ void Account::deleteAppPassword(){
job->start();
}

void Account::fetchDirectEditors(const QUrl &directEditingURL, const QString &directEditingETag)
{
if(directEditingURL.isEmpty() || directEditingETag.isEmpty())
return;

// Check for the directEditing capability
if (!directEditingURL.isEmpty() &&
(directEditingETag.isEmpty() || directEditingETag != _lastDirectEditingETag)) {
// Fetch the available editors and their mime types
JsonApiJob *job = new JsonApiJob(sharedFromThis(), QLatin1String("ocs/v2.php/apps/files/api/v1/directEditing"), this);
QObject::connect(job, &JsonApiJob::jsonReceived, this, &Account::slotDirectEditingRecieved);
job->start();
}
}

void Account::slotDirectEditingRecieved(const QJsonDocument &json)
{
auto data = json.object().value("ocs").toObject().value("data").toObject();
auto editors = data.value("editors").toObject();

foreach (auto editorKey, editors.keys()) {
auto editor = editors.value(editorKey).toObject();

const QString id = editor.value("id").toString();
const QString name = editor.value("name").toString();

if(!id.isEmpty() && !name.isEmpty()) {
auto mimeTypes = editor.value("mimetypes").toArray();
auto optionalMimeTypes = editor.value("optionalMimetypes").toArray();

DirectEditor *directEditor = new DirectEditor(id, name);

foreach(auto mimeType, mimeTypes) {
directEditor->addMimetype(mimeType.toString().toLatin1());
}

foreach(auto optionalMimeType, optionalMimeTypes) {
directEditor->addOptionalMimetype(optionalMimeType.toString().toLatin1());
}

_capabilities.addDirectEditor(directEditor);
}
}
}

} // namespace OCC
8 changes: 8 additions & 0 deletions src/libsync/account.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject
void writeAppPasswordOnce(QString appPassword);
void deleteAppPassword();

/// Direct Editing
// Check for the directEditing capability
void fetchDirectEditors(const QUrl &directEditingURL, const QString &directEditingETag);

public slots:
/// Used when forgetting credentials
void clearQNAMCache();
Expand Down Expand Up @@ -278,6 +282,7 @@ public slots:
protected Q_SLOTS:
void slotCredentialsFetched();
void slotCredentialsAsked();
void slotDirectEditingRecieved(const QJsonDocument &json);

private:
Account(QObject *parent = nullptr);
Expand Down Expand Up @@ -324,6 +329,9 @@ protected Q_SLOTS:

friend class AccountManager;

// Direct Editing
QString _lastDirectEditingETag;

/* IMPORTANT - remove later - FIXME MS@2019-12-07 -->
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
*
Expand Down
81 changes: 81 additions & 0 deletions src/libsync/capabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,85 @@ bool Capabilities::uploadConflictFiles() const

return _capabilities["uploadConflictFiles"].toBool();
}

/*-------------------------------------------------------------------------------------*/

// Direct Editing
void Capabilities::addDirectEditor(DirectEditor* directEditor)
{
if(directEditor)
_directEditors.append(directEditor);
}

DirectEditor* Capabilities::getDirectEditorForMimetype(const QMimeType &mimeType)
{
foreach(DirectEditor* editor, _directEditors) {
if(editor->hasMimetype(mimeType))
return editor;
}

return nullptr;
}

DirectEditor* Capabilities::getDirectEditorForOptionalMimetype(const QMimeType &mimeType)
{
foreach(DirectEditor* editor, _directEditors) {
if(editor->hasOptionalMimetype(mimeType))
return editor;
}

return nullptr;
}

/*-------------------------------------------------------------------------------------*/

DirectEditor::DirectEditor(const QString &id, const QString &name, QObject* parent)
: QObject(parent)
, _id(id)
, _name(name)
{
}

QString DirectEditor::id() const
{
return _id;
}

QString DirectEditor::name() const
{
return _name;
}

void DirectEditor::addMimetype(const QByteArray &mimeType)
{
_mimeTypes.append(mimeType);
}

void DirectEditor::addOptionalMimetype(const QByteArray &mimeType)
{
_optionalMimeTypes.append(mimeType);
}

QList<QByteArray> DirectEditor::mimeTypes() const
{
return _mimeTypes;
}

QList<QByteArray> DirectEditor::optionalMimeTypes() const
{
return _optionalMimeTypes;
}

bool DirectEditor::hasMimetype(const QMimeType &mimeType)
{
return _mimeTypes.contains(mimeType.name().toLatin1());
}

bool DirectEditor::hasOptionalMimetype(const QMimeType &mimeType)
{
return _optionalMimeTypes.contains(mimeType.name().toLatin1());
}

/*-------------------------------------------------------------------------------------*/

}
41 changes: 41 additions & 0 deletions src/libsync/capabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@

#include <QVariantMap>
#include <QStringList>
#include <QMimeDatabase>

namespace OCC {

class DirectEditor;

/**
* @brief The Capabilities class represents the capabilities of an ownCloud
* server
Expand Down Expand Up @@ -127,9 +130,47 @@ class OWNCLOUDSYNC_EXPORT Capabilities
*/
bool uploadConflictFiles() const;

// Direct Editing
void addDirectEditor(DirectEditor* directEditor);
DirectEditor* getDirectEditorForMimetype(const QMimeType &mimeType);
DirectEditor* getDirectEditorForOptionalMimetype(const QMimeType &mimeType);

private:
QVariantMap _capabilities;

QList<DirectEditor*> _directEditors;
};

/*-------------------------------------------------------------------------------------*/

class OWNCLOUDSYNC_EXPORT DirectEditor : public QObject
{
Q_OBJECT
public:
DirectEditor(const QString &id, const QString &name, QObject* parent = 0);

void addMimetype(const QByteArray &mimeType);
void addOptionalMimetype(const QByteArray &mimeType);

bool hasMimetype(const QMimeType &mimeType);
bool hasOptionalMimetype(const QMimeType &mimeType);

QString id() const;
QString name() const;

QList<QByteArray> mimeTypes() const;
QList<QByteArray> optionalMimeTypes() const;

private:
QString _id;
QString _name;

QList<QByteArray> _mimeTypes;
QList<QByteArray> _optionalMimeTypes;
};

/*-------------------------------------------------------------------------------------*/

}

#endif //CAPABILITIES_H
2 changes: 1 addition & 1 deletion src/libsync/networkjobs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ void JsonApiJob::start()
auto query = _additionalParams;
query.addQueryItem(QLatin1String("format"), QLatin1String("json"));
QUrl url = Utility::concatUrlPath(account()->url(), path(), query);
sendRequest("GET", url, _request);
sendRequest(_usePOST ? "POST" : "GET", url, _request);
AbstractNetworkJob::start();
}

Expand Down
12 changes: 12 additions & 0 deletions src/libsync/networkjobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,16 @@ class OWNCLOUDSYNC_EXPORT JsonApiJob : public AbstractNetworkJob
void addQueryParams(const QUrlQuery &params);
void addRawHeader(const QByteArray &headerName, const QByteArray &value);

/**
* @brief usePOST - allow job to do an anonymous POST request instead of GET
* @param params: (optional) true for POST, false for GET (default).
*
* This function needs to be called before start() obviously.
*/
void usePOST(bool usePOST = true) {
_usePOST = usePOST;
}

public slots:
void start() override;

Expand All @@ -398,6 +408,8 @@ public slots:
private:
QUrlQuery _additionalParams;
QNetworkRequest _request;

bool _usePOST = false;
};

/**
Expand Down