Skip to content

Commit

Permalink
Implement migration to spaces
Browse files Browse the repository at this point in the history
Fixes: #9945
  • Loading branch information
TheOneRing authored and Fabian Müller committed Sep 8, 2022
1 parent 82d999b commit ef05a4f
Show file tree
Hide file tree
Showing 15 changed files with 490 additions and 2 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/9945
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Change: Implement server guided migration to spaces

We added a feature that allows limited automatic migration of existing syncs to OCIS spaces.

https://github.com/owncloud/client/issues/9945
https://github.com/owncloud/ocis/issues/3574
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ set(client_SRCS
models/expandingheaderview.cpp
models/models.cpp
models/protocolitemmodel.cpp

spacemigration.cpp
)

set(3rdparty_SRC
Expand Down
14 changes: 14 additions & 0 deletions src/gui/accountstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "creds/abstractcredentials.h"
#include "creds/httpcredentials.h"
#include "gui/settingsdialog.h"
#include "gui/spacemigration.h"
#include "gui/tlserrordialog.h"
#include "logger.h"
#include "settingsdialog.h"
Expand Down Expand Up @@ -410,6 +411,19 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
}
_connectionErrors = errors;

if (status == ConnectionValidator::Connected) {
Q_ASSERT(_account->hasCapabilities());
if (_account->capabilities().migration().space_migration.enabled) {
auto stateptr = AccountManager::instance()->account(_account->uuid());
auto migration = new SpaceMigration(stateptr, _account->capabilities().migration().space_migration.endpoint, this);
connect(migration, &SpaceMigration::finised, this, [migration, this] {
migration->deleteLater();
setState(Connected);
});
migration->start();
return;
}
}
switch (status) {
case ConnectionValidator::Connected:
setState(Connected);
Expand Down
1 change: 1 addition & 0 deletions src/gui/accountstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ protected Q_SLOTS:
* Milliseconds for which to delay reconnection after 503/maintenance.
*/
std::chrono::milliseconds _maintenanceToConnectedDelay;
friend class SpaceMigration;
};
}

Expand Down
3 changes: 3 additions & 0 deletions src/gui/folder.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class FolderDefinition
bool _deployed = false;

friend class FolderMan;
friend class SpaceMigration;
};

/**
Expand Down Expand Up @@ -573,6 +574,8 @@ private slots:
* The vfs mode instance (created by plugin) to use. Never null.
*/
QSharedPointer<Vfs> _vfs;

friend class SpaceMigration;
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/gui/owncloudgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void setUpInitialSyncFolder(AccountStatePtr accountStatePtr, bool useVfs)
for (const auto &d : drives) {
const QString name = GraphApi::Drives::getDriveDisplayName(d);
const QString folderName = FolderMan::instance()->findGoodPathForNewSyncFolder(localDir.filePath(name));
addFolder(folderName, {}, QUrl::fromEncoded(d.getRoot().getWebDavUrl().toUtf8()), name);
addFolder(folderName, {}, QUrl(d.getRoot().getWebDavUrl()), name);
}
finalize();
}
Expand Down
108 changes: 108 additions & 0 deletions src/gui/spacemigration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (C) by Hannah von Reth <hannah.vonreth@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spacemigration.h"

#include "common/version.h"
#include "gui/accountmanager.h"
#include "gui/folderman.h"
#include "libsync/account.h"
#include "libsync/graphapi/drives.h"

#include <QJsonArray>

Q_LOGGING_CATEGORY(lcMigration, "gui.migration.spaces", QtInfoMsg)

using namespace OCC;

SpaceMigration::SpaceMigration(const AccountStatePtr &account, const QString &path, QObject *parent)
: QObject(parent)
, _accountState(account)
, _path(path)
{
}

void SpaceMigration::start()
{
FolderMan::instance()->setSyncEnabled(false);

QJsonArray folders;
for (auto *f : FolderMan::instance()->folders()) {
// same account and uses the legacy dav url
// already migrated folders are ignored
if (f->accountState()->account() == _accountState->account() && f->webDavUrl() == _accountState->account()->davUrl()) {
folders.append(f->remotePath());
_migrationFolders.append(f);
}
}
const QJsonObject obj {
{ QStringLiteral("version"), Version::version().toString() },
{ QStringLiteral("user"), _accountState->account()->davUser() },
{ QStringLiteral("remotefolder"), folders }
};
auto job = new JsonJob(_accountState->account(), _accountState->account()->url(), _path, QByteArrayLiteral("POST"), obj);
job->setParent(this);
connect(job, &JsonJob::finishedSignal, this, [job, this] {
const int status = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status == 200 && job->parseError().error == QJsonParseError::NoError) {
migrate(job->data().value(QStringLiteral("folders")).toObject());
} else {
qCInfo(lcMigration) << status << job->data();
if (job->parseError().error != QJsonParseError::NoError) {
qCInfo(lcMigration) << job->parseError().errorString();
}
Q_EMIT finised();
}
});
job->start();
}

