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 committed Mar 22, 2023
1 parent 803d260 commit 0e49966
Show file tree
Hide file tree
Showing 16 changed files with 505 additions and 3 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 @@ -83,6 +83,8 @@ set(client_SRCS
models/expandingheaderview.cpp
models/models.cpp
models/protocolitemmodel.cpp

spacemigration.cpp
)

set(3rdparty_SRC
Expand Down
16 changes: 16 additions & 0 deletions src/gui/accountstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "gui/quotainfo.h"
#include "gui/settingsdialog.h"
#include "gui/spacemigration.h"
#include "gui/tlserrordialog.h"

#include "logger.h"
Expand Down Expand Up @@ -419,6 +420,21 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
}
_connectionErrors = errors;

if (Q_UNLIKELY(Theme::instance()->enableCernBranding())) {
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::finished, this, [migration, this] {
migration->deleteLater();
setState(Connected);
});
migration->start();
return;
}
}
}
switch (status) {
case ConnectionValidator::Connected:
setState(Connected);
Expand Down
2 changes: 2 additions & 0 deletions src/gui/accountstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ protected Q_SLOTS:
std::chrono::milliseconds _maintenanceToConnectedDelay;

QuotaInfo *_quotaInfo = nullptr;

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 @@ -146,6 +146,7 @@ class FolderDefinition
uint32_t _priority = 0;

friend class FolderMan;
friend class SpaceMigration;
};

/**
Expand Down Expand Up @@ -592,6 +593,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 @@ -86,7 +86,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));
auto folder = addFolder(folderName, {}, QUrl::fromEncoded(d.getRoot().getWebDavUrl().toUtf8()), name);
auto folder = addFolder(folderName, {}, QUrl(d.getRoot().getWebDavUrl()), name);
folder->setPriority(GraphApi::Drives::getDrivePriority(d));
// save the new priority
folder->saveToSettings();
Expand Down
103 changes: 103 additions & 0 deletions src/gui/spacemigration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* 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 {
if (job->parseError().error != QJsonParseError::NoError) {
qCInfo(lcMigration) << job->parseError().errorString();
}
Q_EMIT finished();
}
});
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();

const auto it = std::find_if(drives.cbegin(), drives.cend(), [space_id](auto it) { return it.getId() == space_id; });

if (it != drives.cend()) {
qCDebug(lcMigration) << "Migrating:" << 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 finished();
});
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 finished();

private:
void migrate(const QJsonObject &folders);

AccountStatePtr _accountState;
const QString _path;

QVector<QPointer<Folder>> _migrationFolders;
};

}
18 changes: 16 additions & 2 deletions src/libsync/capabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ Capabilities::Capabilities(const QVariantMap &capabilities)
, _tusSupport(_capabilities.value(QStringLiteral("files")).toMap().value(QStringLiteral("tus_support")).toMap())
, _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 })))
, _appProviders(AppProviders::findVersion(
_capabilities.value(QStringLiteral("files")).toMap().value(QStringLiteral("app_providers")).toList(), QVersionNumber({1, 1, 0})))
, _filesSharing(_fileSharingCapabilities)
, _migration(_capabilities.value(QStringLiteral("migration")).toMap())
{
}

Expand Down Expand Up @@ -335,7 +337,6 @@ Capabilities::AppProviders Capabilities::AppProviders::findVersion(const QVarian
: Capabilities::AppProviders();
}


const FilesSharing &Capabilities::filesSharing() const
{
return _filesSharing;
Expand All @@ -345,4 +346,17 @@ FilesSharing::FilesSharing(const QVariantMap &filesSharing)
: sharing_roles(filesSharing.value(QStringLiteral("sharing_roles"), false).toBool())
{
}

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 @@ -110,6 +110,19 @@ struct OWNCLOUDSYNC_EXPORT FilesSharing
bool sharing_roles = false;
};

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 @@ -275,6 +288,7 @@ class OWNCLOUDSYNC_EXPORT Capabilities

const FilesSharing &filesSharing() const;

const Migration &migration() const;

QVariantMap raw() const;

Expand All @@ -287,6 +301,7 @@ class OWNCLOUDSYNC_EXPORT Capabilities
Status _status;
AppProviders _appProviders;
FilesSharing _filesSharing;
Migration _migration;
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/libsync/theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,11 @@ QVector<QPair<QString, QUrl>> Theme::urlButtons() const
return {};
}

bool Theme::enableCernBranding() const
{
return false;
}

template <>
OWNCLOUDSYNC_EXPORT QString Utility::enumToDisplayName(Theme::UserIDType userIdType)
{
Expand Down
9 changes: 9 additions & 0 deletions src/libsync/theme.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,15 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject
* */
virtual QVector<QPair<QString, QUrl>> urlButtons() const;


/**
* Whether to enable the special code for cernbox
* This includes:
* - spaces migration.
*/
bool enableCernBranding() const;


protected:
QIcon themeTrayIcon(const QString &name, bool sysTrayMenuVisible = false, IconType iconType = IconType::BrandedIconWithFallbackToVanillaIcon) const;
QIcon themeIcon(const QString &name, IconType iconType = IconType::BrandedIconWithFallbackToVanillaIcon) const;
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,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 0e49966

Please sign in to comment.