void SpaceMigration::migrate(const QJsonObject &folders)
{
auto drivesJob = new GraphApi::Drives(_accountState->account(), this);
connect(drivesJob, &GraphApi::Drives::finishedSignal, [drivesJob, folders, this] {
const auto drives = drivesJob->drives();
for (auto &folder : _migrationFolders) {
if (folder) {
const auto obj = folders.value(folder->remotePath()).toObject();
const QString error = obj.value(QStringLiteral("error")).toString();
if (error.isEmpty()) {
const QString newPath = obj.value(QStringLiteral("path")).toString();
const QString space_id = obj.value(QStringLiteral("space_id")).toString();
auto it = std::find_if(drives.cbegin(), drives.cend(), [space_id](auto it) {
return it.getId() == space_id;
});
if (it != drives.cend()) {
qCDebug(lcMigration) << "Migraating:" << folder->path()
<< "davUrl:" << folder->_definition._webDavUrl << "->" << it->getRoot().getWebDavUrl()
<< "remotPath:" << folder->_definition._targetPath << "->" << newPath;
folder->_definition._webDavUrl = QUrl(it->getRoot().getWebDavUrl());
folder->_definition._targetPath = newPath;
folder->saveToSettings();
}
} else {
qCInfo(lcMigration) << "No migration of" << folder->remotePath() << "reason:" << error;
}
}
}
_accountState->_supportsSpaces = true;
AccountManager::instance()->save();
FolderMan::instance()->setSyncEnabled(true);
Q_EMIT finised();
});
drivesJob->start();
}
46 changes: 46 additions & 0 deletions src/gui/spacemigration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) by Hannah von Reth <hannah.vonreth@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once

#include "libsync/networkjobs/jsonjob.h"

#include <QObject>

namespace OCC {
class Folder;

class SpaceMigration : public QObject
{
Q_OBJECT
public:
SpaceMigration(const AccountStatePtr &accountState, const QString &path, QObject *parent);
void start();

Q_SIGNALS:
void finised();

private:
void migrate(const QJsonObject &folders);

AccountStatePtr _accountState;
const QString _path;

QVector<QPointer<Folder>> _migrationFolders;
};

}
6 changes: 5 additions & 1 deletion src/gui/spaces/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
add_library(spaces STATIC
spacesmodel.cpp spacesbrowser.cpp spacesbrowser.ui)
spacesmodel.cpp
spacesbrowser.cpp
spacesbrowser.ui
)

target_link_libraries(spaces PUBLIC Qt5::Widgets libsync)
set_target_properties(spaces PROPERTIES AUTOUIC ON AUTORCC ON)
apply_common_target_settings(spaces)
13 changes: 13 additions & 0 deletions src/libsync/capabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Capabilities::Capabilities(const QVariantMap &capabilities)
, _spaces(_capabilities.value(QStringLiteral("spaces")).toMap())
, _status(_capabilities.value(QStringLiteral("core")).toMap().value(QStringLiteral("status")).toMap())
, _appProviders(AppProviders::findVersion(_capabilities.value(QStringLiteral("files")).toMap().value(QStringLiteral("app_providers")).toList(), QVersionNumber({ 1, 1, 0 })))
, _migration(_capabilities.value(QStringLiteral("migration")).toMap())
{
}

Expand Down Expand Up @@ -334,4 +335,16 @@ Capabilities::AppProviders Capabilities::AppProviders::findVersion(const QVarian
: Capabilities::AppProviders();
}

OCC::Migration::Migration(const QVariantMap &data)
{
const auto spaces = data.value(QStringLiteral("space_migration")).toMap();
space_migration.enabled = spaces.value(QStringLiteral("enabled")).toBool();
space_migration.endpoint = spaces.value(QStringLiteral("endpoint")).toString();
}

const Migration &Capabilities::migration() const
{
return _migration;
}

} // namespace OCC
15 changes: 15 additions & 0 deletions src/libsync/capabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,19 @@ struct OWNCLOUDSYNC_EXPORT SpaceSupport
bool isValid() const;
};

struct OWNCLOUDSYNC_EXPORT Migration
{
struct SpacesMigration
{
bool enabled = false;
QString endpoint;
};

explicit Migration(const QVariantMap &spaces_support);

SpacesMigration space_migration;
};

/**
* @brief The Capabilities class represents the capabilities of an ownCloud
* server
Expand Down Expand Up @@ -254,6 +267,7 @@ class OWNCLOUDSYNC_EXPORT Capabilities

const AppProviders &appProviders() const;

const Migration &migration() const;

QVariantMap raw() const;

Expand All @@ -265,6 +279,7 @@ class OWNCLOUDSYNC_EXPORT Capabilities
SpaceSupport _spaces;
Status _status;
AppProviders _appProviders;
Migration _migration;
};
}

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@ configure_file(test_journal.db "${PROJECT_BINARY_DIR}/bin/test_journal.db" COPYO


owncloud_add_test(JobQueue)
owncloud_add_test(SpacesMigration)

add_subdirectory(modeltests)
Loading

0 comments on commit ef05a4f

Please sign in to comment.