diff --git a/client.qrc b/client.qrc
index e71b14b30..f726c633a 100644
--- a/client.qrc
+++ b/client.qrc
@@ -163,5 +163,6 @@
resources/icons/actions/light-down-arrow.svg
resources/pictures/en-endpoint-security.svg
resources/pictures/fr-endpoint-security.svg
+ resources/icons/actions/messages-bubble-square-typing.svg
diff --git a/config.h.in b/config.h.in
index 974203a23..aab95a2d3 100644
--- a/config.h.in
+++ b/config.h.in
@@ -15,6 +15,11 @@
#cmakedefine APPLICATION_STORAGE_URL "@APPLICATION_STORAGE_URL@"
#cmakedefine APPLICATION_HELP_URL "@APPLICATION_HELP_URL@"
#cmakedefine APPLICATION_CONFLICT_HELP_URL "@APPLICATION_CONFLICT_HELP_URL@"
+#cmakedefine FEEDBACK_EN_URL "@FEEDBACK_EN_URL@"
+#cmakedefine FEEDBACK_FR_URL "@FEEDBACK_FR_URL@"
+#cmakedefine FEEDBACK_DE_URL "@FEEDBACK_DE_URL@"
+#cmakedefine FEEDBACK_ES_URL "@FEEDBACK_ES_URL@"
+#cmakedefine FEEDBACK_IT_URL "@FEEDBACK_IT_URL@"
#cmakedefine APPLICATION_ICON_NAME "@APPLICATION_ICON_NAME@"
#cmakedefine APPLICATION_VIRTUALFILE_SUFFIX "@APPLICATION_VIRTUALFILE_SUFFIX@"
#define APPLICATION_DOTVIRTUALFILE_SUFFIX "." APPLICATION_VIRTUALFILE_SUFFIX
diff --git a/infomaniak/kDrive.cmake b/infomaniak/kDrive.cmake
index 522c9cb36..191274f9a 100644
--- a/infomaniak/kDrive.cmake
+++ b/infomaniak/kDrive.cmake
@@ -16,6 +16,11 @@ endif()
set( APPLICATION_HELP_URL "https://support.infomaniak.com/" CACHE STRING "URL for the help menu" )
set( APPLICATION_CONFLICT_HELP_URL "https://www.infomaniak.com/en/support/faq/2403/resolve-a-kdrive-sync-conflict" )
+set( FEEDBACK_FR_URL "https://feedback.userreport.com/fe6ca4b6-5812-4f39-8ca6-5f2300aecda6" )
+set( FEEDBACK_EN_URL "https://feedback.userreport.com/652ad8f0-84c8-4a21-9e31-7a8bd7134f46" )
+set( FEEDBACK_DE_URL "https://feedback.userreport.com/074f5c5a-372b-40a6-b82f-9157fdb3d2d7" )
+set( FEEDBACK_ES_URL "https://feedback.userreport.com/e1304b1e-ebd0-4ffe-9234-a1a91730e651" )
+set( FEEDBACK_IT_URL "https://feedback.userreport.com/191a0beb-797d-4ec1-b1ff-31889a0012ee" )
if( APPLE )
set( APPLICATION_ICON_NAME "kdrive-mac" )
diff --git a/resources/icons/actions/messages-bubble-square-typing.svg b/resources/icons/actions/messages-bubble-square-typing.svg
new file mode 100644
index 000000000..648e21dd5
--- /dev/null
+++ b/resources/icons/actions/messages-bubble-square-typing.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index dd03ddc08..a0b8074f3 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -80,6 +80,7 @@ set(client_SRCS
customtreewidgetitem.h customtreewidgetitem.cpp
customsystembar.h customsystembar.cpp
customwordwraplabel.h customwordwraplabel.cpp
+ taglabel.h taglabel.cpp
debuggingdialog.h debuggingdialog.cpp
disabledoverlay.h disabledoverlay.cpp
drivepreferenceswidget.h drivepreferenceswidget.cpp
@@ -116,6 +117,7 @@ set(client_SRCS
preferencesmenubarwidget.h preferencesmenubarwidget.cpp
preferenceswidget.h preferenceswidget.cpp
versionwidget.h versionwidget.cpp
+ betaprogramdialog.h betaprogramdialog.cpp
progressbarwidget.h progressbarwidget.cpp
proxyserverdialog.h proxyserverdialog.cpp
guirequests.h guirequests.cpp
diff --git a/src/gui/betaprogramdialog.cpp b/src/gui/betaprogramdialog.cpp
new file mode 100644
index 000000000..dd62e1e2e
--- /dev/null
+++ b/src/gui/betaprogramdialog.cpp
@@ -0,0 +1,233 @@
+/*
+ * Infomaniak kDrive - Desktop
+ * Copyright (C) 2023-2024 Infomaniak Network SA
+ *
+ * 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 3 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "betaprogramdialog.h"
+
+#include "adddriveconfirmationwidget.h"
+#include "customcombobox.h"
+#include "guirequests.h"
+#include "parameterscache.h"
+#include "utility/utility.h"
+
+#include
+#include
+
+static constexpr int mainLayoutHMargin = 40;
+static constexpr int mainLayoutSpacing = 24;
+static constexpr int titleBoxVSpacing = 14;
+static constexpr int subLayoutSpacing = 8;
+static constexpr int iconSize = 16;
+static constexpr auto iconColor = QColor(239, 139, 52);
+static constexpr int indexNo = 0;
+static constexpr int indexBeta = 1;
+static constexpr int indexInternal = 2;
+
+namespace KDC {
+
+BetaProgramDialog::BetaProgramDialog(const bool isQuit, const bool isStaff, QWidget *parent /*= nullptr*/) :
+ CustomDialog(true, parent), _isQuit(isQuit && !isStaff), _isStaff(isStaff) {
+ setObjectName("BetaProgramDialog");
+
+ /*
+ * |-------------------------------------------------------|
+ * | layout |
+ * | |
+ * | |---------------------------------------------| |
+ * | | acknowledmentLayout | |
+ * | | | |
+ * | |---------------------------------------------| |
+ * | |
+ * | |---------------------------------------------| |
+ * | | buttonLayout | |
+ * | |---------------------------------------------| |
+ * |-------------------------------------------------------|
+ */
+
+ auto *layout = new QVBoxLayout(this);
+ layout->setContentsMargins(mainLayoutHMargin, 0, mainLayoutHMargin, 0);
+ layout->setSpacing(mainLayoutSpacing);
+ mainLayout()->addLayout(layout);
+
+ // Title
+ auto *titleLabel = new QLabel(this);
+ titleLabel->setObjectName("titleLabel");
+ titleLabel->setText(_isQuit ? tr("Quit the beta program") : tr("Join the beta program"));
+ layout->addWidget(titleLabel);
+ layout->addSpacing(titleBoxVSpacing);
+
+ // Main text box
+ if (!_isQuit) {
+ auto *mainTextBox = new QLabel(this);
+ mainTextBox->setObjectName("largeNormalBoldTextLabel");
+ mainTextBox->setText(tr(
+ R"(Get early access to new versions of the application before they are released to the general public, and take )"
+ R"(part in improving the application by sending us your comments.)"));
+ mainTextBox->setWordWrap(true);
+ layout->addWidget(mainTextBox);
+ }
+
+ if (_isStaff) {
+ auto *staffLabel = new QLabel(this);
+ staffLabel->setObjectName("largeMediumBoldTextLabel");
+ staffLabel->setText(tr("Benefit from application beta updates"));
+ layout->addWidget(staffLabel);
+
+ _staffSelectionBox = new CustomComboBox(this);
+ _staffSelectionBox->insertItem(indexNo, tr("No"));
+ _staffSelectionBox->insertItem(indexBeta, tr("Public beta version"));
+ _staffSelectionBox->insertItem(indexInternal, tr("Internal beta version"));
+
+ switch (ParametersCache::instance()->parametersInfo().distributionChannel()) {
+ case DistributionChannel::Prod:
+ _initialIndex = indexNo;
+ break;
+ case DistributionChannel::Beta:
+ _initialIndex = indexBeta;
+ break;
+ case DistributionChannel::Internal:
+ _initialIndex = indexInternal;
+ break;
+ default:
+ break;
+ }
+ _staffSelectionBox->setCurrentIndex(_initialIndex);
+ layout->addWidget(_staffSelectionBox);
+
+ connect(_staffSelectionBox, &CustomComboBox::currentIndexChanged, this, &BetaProgramDialog::onChannelChange);
+ }
+
+ // Acknowledgment
+ _acknowledgmentFrame = new QFrame(this);
+ _acknowledgmentFrame->setStyleSheet("QFrame {border-radius: 8px; background-color: #F4F6FC;}");
+ _acknowledgmentFrame->setVisible(!_isStaff);
+ layout->addWidget(_acknowledgmentFrame);
+
+ auto *acknowledgmentLayout = new QGridLayout(this);
+ _acknowledgmentFrame->setLayout(acknowledgmentLayout);
+ acknowledgmentLayout->setSpacing(subLayoutSpacing);
+
+ auto *warningIcon = new QLabel(this);
+ warningIcon->setPixmap(GuiUtility::getIconWithColor(":/client/resources/icons/actions/warning.svg", iconColor)
+ .pixmap(QSize(iconSize, iconSize)));
+ warningIcon->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+ acknowledgmentLayout->addWidget(warningIcon, 0, 0);
+
+ _acknowledgmentLabel = new QLabel(this);
+ _acknowledgmentLabel->setObjectName("largeNormalTextLabel");
+ if (_isQuit) {
+ setTooRecentMessage();
+ } else {
+ setInstabilityMessage();
+ }
+ _acknowledgmentLabel->setWordWrap(true);
+ _acknowledgmentLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ acknowledgmentLayout->addWidget(_acknowledgmentLabel, 0, 1);
+
+ _acknowledgmentCheckbox = new QCheckBox(tr("I understand"), this);
+ acknowledgmentLayout->addWidget(_acknowledgmentCheckbox, 1, 1);
+
+ if (_isQuit) {
+ auto *bottomTextBox = new QLabel(this);
+ bottomTextBox->setObjectName("largeNormalTextLabel");
+ bottomTextBox->setText(tr("Are you sure you want to leave the beta program?"));
+ bottomTextBox->setWordWrap(true);
+ layout->addWidget(bottomTextBox);
+ }
+
+ layout->addStretch();
+
+ // Buttons
+ auto *buttonLayout = new QHBoxLayout(this);
+ layout->addItem(buttonLayout);
+ buttonLayout->setSpacing(subLayoutSpacing);
+ _saveButton = new QPushButton(this);
+ _saveButton->setObjectName("defaultbutton");
+ _saveButton->setFlat(true);
+ _saveButton->setText(tr("Save"));
+ _saveButton->setEnabled(false);
+ buttonLayout->addWidget(_saveButton);
+ auto *cancelButton = new QPushButton(this);
+ cancelButton->setObjectName("nondefaultbutton");
+ cancelButton->setFlat(true);
+ cancelButton->setText(tr("Cancel"));
+ buttonLayout->addWidget(cancelButton);
+ buttonLayout->addStretch();
+
+ connect(_saveButton, &QPushButton::clicked, this, &BetaProgramDialog::onSave);
+ connect(cancelButton, &QPushButton::clicked, this, &BetaProgramDialog::reject);
+ connect(this, &BetaProgramDialog::exit, this, &BetaProgramDialog::reject);
+ connect(_acknowledgmentCheckbox, &QCheckBox::toggled, this, &BetaProgramDialog::onAcknowledgment);
+}
+
+void BetaProgramDialog::onAcknowledgment() {
+ _saveButton->setEnabled(_acknowledgmentCheckbox->isChecked());
+}
+
+DistributionChannel toDistributionChannel(const int index) {
+ switch (index) {
+ case indexNo:
+ return DistributionChannel::Prod;
+ case indexBeta:
+ return DistributionChannel::Beta;
+ case indexInternal:
+ return DistributionChannel::Internal;
+ default:
+ break;
+ }
+ return DistributionChannel::Unknown;
+}
+
+void BetaProgramDialog::onSave() {
+ if (_isStaff) {
+ _newChannel = toDistributionChannel(_staffSelectionBox->currentIndex());
+ } else {
+ if (_isQuit) {
+ _newChannel = DistributionChannel::Prod;
+ } else {
+ _newChannel = DistributionChannel::Beta;
+ }
+ }
+
+ accept();
+}
+
+void BetaProgramDialog::onChannelChange(const int index) {
+ _acknowledgmentCheckbox->setChecked(false);
+ if (_initialIndex == index) {
+ _acknowledgmentFrame->setVisible(false);
+ return;
+ }
+
+ _acknowledgmentFrame->setVisible(true);
+ if (index > _initialIndex)
+ setInstabilityMessage();
+ else
+ setTooRecentMessage();
+}
+
+void BetaProgramDialog::setTooRecentMessage() const {
+ _acknowledgmentLabel->setText(
+ tr("Your current version of the application may be too recent, your choice will be effective when the next update is "
+ "available."));
+}
+
+void BetaProgramDialog::setInstabilityMessage() const {
+ _acknowledgmentLabel->setText(tr("Beta versions may leave unexpectedly or cause instabilities."));
+}
+
+} // namespace KDC
diff --git a/src/gui/betaprogramdialog.h b/src/gui/betaprogramdialog.h
new file mode 100644
index 000000000..9ebe25c9e
--- /dev/null
+++ b/src/gui/betaprogramdialog.h
@@ -0,0 +1,62 @@
+/*
+ * Infomaniak kDrive - Desktop
+ * Copyright (C) 2023-2024 Infomaniak Network SA
+ *
+ * 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 3 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "customcombobox.h"
+#include "customdialog.h"
+
+#include "utility/types.h"
+
+
+class QLabel;
+class QCheckBox;
+
+namespace KDC {
+
+class CustomComboBox;
+
+class BetaProgramDialog final : public CustomDialog {
+ Q_OBJECT
+
+ public:
+ explicit BetaProgramDialog(bool isQuit, bool isStaff, QWidget *parent = nullptr);
+
+ [[nodiscard]] DistributionChannel selectedDistributionChannel() const { return _newChannel; }
+
+ private slots:
+ void onAcknowledgment();
+ void onSave();
+ void onChannelChange(int index);
+
+ private:
+ void setTooRecentMessage() const;
+ void setInstabilityMessage() const;
+
+ bool _isQuit{false};
+ bool _isStaff{false};
+ DistributionChannel _newChannel{DistributionChannel::Unknown};
+ QCheckBox *_acknowledgmentCheckbox{nullptr};
+ QFrame *_acknowledgmentFrame{nullptr};
+ QLabel *_acknowledgmentLabel{nullptr};
+ CustomComboBox *_staffSelectionBox{nullptr};
+ QPushButton *_saveButton{nullptr};
+ int _initialIndex{0};
+};
+
+} // namespace KDC
diff --git a/src/gui/clientgui.cpp b/src/gui/clientgui.cpp
index a22a1fc58..3cbcb79ab 100644
--- a/src/gui/clientgui.cpp
+++ b/src/gui/clientgui.cpp
@@ -456,9 +456,9 @@ void ClientGui::setupSynthesisPopover() {
_workaroundManualVisibility = true;
#endif
- qCInfo(lcClientGui) << "Tray menu workarounds:" << "noabouttoshow:" << _workaroundNoAboutToShowUpdate
- << "fakedoubleclick:" << _workaroundFakeDoubleClick << "showhide:" << _workaroundShowAndHideTray
- << "manualvisibility:" << _workaroundManualVisibility;
+ qCInfo(lcClientGui) << "Tray menu workarounds:"
+ << "noabouttoshow:" << _workaroundNoAboutToShowUpdate << "fakedoubleclick:" << _workaroundFakeDoubleClick
+ << "showhide:" << _workaroundShowAndHideTray << "manualvisibility:" << _workaroundManualVisibility;
connect(&_delayedTrayUpdateTimer, &QTimer::timeout, this, &ClientGui::onUpdateSystray);
_delayedTrayUpdateTimer.setInterval(2 * 1000);
@@ -758,7 +758,7 @@ void ClientGui::onNewDriveWizard() {
void ClientGui::onShowWindowsUpdateDialog(const VersionInfo &versionInfo) const {
static std::mutex mutex;
- std::unique_lock lock(mutex, std::try_to_lock);
+ const std::unique_lock lock(mutex, std::try_to_lock);
if (!lock.owns_lock()) return;
if (UpdateDialog dialog(versionInfo); dialog.exec() == QDialog::Accepted) {
GuiRequests::startInstaller();
@@ -1016,6 +1016,7 @@ void ClientGui::onUserUpdated(const UserInfo &userInfo) {
if (userInfo.connected()) {
userInfoMapIt->second.setCredentialsAsked(false);
}
+ userInfoMapIt->second.setIsStaff(userInfo.isStaff());
emit userListRefreshed();
}
}
diff --git a/src/gui/customdialog.cpp b/src/gui/customdialog.cpp
index 15781f19e..2d72134d0 100644
--- a/src/gui/customdialog.cpp
+++ b/src/gui/customdialog.cpp
@@ -47,7 +47,7 @@ static const int resizeStripeWidth = 5;
Q_LOGGING_CATEGORY(lcCustomDialog, "gui.customdialog", QtInfoMsg)
-CustomDialog::CustomDialog(bool popup, QWidget *parent) :
+CustomDialog::CustomDialog(const bool popup, QWidget *parent) :
QDialog(parent), _backgroundColor(QColor()), _buttonIconColor(QColor()), _backgroundForcedColor(QColor()), _layout(nullptr) {
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
diff --git a/src/gui/fixconflictingfilesdialog.cpp b/src/gui/fixconflictingfilesdialog.cpp
index d64dd911a..1f0cbcfed 100644
--- a/src/gui/fixconflictingfilesdialog.cpp
+++ b/src/gui/fixconflictingfilesdialog.cpp
@@ -64,8 +64,7 @@ void FixConflictingFilesDialog::onLinkActivated(const QString &link) {
}
} else {
if (!QDesktopServices::openUrl(QUrl(link))) {
- CustomMessageBox msgBox(QMessageBox::Warning, tr("Unable to open link %1.").arg(link),
- QMessageBox::Ok, this);
+ CustomMessageBox msgBox(QMessageBox::Warning, tr("Unable to open link %1.").arg(link), QMessageBox::Ok, this);
msgBox.exec();
}
}
diff --git a/src/gui/guirequests.cpp b/src/gui/guirequests.cpp
index 18a791a78..e0f06e990 100644
--- a/src/gui/guirequests.cpp
+++ b/src/gui/guirequests.cpp
@@ -1212,9 +1212,14 @@ ExitCode GuiRequests::changeDistributionChannel(const DistributionChannel channe
return ExitCode::Ok;
}
-ExitCode GuiRequests::versionInfo(VersionInfo &versionInfo) {
+ExitCode GuiRequests::versionInfo(VersionInfo &versionInfo,
+ const DistributionChannel channel /*= DistributionChannel::Unknown*/) {
+ QByteArray params;
+ QDataStream paramsStream(¶ms, QIODevice::WriteOnly);
+ paramsStream << channel;
+
QByteArray results;
- if (!CommClient::instance()->execute(RequestNum::UPDATER_VERSION_INFO, {}, results)) {
+ if (!CommClient::instance()->execute(RequestNum::UPDATER_VERSION_INFO, params, results)) {
return ExitCode::SystemError;
}
diff --git a/src/gui/guirequests.h b/src/gui/guirequests.h
index ee2327462..2cdec1fdf 100644
--- a/src/gui/guirequests.h
+++ b/src/gui/guirequests.h
@@ -129,7 +129,7 @@ struct GuiRequests {
static ExitCode crash();
static ExitCode changeDistributionChannel(DistributionChannel channel);
- static ExitCode versionInfo(VersionInfo &versionInfo);
+ static ExitCode versionInfo(VersionInfo &versionInfo, DistributionChannel channel = DistributionChannel::Unknown);
static ExitCode updateState(UpdateState &state);
static ExitCode startInstaller();
static ExitCode skipUpdate(const std::string &version);
diff --git a/src/gui/parameterscache.h b/src/gui/parameterscache.h
index c018ebcc4..62bc36876 100644
--- a/src/gui/parameterscache.h
+++ b/src/gui/parameterscache.h
@@ -27,12 +27,12 @@ namespace KDC {
class ParametersCache {
public:
static std::shared_ptr instance() noexcept;
- inline static bool isExtendedLogEnabled() noexcept { return instance()->_parametersInfo.extendedLog(); };
+ static bool isExtendedLogEnabled() noexcept { return instance()->_parametersInfo.extendedLog(); };
ParametersCache(ParametersCache const &) = delete;
void operator=(ParametersCache const &) = delete;
- inline ParametersInfo ¶metersInfo() { return _parametersInfo; }
+ ParametersInfo ¶metersInfo() { return _parametersInfo; }
bool saveParametersInfo(bool displayMessageBoxOnError = true);
private:
diff --git a/src/gui/preferenceswidget.cpp b/src/gui/preferenceswidget.cpp
index dd6c2867e..bd49e7f10 100644
--- a/src/gui/preferenceswidget.cpp
+++ b/src/gui/preferenceswidget.cpp
@@ -423,7 +423,7 @@ void PreferencesWidget::showErrorBanner(const bool unresolvedErrors) const {
void PreferencesWidget::showEvent(QShowEvent *event) {
Q_UNUSED(event)
retranslateUi();
- _versionWidget->refresh();
+ _versionWidget->refresh(isStaff());
}
void PreferencesWidget::clearUndecidedLists() {
@@ -441,6 +441,11 @@ void PreferencesWidget::clearUndecidedLists() {
}
}
+bool PreferencesWidget::isStaff() const {
+ constexpr auto isStaffCallback = [](std::pair const &item) { return item.second.isStaff(); };
+ return std::ranges::find_if(_gui->userInfoMap(), isStaffCallback) != _gui->userInfoMap().end();
+}
+
void PreferencesWidget::onFolderConfirmationSwitchClicked(bool checked) {
ParametersCache::instance()->parametersInfo().setUseBigFolderSizeLimit(checked);
if (!ParametersCache::instance()->saveParametersInfo()) {
@@ -501,7 +506,7 @@ void PreferencesWidget::onLanguageChange() {
CommonUtility::setupTranslations(QApplication::instance(), language);
retranslateUi();
- _versionWidget->refresh();
+ _versionWidget->refresh(isStaff());
}
void PreferencesWidget::onMoveToTrashSwitchClicked(bool checked) {
diff --git a/src/gui/preferenceswidget.h b/src/gui/preferenceswidget.h
index fb228b87b..5769efe66 100644
--- a/src/gui/preferenceswidget.h
+++ b/src/gui/preferenceswidget.h
@@ -99,6 +99,8 @@ class PreferencesWidget : public LargeWidgetWithCustomToolTip {
void clearUndecidedLists();
+ [[nodiscard]] bool isStaff() const;
+
private slots:
void onFolderConfirmationSwitchClicked(bool checked = false);
void onFolderConfirmationAmountTextEdited(const QString &text);
diff --git a/src/gui/synthesispopover.cpp b/src/gui/synthesispopover.cpp
index 37c6598b2..55268a5d7 100644
--- a/src/gui/synthesispopover.cpp
+++ b/src/gui/synthesispopover.cpp
@@ -963,9 +963,7 @@ void SynthesisPopover::onItemCompleted(int syncDbId, const SyncFileItemInfo &ite
}
}
-void SynthesisPopover::onOpenErrorsMenu(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onOpenErrorsMenu() {
QList driveErrorList;
getDriveErrorList(driveErrorList);
@@ -1080,7 +1078,7 @@ void SynthesisPopover::onUpdateAvailabilityChange(const UpdateState updateState)
default:
_lockedAppUpdateButton->setText(tr("Unavailable"));
sentry::Handler::captureMessage(sentry::Level::Fatal, "AppLocked",
- "HTTP Error426 received but unable to fetch an update");
+ "HTTP Error426 received but unable to fetch an update");
break;
}
}
@@ -1110,16 +1108,12 @@ void SynthesisPopover::onRefreshErrorList(int /*driveDbId*/) {
refreshErrorsButton();
}
-void SynthesisPopover::onOpenFolder(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onOpenFolder() {
int syncDbId = qvariant_cast(sender()->property(MenuWidget::actionTypeProperty.c_str()));
openUrl(syncDbId);
}
-void SynthesisPopover::onOpenWebview(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onOpenWebview() {
if (_gui->currentDriveDbId() != 0) {
const auto driveInfoIt = _gui->driveInfoMap().find(_gui->currentDriveDbId());
if (driveInfoIt == _gui->driveInfoMap().end()) {
@@ -1143,17 +1137,15 @@ void SynthesisPopover::onOpenWebview(bool checked) {
}
}
-void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
- Q_UNUSED(checked)
-
- MenuWidget *menu = new MenuWidget(MenuWidget::Menu, this);
+void SynthesisPopover::onOpenMiscellaneousMenu() {
+ auto *menu = new MenuWidget(MenuWidget::Menu, this);
// Open Folder
std::map syncInfoMap;
_gui->loadSyncInfoMap(_gui->currentDriveDbId(), syncInfoMap);
- if (syncInfoMap.size() >= 1) {
- QWidgetAction *foldersMenuAction = new QWidgetAction(this);
- MenuItemWidget *foldersMenuItemWidget = new MenuItemWidget(tr("Open in folder"));
+ if (!syncInfoMap.empty()) {
+ auto *foldersMenuAction = new QWidgetAction(this);
+ auto *foldersMenuItemWidget = new MenuItemWidget(tr("Open in folder"));
foldersMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/folder.svg");
foldersMenuAction->setDefaultWidget(foldersMenuItemWidget);
@@ -1165,16 +1157,15 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
foldersMenuItemWidget->setHasSubmenu(true);
// Open folders submenu
- MenuWidget *submenu = new MenuWidget(MenuWidget::Submenu, menu);
+ auto *submenu = new MenuWidget(MenuWidget::Submenu, menu);
- QActionGroup *openFolderActionGroup = new QActionGroup(this);
+ auto *openFolderActionGroup = new QActionGroup(this);
openFolderActionGroup->setExclusive(true);
- QWidgetAction *openFolderAction;
- for (auto const &syncInfoMapElt: syncInfoMap) {
- openFolderAction = new QWidgetAction(this);
- openFolderAction->setProperty(MenuWidget::actionTypeProperty.c_str(), syncInfoMapElt.first);
- MenuItemWidget *openFolderMenuItemWidget = new MenuItemWidget(syncInfoMapElt.second.name());
+ for (auto const &[syncId, syncInfo]: syncInfoMap) {
+ auto *openFolderAction = new QWidgetAction(this);
+ openFolderAction->setProperty(MenuWidget::actionTypeProperty.c_str(), syncId);
+ auto *openFolderMenuItemWidget = new MenuItemWidget(syncInfo.name());
openFolderMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/folder.svg");
openFolderAction->setDefaultWidget(openFolderMenuItemWidget);
connect(openFolderAction, &QWidgetAction::triggered, this, &SynthesisPopover::onOpenFolder);
@@ -1189,8 +1180,8 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
}
// Open web version
- QWidgetAction *driveOpenWebViewAction = new QWidgetAction(this);
- MenuItemWidget *driveOpenWebViewMenuItemWidget = new MenuItemWidget(tr("Open %1 web version").arg(APPLICATION_SHORTNAME));
+ auto *driveOpenWebViewAction = new QWidgetAction(this);
+ auto *driveOpenWebViewMenuItemWidget = new MenuItemWidget(tr("Open %1 web version").arg(APPLICATION_SHORTNAME));
driveOpenWebViewMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/webview.svg");
driveOpenWebViewAction->setDefaultWidget(driveOpenWebViewMenuItemWidget);
driveOpenWebViewAction->setVisible(_gui->currentDriveDbId() != 0);
@@ -1198,9 +1189,9 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
menu->addAction(driveOpenWebViewAction);
// Drive parameters
- if (_gui->driveInfoMap().size()) {
- QWidgetAction *driveParametersAction = new QWidgetAction(this);
- MenuItemWidget *driveParametersMenuItemWidget = new MenuItemWidget(tr("Drive parameters"));
+ if (!_gui->driveInfoMap().empty()) {
+ auto *driveParametersAction = new QWidgetAction(this);
+ auto *driveParametersMenuItemWidget = new MenuItemWidget(tr("Drive parameters"));
driveParametersMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/drive.svg");
driveParametersAction->setDefaultWidget(driveParametersMenuItemWidget);
connect(driveParametersAction, &QWidgetAction::triggered, this, &SynthesisPopover::onOpenDriveParameters);
@@ -1208,10 +1199,10 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
}
// Disable Notifications
- QWidgetAction *notificationsMenuAction = new QWidgetAction(this);
- bool notificationAlreadyDisabledForPeriod =
+ auto *notificationsMenuAction = new QWidgetAction(this);
+ const auto notificationAlreadyDisabledForPeriod =
_notificationsDisabled != NotificationsDisabled::Never && _notificationsDisabled != NotificationsDisabled::Always;
- MenuItemWidget *notificationsMenuItemWidget =
+ auto *notificationsMenuItemWidget =
new MenuItemWidget(notificationAlreadyDisabledForPeriod
? tr("Notifications disabled until %1").arg(_notificationsDisabledUntilDateTime.toString())
: tr("Disable Notifications"));
@@ -1221,9 +1212,9 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
menu->addAction(notificationsMenuAction);
// Disable Notifications submenu
- MenuWidget *submenu = new MenuWidget(MenuWidget::Submenu, menu);
+ auto *submenu = new MenuWidget(MenuWidget::Submenu, menu);
- QActionGroup *notificationActionGroup = new QActionGroup(this);
+ auto *notificationActionGroup = new QActionGroup(this);
notificationActionGroup->setExclusive(true);
const std::map ¬ificationMap =
@@ -1231,13 +1222,12 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
? _notificationsDisabledMap
: _notificationsDisabledForPeriodMap;
- QWidgetAction *notificationAction;
- for (auto const ¬ificationMapElt: notificationMap) {
- notificationAction = new QWidgetAction(this);
- notificationAction->setProperty(MenuWidget::actionTypeProperty.c_str(), toInt(notificationMapElt.first));
- QString text = QCoreApplication::translate("KDC::SynthesisPopover", notificationMapElt.second.toStdString().c_str());
- MenuItemWidget *notificationMenuItemWidget = new MenuItemWidget(text);
- notificationMenuItemWidget->setChecked(notificationMapElt.first == _notificationsDisabled);
+ for (auto const &[notifDisabled, str]: notificationMap) {
+ auto *notificationAction = new QWidgetAction(this);
+ notificationAction->setProperty(MenuWidget::actionTypeProperty.c_str(), toInt(notifDisabled));
+ QString text = QCoreApplication::translate("KDC::SynthesisPopover", str.toStdString().c_str());
+ auto *notificationMenuItemWidget = new MenuItemWidget(text);
+ notificationMenuItemWidget->setChecked(notifDisabled == _notificationsDisabled);
notificationAction->setDefaultWidget(notificationMenuItemWidget);
connect(notificationAction, &QWidgetAction::triggered, this, &SynthesisPopover::onNotificationActionTriggered);
notificationActionGroup->addAction(notificationAction);
@@ -1247,24 +1237,32 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
notificationsMenuAction->setMenu(submenu);
// Application preferences
- QWidgetAction *preferencesAction = new QWidgetAction(this);
- MenuItemWidget *preferencesMenuItemWidget = new MenuItemWidget(tr("Application preferences"));
+ auto *preferencesAction = new QWidgetAction(this);
+ auto *preferencesMenuItemWidget = new MenuItemWidget(tr("Application preferences"));
preferencesMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/parameters.svg");
preferencesAction->setDefaultWidget(preferencesMenuItemWidget);
connect(preferencesAction, &QWidgetAction::triggered, this, &SynthesisPopover::onOpenPreferences);
menu->addAction(preferencesAction);
// Help
- QWidgetAction *helpAction = new QWidgetAction(this);
- MenuItemWidget *helpMenuItemWidget = new MenuItemWidget(tr("Need help"));
+ auto *helpAction = new QWidgetAction(this);
+ auto *helpMenuItemWidget = new MenuItemWidget(tr("Need help"));
helpMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/help.svg");
helpAction->setDefaultWidget(helpMenuItemWidget);
connect(helpAction, &QWidgetAction::triggered, this, &SynthesisPopover::onDisplayHelp);
menu->addAction(helpAction);
+ // Send feedbacks
+ auto *feedbacksAction = new QWidgetAction(this);
+ auto *feedbacksMenuItemWidget = new MenuItemWidget(tr("Send feedbacks"));
+ feedbacksMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/messages-bubble-square-typing.svg");
+ feedbacksAction->setDefaultWidget(feedbacksMenuItemWidget);
+ connect(feedbacksAction, &QWidgetAction::triggered, this, &SynthesisPopover::onSendFeedback);
+ menu->addAction(feedbacksAction);
+
// Quit
- QWidgetAction *exitAction = new QWidgetAction(this);
- MenuItemWidget *exitMenuItemWidget = new MenuItemWidget(tr("Quit kDrive"));
+ auto *exitAction = new QWidgetAction(this);
+ auto *exitMenuItemWidget = new MenuItemWidget(tr("Quit kDrive"));
exitMenuItemWidget->setLeftIcon(":/client/resources/icons/actions/error-sync.svg");
exitAction->setDefaultWidget(exitMenuItemWidget);
connect(exitAction, &QWidgetAction::triggered, this, &SynthesisPopover::onExit);
@@ -1272,29 +1270,29 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
if (_debugCrash) {
// Emulate a crash
- QWidgetAction *crashAction = new QWidgetAction(this);
- MenuItemWidget *crashMenuItemWidget = new MenuItemWidget("Emulate a crash");
+ auto *crashAction = new QWidgetAction(this);
+ auto *crashMenuItemWidget = new MenuItemWidget("Emulate a crash");
crashAction->setDefaultWidget(crashMenuItemWidget);
connect(crashAction, &QWidgetAction::triggered, this, &SynthesisPopover::onCrash);
menu->addAction(crashAction);
// Emulate a server crash
- QWidgetAction *crashServerAction = new QWidgetAction(this);
- MenuItemWidget *crashServerMenuItemWidget = new MenuItemWidget("Emulate a server crash");
+ auto *crashServerAction = new QWidgetAction(this);
+ auto *crashServerMenuItemWidget = new MenuItemWidget("Emulate a server crash");
crashServerAction->setDefaultWidget(crashServerMenuItemWidget);
connect(crashServerAction, &QWidgetAction::triggered, this, &SynthesisPopover::onCrashServer);
menu->addAction(crashServerAction);
// Emulate an ENFORCE crash
- QWidgetAction *crashEnforceAction = new QWidgetAction(this);
- MenuItemWidget *crashEnforceMenuItemWidget = new MenuItemWidget("Emulate an ENFORCE crash");
+ auto *crashEnforceAction = new QWidgetAction(this);
+ auto *crashEnforceMenuItemWidget = new MenuItemWidget("Emulate an ENFORCE crash");
crashEnforceAction->setDefaultWidget(crashEnforceMenuItemWidget);
connect(crashEnforceAction, &QWidgetAction::triggered, this, &SynthesisPopover::onCrashEnforce);
menu->addAction(crashEnforceAction);
// Emulate a qFatal crash
- QWidgetAction *crashFatalAction = new QWidgetAction(this);
- MenuItemWidget *crashFatalMenuItemWidget = new MenuItemWidget("Emulate a qFatal crash");
+ auto *crashFatalAction = new QWidgetAction(this);
+ auto *crashFatalMenuItemWidget = new MenuItemWidget("Emulate a qFatal crash");
crashFatalAction->setDefaultWidget(crashFatalMenuItemWidget);
connect(crashFatalAction, &QWidgetAction::triggered, this, &SynthesisPopover::onCrashFatal);
menu->addAction(crashFatalAction);
@@ -1303,52 +1301,41 @@ void SynthesisPopover::onOpenMiscellaneousMenu(bool checked) {
menu->exec(QWidget::mapToGlobal(_menuButton->geometry().center()));
}
-void SynthesisPopover::onOpenPreferences(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onOpenPreferences() {
emit showParametersDialog();
}
-void SynthesisPopover::onDisplayHelp(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onDisplayHelp() {
QDesktopServices::openUrl(QUrl(Theme::instance()->helpUrl()));
}
-void SynthesisPopover::onExit(bool checked) {
- Q_UNUSED(checked)
+void SynthesisPopover::onSendFeedback() {
+ const auto url = QUrl(Theme::instance()->feedbackUrl(ParametersCache::instance()->parametersInfo().language()));
+ QDesktopServices::openUrl(url);
+}
+void SynthesisPopover::onExit() {
hide();
emit exit();
}
-void SynthesisPopover::onCrash(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onCrash() {
emit crash();
}
-void SynthesisPopover::onCrashServer(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onCrashServer() {
emit crashServer();
}
-void SynthesisPopover::onCrashEnforce(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onCrashEnforce() {
emit crashEnforce();
}
-void SynthesisPopover::onCrashFatal(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onCrashFatal() {
emit crashFatal();
}
-void SynthesisPopover::onNotificationActionTriggered(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onNotificationActionTriggered() {
bool notificationAlreadyDisabledForPeriod =
_notificationsDisabled != NotificationsDisabled::Never && _notificationsDisabled != NotificationsDisabled::Always;
@@ -1383,9 +1370,7 @@ void SynthesisPopover::onNotificationActionTriggered(bool checked) {
emit disableNotifications(_notificationsDisabled, _notificationsDisabledUntilDateTime);
}
-void SynthesisPopover::onOpenDriveParameters(bool checked) {
- Q_UNUSED(checked)
-
+void SynthesisPopover::onOpenDriveParameters() {
emit showParametersDialog(_gui->currentDriveDbId());
}
diff --git a/src/gui/synthesispopover.h b/src/gui/synthesispopover.h
index e97a75567..c9ad1319e 100644
--- a/src/gui/synthesispopover.h
+++ b/src/gui/synthesispopover.h
@@ -148,20 +148,21 @@ class SynthesisPopover : public QDialog {
void handleRemovedDrives();
private slots:
- void onOpenErrorsMenu(bool checked = false);
+ void onOpenErrorsMenu();
void onDisplayErrors(int syncDbId);
- void onOpenFolder(bool checked);
- void onOpenWebview(bool checked);
- void onOpenMiscellaneousMenu(bool checked);
- void onOpenPreferences(bool checked = false);
- void onNotificationActionTriggered(bool checked = false);
- void onOpenDriveParameters(bool checked = false);
- void onDisplayHelp(bool checked = false);
- void onExit(bool checked = false);
- void onCrash(bool checked = false);
- void onCrashServer(bool checked = false);
- void onCrashEnforce(bool checked = false);
- void onCrashFatal(bool checked = false);
+ void onOpenFolder();
+ void onOpenWebview();
+ void onOpenMiscellaneousMenu();
+ void onOpenPreferences();
+ void onNotificationActionTriggered();
+ void onOpenDriveParameters();
+ void onDisplayHelp();
+ void onSendFeedback();
+ void onExit();
+ void onCrash();
+ void onCrashServer();
+ void onCrashEnforce();
+ void onCrashFatal();
void onDriveSelected(int driveDbId);
void onAddDrive();
void onPauseSync(ActionTarget target, int syncDbId = 0);
diff --git a/src/gui/taglabel.cpp b/src/gui/taglabel.cpp
new file mode 100644
index 000000000..b5bbb1187
--- /dev/null
+++ b/src/gui/taglabel.cpp
@@ -0,0 +1,78 @@
+/*
+ * Infomaniak kDrive - Desktop
+ * Copyright (C) 2023-2024 Infomaniak Network SA
+ *
+ * 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 3 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "taglabel.h"
+
+#include "guiutility.h"
+
+#include
+#include
+#include
+#include
+
+namespace KDC {
+
+static constexpr int hMargin = 4;
+#ifdef __APPLE__
+static constexpr int offset = 0;
+#else
+static constexpr int offset = 2;
+#endif
+
+TagLabel::TagLabel(const QColor &color /*= Qt::transparent*/, QWidget *parent /*= nullptr*/) :
+ QLabel(parent), _backgroundColor(color) {
+ setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ setAlignment(Qt::AlignCenter);
+}
+
+
+QSize TagLabel::sizeHint() const {
+ const QFontMetrics fm(_customFont);
+ const auto textSize = fm.size(Qt::TextSingleLine, text());
+ return {textSize.width() + 2 * hMargin + offset, textSize.height() + offset};
+}
+
+void TagLabel::paintEvent(QPaintEvent *event) {
+ Q_UNUSED(event)
+
+ // Update shadow color
+ if (auto *effect = qobject_cast(graphicsEffect())) {
+ effect->setColor(GuiUtility::getShadowColor());
+ }
+
+ // Draw round rectangle
+ QPainterPath painterPath;
+ painterPath.addRoundedRect(rect(), rect().height() / 2, rect().height() / 2);
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(backgroundColor());
+ painter.drawPath(painterPath);
+
+ // Draw text
+ painter.setPen(QColor(255, 255, 255));
+ QTextOption textOption;
+ textOption.setAlignment(Qt::AlignCenter);
+ painter.setFont(_customFont);
+ QRect tmp = rect();
+ tmp.translate(0, offset);
+ painter.drawText(tmp, text(), textOption);
+}
+
+} // namespace KDC
diff --git a/src/gui/taglabel.h b/src/gui/taglabel.h
new file mode 100644
index 000000000..3db8bfe48
--- /dev/null
+++ b/src/gui/taglabel.h
@@ -0,0 +1,45 @@
+/*
+ * Infomaniak kDrive - Desktop
+ * Copyright (C) 2023-2024 Infomaniak Network SA
+ *
+ * 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 3 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+
+namespace KDC {
+
+class TagLabel final : public QLabel {
+ Q_OBJECT
+
+ public:
+ explicit TagLabel(const QColor &color = Qt::transparent, QWidget *parent = nullptr);
+
+ [[nodiscard]] QColor backgroundColor() const { return _backgroundColor; }
+ void setBackgroundColor(const QColor &value) { _backgroundColor = value; }
+
+ [[nodiscard]] const QFont &setCustomFont() const { return _customFont; }
+ void customFont(const QFont &font) { _customFont = font; }
+
+ private:
+ [[nodiscard]] QSize sizeHint() const override;
+ void paintEvent(QPaintEvent *event) override;
+
+ QColor _backgroundColor{Qt::transparent};
+ QFont _customFont{"Suisse Int'l", 12};
+};
+
+} // namespace KDC
diff --git a/src/gui/versionwidget.cpp b/src/gui/versionwidget.cpp
index 8d8049fb8..5bf2f67ed 100644
--- a/src/gui/versionwidget.cpp
+++ b/src/gui/versionwidget.cpp
@@ -22,11 +22,12 @@
#include "enablestateholder.h"
#include "guirequests.h"
#include "guiutility.h"
+#include "betaprogramdialog.h"
#include "parameterscache.h"
#include "preferencesblocwidget.h"
-#include "utility/utility.h"
-#include "utility/widgetsignalblocker.h"
+#include "taglabel.h"
#include "libcommon/utility/utility.h"
+#include "utility/utility.h"
#include
#include
@@ -43,6 +44,12 @@ static const QString versionLink = "versionLink";
static const QString releaseNoteLink = "releaseNoteLink";
static const QString downloadPageLink = "downloadPageLink";
+static constexpr int statusLayoutSpacing = 8;
+static constexpr auto betaTagColor = QColor(214, 56, 100);
+static constexpr auto internalTagColor = QColor(120, 116, 176);
+
+Q_LOGGING_CATEGORY(lcVersionWidget, "gui.versionwidget", QtInfoMsg)
+
VersionWidget::VersionWidget(QWidget *parent /*= nullptr*/) : QWidget(parent) {
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
@@ -50,67 +57,94 @@ VersionWidget::VersionWidget(QWidget *parent /*= nullptr*/) : QWidget(parent) {
_versionLabel = new QLabel(this);
_versionLabel->setObjectName("blocLabel");
- layout()->addWidget(_versionLabel);
+ mainLayout->addWidget(_versionLabel);
- const auto versionBloc = new PreferencesBlocWidget();
- layout()->addWidget(versionBloc);
- QBoxLayout *versionBox = versionBloc->addLayout(QBoxLayout::Direction::LeftToRight);
- const auto versionVBox = new QVBoxLayout();
- versionVBox->setContentsMargins(0, 0, 0, 0);
- versionVBox->setSpacing(1);
- versionBox->addLayout(versionVBox);
- versionBox->setStretchFactor(versionVBox, 1);
+ const auto prefBloc = new PreferencesBlocWidget();
+ mainLayout->addWidget(prefBloc);
- _updateStatusLabel = new QLabel(this);
- _updateStatusLabel->setObjectName("boldTextLabel");
- _updateStatusLabel->setWordWrap(true);
- versionVBox->addWidget(_updateStatusLabel);
-
- // TODO : add it back later (version 3.6.8 or 4.0)
- // const auto channelBox = new QHBoxLayout(this);
- // _prodButton = new QRadioButton(tr("Prod"), this);
- // channelBox->addWidget(_prodButton);
- // channelBox->addStretch();
- // _betaButton = new QRadioButton(tr("Beta"), this);
- // channelBox->addWidget(_betaButton);
- // channelBox->addStretch();
- // _internalButton = new QRadioButton(tr("Internal"), this);
- // channelBox->addWidget(_internalButton);
- // channelBox->addStretch();
- // versionVBox->addLayout(channelBox);
-
- _showReleaseNotesLabel = new QLabel(this);
- _showReleaseNotesLabel->setObjectName("boldTextLabel");
- _showReleaseNotesLabel->setWordWrap(true);
- _showReleaseNotesLabel->setVisible(false);
- versionVBox->addWidget(_showReleaseNotesLabel);
-
- static const QString versionNumberLinkText =
- tr(R"(%3)").arg(CommonUtility::linkStyle, versionLink, KDRIVE_VERSION_STRING);
- _versionNumberLabel = new QLabel(this);
- _versionNumberLabel->setContextMenuPolicy(Qt::PreventContextMenu);
- _versionNumberLabel->setText(versionNumberLinkText);
- versionVBox->addWidget(_versionNumberLabel);
-
- const auto copyrightLabel = new QLabel(QString("Copyright %1").arg(APPLICATION_VENDOR));
- copyrightLabel->setObjectName("description");
- versionVBox->addWidget(copyrightLabel);
-
- _updateButton = new QPushButton(this);
- _updateButton->setObjectName("defaultbutton");
- _updateButton->setFlat(true);
- versionBox->addWidget(_updateButton);
+ initVersionInfoBloc(prefBloc);
+ prefBloc->addSeparator();
+ initBetaBloc(prefBloc);
refresh();
- // TODO : add it back later (version 3.6.8 or 4.0)
- // connect(_prodButton, &QRadioButton::clicked, this, &VersionWidget::onChannelButtonClicked);
- // connect(_betaButton, &QRadioButton::clicked, this, &VersionWidget::onChannelButtonClicked);
- // connect(_internalButton, &QRadioButton::clicked, this, &VersionWidget::onChannelButtonClicked);
connect(_updateStatusLabel, &QLabel::linkActivated, this, &VersionWidget::onLinkActivated);
connect(_versionNumberLabel, &QLabel::linkActivated, this, &VersionWidget::onLinkActivated);
connect(_showReleaseNotesLabel, &QLabel::linkActivated, this, &VersionWidget::onLinkActivated);
connect(_updateButton, &QPushButton::clicked, this, &VersionWidget::onUpdateButtonClicked);
+ connect(_joinBetaButton, &QPushButton::clicked, this, &VersionWidget::onJoinBetaButtonClicked);
+}
+
+void VersionWidget::refresh(const bool isStaff) {
+ _isStaff = isStaff;
+ refresh();
+}
+
+void VersionWidget::showAboutDialog() {
+ EnableStateHolder _(this);
+ AboutDialog dialog(this);
+ dialog.execAndMoveToCenter(GuiUtility::getTopLevelWidget(this));
+}
+
+void VersionWidget::showReleaseNotes() const {
+ QString os;
+#if defined(__APPLE__)
+ os = "macos";
+#elif defined(_WIN32)
+ os = "win";
+#else
+ os = "linux";
+#endif
+
+ VersionInfo versionInfo;
+ GuiRequests::versionInfo(versionInfo);
+
+ const Language &appLanguage = ParametersCache::instance()->parametersInfo().language();
+ QString languageCode = CommonUtility::languageCode(appLanguage);
+ if (languageCode.isEmpty()) languageCode = "en";
+ QDesktopServices::openUrl(
+ QUrl(QString("%1-%2-%3-%4.html")
+ .arg(APPLICATION_STORAGE_URL, versionInfo.fullVersion().c_str(), os, languageCode.left(2))));
+}
+
+void VersionWidget::showDownloadPage() const {
+ QDesktopServices::openUrl(QUrl(APPLICATION_DOWNLOAD_URL));
+}
+
+void VersionWidget::onUpdateStateChanged(const UpdateState state) const {
+ refresh(state);
+}
+
+void VersionWidget::onLinkActivated(const QString &link) {
+ if (link == versionLink)
+ showAboutDialog();
+ else if (link == releaseNoteLink)
+ showReleaseNotes();
+ else if (link == downloadPageLink)
+ showDownloadPage();
+ else {
+ qCWarning(lcVersionWidget) << "Unknown link clicked: " << link;
+ Q_ASSERT(false);
+ }
+}
+
+void VersionWidget::onUpdateButtonClicked() {
+#if defined(__APPLE__)
+ GuiRequests::startInstaller();
+#else
+ VersionInfo versionInfo;
+ GuiRequests::versionInfo(versionInfo);
+ emit showUpdateDialog(versionInfo);
+#endif
+}
+
+void VersionWidget::onJoinBetaButtonClicked() {
+ if (auto dialog = BetaProgramDialog(
+ ParametersCache::instance()->parametersInfo().distributionChannel() != DistributionChannel::Prod, _isStaff, this);
+ dialog.exec() == QDialog::Accepted) {
+ saveDistributionChannel(dialog.selectedDistributionChannel());
+ refresh();
+ }
}
void VersionWidget::refresh(UpdateState state /*= UpdateState::Unknown*/) const {
@@ -179,97 +213,104 @@ void VersionWidget::refresh(UpdateState state /*= UpdateState::Unknown*/) const
_updateStatusLabel->setText(statusString);
_showReleaseNotesLabel->setVisible(showReleaseNote);
_updateButton->setVisible(showUpdateButton);
-}
-void VersionWidget::showAboutDialog() {
- EnableStateHolder _(this);
- AboutDialog dialog(this);
- dialog.execAndMoveToCenter(GuiUtility::getTopLevelWidget(this));
+ // Beta version info
+ if (_betaVersionLabel) {
+ _betaVersionLabel->setText(tr("Beta program"));
+ _betaVersionDescription->setText(tr("Get early access to new versions of the application"));
+
+ if (const auto channel = ParametersCache::instance()->parametersInfo().distributionChannel();
+ channel == DistributionChannel::Prod) {
+ _joinBetaButton->setText(tr("Join"));
+ _betaTag->setVisible(false);
+ } else {
+ _joinBetaButton->setText(_isStaff ? tr("Modify") : tr("Quit"));
+ _betaTag->setVisible(true);
+ _betaTag->setBackgroundColor(channel == DistributionChannel::Beta ? betaTagColor : internalTagColor);
+ _betaTag->setText(channel == DistributionChannel::Beta ? "BETA" : "INTERNAL");
+ }
+ }
}
-void VersionWidget::showReleaseNotes() const {
- QString os;
-#if defined(__APPLE__)
- os = "macos";
-#elif defined(_WIN32)
- os = "win";
-#else
- os = "linux";
-#endif
+void VersionWidget::initVersionInfoBloc(PreferencesBlocWidget *prefBloc) {
+ auto *versionLayout = prefBloc->addLayout(QBoxLayout::Direction::LeftToRight);
- VersionInfo versionInfo;
- GuiRequests::versionInfo(versionInfo);
+ auto *verticalLayout = new QVBoxLayout(this);
+ verticalLayout->setSpacing(1);
+ verticalLayout->setContentsMargins(0, 0, 0, 0);
+ versionLayout->addLayout(verticalLayout);
- const Language &appLanguage = ParametersCache::instance()->parametersInfo().language();
- QString languageCode = CommonUtility::languageCode(appLanguage);
- if (languageCode.isEmpty()) languageCode = "en";
- QDesktopServices::openUrl(
- QUrl(QString("%1-%2-%3-%4.html")
- .arg(APPLICATION_STORAGE_URL, versionInfo.fullVersion().c_str(), os, languageCode.left(2))));
-}
+ auto *statusLayout = new QHBoxLayout(this);
+ statusLayout->setSpacing(statusLayoutSpacing);
+ _updateStatusLabel = new QLabel(this);
+ _updateStatusLabel->setObjectName("boldTextLabel");
+ statusLayout->addWidget(_updateStatusLabel);
-void VersionWidget::showDownloadPage() const {
- QDesktopServices::openUrl(QUrl(APPLICATION_DOWNLOAD_URL));
-}
+ _betaTag = new TagLabel(betaTagColor, this);
+ _betaTag->setText("BETA");
+ _betaTag->setVisible(false);
+ statusLayout->addWidget(_betaTag);
+ statusLayout->addStretch();
+ verticalLayout->addLayout(statusLayout);
-void VersionWidget::onUpdateStateChanged(const UpdateState state) const {
- refresh(state);
-}
+ _showReleaseNotesLabel = new QLabel(this);
+ _showReleaseNotesLabel->setObjectName("boldTextLabel");
+ _showReleaseNotesLabel->setWordWrap(true);
+ _showReleaseNotesLabel->setVisible(false);
+ verticalLayout->addWidget(_showReleaseNotesLabel);
-void VersionWidget::onChannelButtonClicked() const {
- auto channel = DistributionChannel::Unknown;
- if (sender() == _prodButton)
- channel = DistributionChannel::Prod;
- else if (sender() == _betaButton)
- channel = DistributionChannel::Beta;
- else if (sender() == _internalButton)
- channel = DistributionChannel::Internal;
- else
- return;
+ static const QString versionNumberLinkText =
+ tr(R"(%3)").arg(CommonUtility::linkStyle, versionLink, KDRIVE_VERSION_STRING);
+ _versionNumberLabel = new QLabel(this);
+ _versionNumberLabel->setContextMenuPolicy(Qt::PreventContextMenu);
+ _versionNumberLabel->setText(versionNumberLinkText);
+ verticalLayout->addWidget(_versionNumberLabel);
- GuiRequests::changeDistributionChannel(channel);
- refresh();
-}
+ const auto copyrightLabel = new QLabel(QString("Copyright %1").arg(APPLICATION_VENDOR));
+ copyrightLabel->setObjectName("description");
+ verticalLayout->addWidget(copyrightLabel);
-void VersionWidget::onLinkActivated(const QString &link) {
- if (link == versionLink)
- showAboutDialog();
- else if (link == releaseNoteLink)
- showReleaseNotes();
- else if (link == downloadPageLink)
- showDownloadPage();
+ _updateButton = new QPushButton(this);
+ _updateButton->setObjectName("defaultbutton");
+ _updateButton->setFlat(true);
+ versionLayout->addWidget(_updateButton);
}
-void VersionWidget::onUpdateButtonClicked() {
-#if defined(__APPLE__)
- GuiRequests::startInstaller();
-#else
- VersionInfo versionInfo;
- GuiRequests::versionInfo(versionInfo);
- emit showUpdateDialog(versionInfo);
+void VersionWidget::initBetaBloc(PreferencesBlocWidget *prefBloc) {
+#if defined(__unix__) && !defined(__APPLE__)
+ return; // Beta program is not available on Linux for now
#endif
+
+ auto *betaLayout = prefBloc->addLayout(QBoxLayout::Direction::LeftToRight);
+
+ auto *verticalLayout = new QVBoxLayout(this);
+ verticalLayout->setSpacing(1);
+ verticalLayout->setContentsMargins(0, 0, 0, 0);
+ betaLayout->addLayout(verticalLayout);
+
+ _betaVersionLabel = new QLabel(this);
+ _betaVersionLabel->setObjectName("boldTextLabel");
+ _betaVersionLabel->setWordWrap(true);
+ verticalLayout->addWidget(_betaVersionLabel);
+
+ _betaVersionDescription = new QLabel(this);
+ _betaVersionDescription->setObjectName("description");
+ _betaVersionDescription->setWordWrap(true);
+ _betaVersionDescription->setMinimumWidth(300);
+ verticalLayout->addWidget(_betaVersionDescription);
+
+ betaLayout->addStretch();
+
+ _joinBetaButton = new QPushButton(this);
+ _joinBetaButton->setObjectName("transparentbutton");
+ _joinBetaButton->setFlat(true);
+ betaLayout->addWidget(_joinBetaButton);
}
-void VersionWidget::refreshChannelButtons(const DistributionChannel channel) const {
- switch (channel) {
- case DistributionChannel::Prod: {
- WidgetSignalBlocker _(_prodButton);
- _prodButton->setChecked(true);
- break;
- }
- case DistributionChannel::Beta: {
- WidgetSignalBlocker _(_betaButton);
- _betaButton->setChecked(true);
- break;
- }
- case DistributionChannel::Internal: {
- WidgetSignalBlocker _(_internalButton);
- _internalButton->setChecked(true);
- break;
- }
- default:
- break;
- }
+void VersionWidget::saveDistributionChannel(const DistributionChannel channel) const {
+ GuiRequests::changeDistributionChannel(channel);
+ ParametersCache::instance()->parametersInfo().setDistributionChannel(channel);
+ ParametersCache::instance()->saveParametersInfo();
}
} // namespace KDC
diff --git a/src/gui/versionwidget.h b/src/gui/versionwidget.h
index cfa18b204..26e3e3ba7 100644
--- a/src/gui/versionwidget.h
+++ b/src/gui/versionwidget.h
@@ -35,13 +35,16 @@ class QLabel;
class QBoxLayout;
namespace KDC {
+class TagLabel;
+
+class PreferencesBlocWidget;
class VersionWidget final : public QWidget {
Q_OBJECT
public:
explicit VersionWidget(QWidget *parent = nullptr);
- void refresh(UpdateState state = UpdateState::Unknown) const;
+ void refresh(bool isStaff);
void showAboutDialog();
void showReleaseNotes() const;
@@ -54,22 +57,29 @@ class VersionWidget final : public QWidget {
void onUpdateStateChanged(UpdateState state) const;
private slots:
- void onChannelButtonClicked() const;
void onLinkActivated(const QString &link);
void onUpdateButtonClicked();
+ void onJoinBetaButtonClicked();
private:
- void refreshChannelButtons(DistributionChannel channel) const;
+ void refresh(UpdateState state = UpdateState::Unknown) const;
+ void initVersionInfoBloc(PreferencesBlocWidget *prefBloc);
+ void initBetaBloc(PreferencesBlocWidget *prefBloc);
+ void saveDistributionChannel(DistributionChannel channel) const;
- QRadioButton *_prodButton{nullptr};
- QRadioButton *_betaButton{nullptr};
- QRadioButton *_internalButton{nullptr};
+ bool _isStaff{false};
QLabel *_versionLabel{nullptr};
+
QLabel *_updateStatusLabel{nullptr};
+ TagLabel *_betaTag{nullptr};
QLabel *_showReleaseNotesLabel{nullptr};
QLabel *_versionNumberLabel{nullptr};
QPushButton *_updateButton{nullptr};
+
+ QLabel *_betaVersionLabel{nullptr};
+ QLabel *_betaVersionDescription{nullptr};
+ QPushButton *_joinBetaButton{nullptr};
};
} // namespace KDC
diff --git a/src/libcommon/info/parametersinfo.cpp b/src/libcommon/info/parametersinfo.cpp
index 916157a78..8b01ea79d 100644
--- a/src/libcommon/info/parametersinfo.cpp
+++ b/src/libcommon/info/parametersinfo.cpp
@@ -42,7 +42,7 @@ QDataStream &operator>>(QDataStream &in, ParametersInfo ¶metersInfo) {
parametersInfo._extendedLog >> parametersInfo._purgeOldLogs >> parametersInfo._syncHiddenFiles >>
parametersInfo._useBigFolderSizeLimit >> parametersInfo._bigFolderSizeLimit >> parametersInfo._darkTheme >>
parametersInfo._showShortcuts >> parametersInfo._dialogGeometry >> parametersInfo._maxAllowedCpu >>
- parametersInfo._proxyConfigInfo;
+ parametersInfo._proxyConfigInfo >> parametersInfo._distributionChannel;
return in;
}
@@ -52,7 +52,7 @@ QDataStream &operator<<(QDataStream &out, const ParametersInfo ¶metersInfo)
<< parametersInfo._extendedLog << parametersInfo._purgeOldLogs << parametersInfo._syncHiddenFiles
<< parametersInfo._useBigFolderSizeLimit << parametersInfo._bigFolderSizeLimit << parametersInfo._darkTheme
<< parametersInfo._showShortcuts << parametersInfo._dialogGeometry << parametersInfo._maxAllowedCpu
- << parametersInfo._proxyConfigInfo;
+ << parametersInfo._proxyConfigInfo << parametersInfo._distributionChannel;
return out;
}
diff --git a/src/libcommon/info/parametersinfo.h b/src/libcommon/info/parametersinfo.h
index f41ebf871..9ffadbf4b 100644
--- a/src/libcommon/info/parametersinfo.h
+++ b/src/libcommon/info/parametersinfo.h
@@ -75,6 +75,8 @@ class ParametersInfo {
inline const QMap &dialogGeometry() const { return _dialogGeometry; }
inline int maxAllowedCpu() const { return _maxAllowedCpu; }
inline void setMaxAllowedCpu(int maxAllowedCpu) { _maxAllowedCpu = maxAllowedCpu; }
+ [[nodiscard]] DistributionChannel distributionChannel() const { return _distributionChannel; }
+ void setDistributionChannel(const DistributionChannel channel) { _distributionChannel = channel; }
friend QDataStream &operator>>(QDataStream &in, ParametersInfo ¶metersInfo);
friend QDataStream &operator<<(QDataStream &out, const ParametersInfo ¶metersInfo);
@@ -97,6 +99,7 @@ class ParametersInfo {
bool _showShortcuts;
QMap _dialogGeometry;
int _maxAllowedCpu;
+ DistributionChannel _distributionChannel{DistributionChannel::Prod};
};
} // namespace KDC
diff --git a/src/libcommon/info/userinfo.cpp b/src/libcommon/info/userinfo.cpp
index cbc7103ce..778d6e562 100644
--- a/src/libcommon/info/userinfo.cpp
+++ b/src/libcommon/info/userinfo.cpp
@@ -20,26 +20,29 @@
namespace KDC {
-UserInfo::UserInfo(int dbId, int userId, const QString &name, const QString &email, const QImage &avatar, bool connected) :
+UserInfo::UserInfo(const int dbId, const int userId, const QString &name, const QString &email, const QImage &avatar,
+ const bool connected) :
_dbId(dbId), _userId(userId), _name(name), _email(email), _avatar(avatar), _connected(connected) {}
UserInfo::UserInfo() {}
QDataStream &operator>>(QDataStream &in, UserInfo &userInfo) {
- in >> userInfo._dbId >> userInfo._userId >> userInfo._name >> userInfo._email >> userInfo._avatar >> userInfo._connected;
+ in >> userInfo._dbId >> userInfo._userId >> userInfo._name >> userInfo._email >> userInfo._avatar >> userInfo._connected >>
+ userInfo._isStaff;
return in;
}
QDataStream &operator<<(QDataStream &out, const UserInfo &userInfo) {
- out << userInfo._dbId << userInfo._userId << userInfo._name << userInfo._email << userInfo._avatar << userInfo._connected;
+ out << userInfo._dbId << userInfo._userId << userInfo._name << userInfo._email << userInfo._avatar << userInfo._connected
+ << userInfo._isStaff;
return out;
}
QDataStream &operator<<(QDataStream &out, const QList &list) {
- int count = static_cast(list.size());
+ const auto count = static_cast(list.size());
out << count;
for (int i = 0; i < count; i++) {
- UserInfo userInfo = list[i];
+ const UserInfo &userInfo = list[i];
out << userInfo;
}
return out;
@@ -49,9 +52,9 @@ QDataStream &operator>>(QDataStream &in, QList &list) {
int count = 0;
in >> count;
for (int i = 0; i < count; i++) {
- UserInfo *userInfo = new UserInfo();
- in >> *userInfo;
- list.push_back(*userInfo);
+ UserInfo userInfo;
+ in >> userInfo;
+ list.push_back(userInfo);
}
return in;
}
diff --git a/src/libcommon/info/userinfo.h b/src/libcommon/info/userinfo.h
index 94c122405..b9c8f8a26 100644
--- a/src/libcommon/info/userinfo.h
+++ b/src/libcommon/info/userinfo.h
@@ -44,6 +44,8 @@ class UserInfo {
inline bool connected() const { return _connected; }
inline bool credentialsAsked() const { return _credentialsAsked; }
inline void setCredentialsAsked(bool newCredentialsAsked) { _credentialsAsked = newCredentialsAsked; }
+ [[nodiscard]] bool isStaff() const { return _isStaff; }
+ void setIsStaff(const bool is_staff) { _isStaff = is_staff; }
friend QDataStream &operator>>(QDataStream &in, UserInfo &userInfo);
friend QDataStream &operator<<(QDataStream &out, const UserInfo &userInfo);
@@ -52,13 +54,16 @@ class UserInfo {
friend QDataStream &operator<<(QDataStream &out, const QList &list);
private:
- int _dbId = -1;
- int _userId = -1;
+ int _dbId{-1};
+ int _userId{-1};
QString _name;
QString _email;
QImage _avatar;
- bool _connected = false;
- bool _credentialsAsked = false;
+ bool _connected{false};
+
+ // Non DB attributes
+ bool _credentialsAsked{false};
+ bool _isStaff{false};
};
} // namespace KDC
diff --git a/src/libcommon/log/sentry/abstractcounterscopedptrace.h b/src/libcommon/log/sentry/abstractcounterscopedptrace.h
index b5e81ed2e..f9d6791b5 100644
--- a/src/libcommon/log/sentry/abstractcounterscopedptrace.h
+++ b/src/libcommon/log/sentry/abstractcounterscopedptrace.h
@@ -43,9 +43,7 @@ class AbstractCounterScopedPTrace : public AbstractScopedPTrace {
void stop([[maybe_unused]] PTraceStatus status = PTraceStatus::Ok) final {
assert(false && "stop() should not be called with CounterScopedPTrace.");
}
- void restart() final {
- assert(false && "restart() should not be called with CounterScopedPTrace.");
- }
+ void restart() final { assert(false && "restart() should not be called with CounterScopedPTrace."); }
unsigned int _nbOfCyclesPerTrace = 0; // The number of time start() should be called before stopping the trace.
unsigned int _counter = 0;
};
diff --git a/src/libcommon/log/sentry/abstractptrace.h b/src/libcommon/log/sentry/abstractptrace.h
index f54c65379..d6ad1f47c 100644
--- a/src/libcommon/log/sentry/abstractptrace.h
+++ b/src/libcommon/log/sentry/abstractptrace.h
@@ -34,8 +34,8 @@ class AbstractPTrace {
virtual void restart() { _restart(); }
protected:
- explicit AbstractPTrace(const PTraceDescriptor &info) : _pTraceInfo(info){};
- explicit AbstractPTrace(const PTraceDescriptor &info, int dbId) : _pTraceInfo(info), _syncDbId(dbId){};
+ explicit AbstractPTrace(const PTraceDescriptor &info) : _pTraceInfo(info) {};
+ explicit AbstractPTrace(const PTraceDescriptor &info, int dbId) : _pTraceInfo(info), _syncDbId(dbId) {};
// Start a new performance trace.
inline AbstractPTrace &_start() {
diff --git a/src/libcommon/log/sentry/handler.cpp b/src/libcommon/log/sentry/handler.cpp
index df9f4eef6..cb42268a1 100644
--- a/src/libcommon/log/sentry/handler.cpp
+++ b/src/libcommon/log/sentry/handler.cpp
@@ -448,8 +448,7 @@ Handler::~Handler() {
Handler::SentryEvent::SentryEvent(const std::string &title, const std::string &message, Level level,
sentry::ConfidentialityLevel confidentialityLevel, const SentryUser &user) :
- title(title),
- message(message), level(level), confidentialityLevel(confidentialityLevel), userId(user.userId()) {}
+ title(title), message(message), level(level), confidentialityLevel(confidentialityLevel), userId(user.userId()) {}
void Handler::stopPTrace(const pTraceId &id, PTraceStatus status) {
if (id == 0) return;
@@ -489,7 +488,7 @@ pTraceId Handler::makeUniquePTraceId() {
bool reseted = false;
do {
_pTraceIdCounter++;
- if (_pTraceIdCounter >= std::numeric_limits::max()-1) {
+ if (_pTraceIdCounter >= std::numeric_limits::max() - 1) {
if (reseted) {
assert(false && "No more unique pTraceId available");
return 0;
diff --git a/src/libcommon/log/sentry/ptracedescriptor.h b/src/libcommon/log/sentry/ptracedescriptor.h
index d81597461..5d4df148f 100644
--- a/src/libcommon/log/sentry/ptracedescriptor.h
+++ b/src/libcommon/log/sentry/ptracedescriptor.h
@@ -68,9 +68,8 @@ enum class PTraceName {
struct PTraceDescriptor {
PTraceDescriptor() = default;
PTraceDescriptor(std::string pTraceTitle, std::string pTraceDescription, PTraceName pTraceName,
- PTraceName parentPTraceName = PTraceName::None) :
- _pTraceName{pTraceName},
- _parentPTraceName{parentPTraceName}, _pTraceTitle{std::move(pTraceTitle)},
+ PTraceName parentPTraceName = PTraceName::None) :
+ _pTraceName{pTraceName}, _parentPTraceName{parentPTraceName}, _pTraceTitle{std::move(pTraceTitle)},
_pTraceDescription{std::move(pTraceDescription)} {}
const PTraceName _pTraceName = PTraceName::None;
diff --git a/src/libcommon/log/sentry/ptraces.h b/src/libcommon/log/sentry/ptraces.h
index 0ab6ee189..b7a3f7d90 100644
--- a/src/libcommon/log/sentry/ptraces.h
+++ b/src/libcommon/log/sentry/ptraces.h
@@ -24,14 +24,11 @@
namespace KDC::sentry::pTraces {
struct None : public AbstractPTrace {
- None() : AbstractPTrace({}){};
+ None() : AbstractPTrace({}) {};
explicit None(int syncdbId) : AbstractPTrace({}, syncdbId) {}
- void start() final { /* Do nothing */
- }
- void stop([[maybe_unused]] PTraceStatus status = PTraceStatus::Ok) final { /* Do nothing */
- }
- void restart() final { /* Do nothing */
- }
+ void start() final { /* Do nothing */ }
+ void stop([[maybe_unused]] PTraceStatus status = PTraceStatus::Ok) final { /* Do nothing */ }
+ void restart() final { /* Do nothing */ }
};
/*
@@ -52,7 +49,7 @@ struct AppStart : public AbstractPTrace {
struct Sync : public AbstractPTrace {
[[nodiscard]] explicit Sync(int dbId) :
- AbstractPTrace({"Synchronisation", "Synchronisation initialization", PTraceName::Sync}, dbId){};
+ AbstractPTrace({"Synchronisation", "Synchronisation initialization", PTraceName::Sync}, dbId) {};
};
struct UpdateDetection1 : public AbstractPTrace {
@@ -68,7 +65,7 @@ struct UpdateDetection2 : public AbstractPTrace {
struct Reconciliation1 : public AbstractPTrace {
[[nodiscard]] explicit Reconciliation1(int dbId) :
AbstractPTrace({"Reconciliation1", "Platform inconsistency check", PTraceName::Reconciliation1, PTraceName::Sync},
- dbId){};
+ dbId) {};
};
struct Reconciliation2 : public AbstractPTrace {
@@ -126,7 +123,7 @@ struct RFSOChangeDetected : public AbstractScopedPTrace {
struct RFSOGenerateInitialSnapshot : public AbstractScopedPTrace {
explicit RFSOGenerateInitialSnapshot(int syncDbId) :
AbstractScopedPTrace({"RFSO_GenerateInitialSnapshot", "Generate snapshot", PTraceName::RFSOGenerateInitialSnapshot},
- PTraceStatus::Aborted, syncDbId){};
+ PTraceStatus::Aborted, syncDbId) {};
};
// This scoped performance trace expects to be manually stopped.
diff --git a/src/libcommon/log/sentry/utility.h b/src/libcommon/log/sentry/utility.h
index 62cb39816..210918a4a 100644
--- a/src/libcommon/log/sentry/utility.h
+++ b/src/libcommon/log/sentry/utility.h
@@ -50,4 +50,4 @@ static inline std::unique_ptr syncStepToPTrace(SyncStep step, in
}
return std::make_unique(syncDbId);
}
-} // namespace KDC
+} // namespace KDC::sentry
diff --git a/src/libcommon/theme/theme.cpp b/src/libcommon/theme/theme.cpp
index 75f9bebe8..e89a1411d 100644
--- a/src/libcommon/theme/theme.cpp
+++ b/src/libcommon/theme/theme.cpp
@@ -140,6 +140,22 @@ QString Theme::helpUrl() const {
#endif
}
+QString Theme::feedbackUrl(const Language language) const {
+ switch (language) {
+ case Language::French:
+ return FEEDBACK_FR_URL;
+ case Language::German:
+ return FEEDBACK_DE_URL;
+ case Language::Spanish:
+ return FEEDBACK_ES_URL;
+ case Language::Italian:
+ return FEEDBACK_IT_URL;
+ default:
+ break;
+ }
+ return FEEDBACK_EN_URL;
+}
+
QString Theme::conflictHelpUrl() const {
#ifdef APPLICATION_CONFLICT_HELP_URL
return QString::fromLatin1(APPLICATION_CONFLICT_HELP_URL);
diff --git a/src/libcommon/theme/theme.h b/src/libcommon/theme/theme.h
index d1543b5b0..53ac6925d 100644
--- a/src/libcommon/theme/theme.h
+++ b/src/libcommon/theme/theme.h
@@ -46,6 +46,7 @@ class Theme : public QObject {
virtual QString version() const;
virtual QString helpUrl() const;
+ QString feedbackUrl(Language language) const;
virtual QString conflictHelpUrl() const;
diff --git a/src/libcommon/utility/types.h b/src/libcommon/utility/types.h
index 48e8a73f8..7f16bf1d9 100644
--- a/src/libcommon/utility/types.h
+++ b/src/libcommon/utility/types.h
@@ -529,6 +529,8 @@ struct VersionInfo {
return out;
}
};
+using AllVersionsInfo = std::unordered_map;
+
namespace sentry {
enum class ConfidentialityLevel {
Anonymous, // The sentry will not be able to identify the user (no ip, no email, no username, ...)
diff --git a/src/libcommon/utility/utility_win.cpp b/src/libcommon/utility/utility_win.cpp
index ce289a81b..7f030de65 100644
--- a/src/libcommon/utility/utility_win.cpp
+++ b/src/libcommon/utility/utility_win.cpp
@@ -48,7 +48,7 @@ static SyncPath getAppSupportDir_private() {
return appDataPath;
}
sentry::Handler::captureMessage(sentry::Level::Warning, "Utility_win::getAppSupportDir_private",
- "Fail to get AppSupportDir through SHGetKnownFolderPath, using fallback method");
+ "Fail to get AppSupportDir through SHGetKnownFolderPath, using fallback method");
return std::filesystem::temp_directory_path().parent_path().parent_path().native();
}
diff --git a/src/libcommonserver/commserver.cpp b/src/libcommonserver/commserver.cpp
index c30a21dc7..a0919c6df 100644
--- a/src/libcommonserver/commserver.cpp
+++ b/src/libcommonserver/commserver.cpp
@@ -90,6 +90,11 @@ void CommServer::sendReply(int id, const QByteArray &result) {
}
}
+bool CommServer::sendSignal(const SignalNum num, const QByteArray ¶ms) {
+ int id = 0;
+ return sendSignal(num, params, id);
+}
+
bool CommServer::sendSignal(SignalNum num, const QByteArray ¶ms, int &id) {
if (_tcpSocket && _tcpSocket->isOpen()) {
_requestWorker->addSignal(num, params, id);
diff --git a/src/libcommonserver/commserver.h b/src/libcommonserver/commserver.h
index fff7b3e23..d24dc5704 100644
--- a/src/libcommonserver/commserver.h
+++ b/src/libcommonserver/commserver.h
@@ -45,6 +45,7 @@ class CommServer : public QObject {
void operator=(CommServer const &) = delete;
void sendReply(int id, const QByteArray &result);
+ bool sendSignal(SignalNum num, const QByteArray ¶ms);
bool sendSignal(SignalNum num, const QByteArray ¶ms, int &id);
inline quint16 commPort() const { return _tcpServer.serverPort(); }
diff --git a/src/libcommonserver/db/db.cpp b/src/libcommonserver/db/db.cpp
index 7bf5145f3..6fe5cbea3 100644
--- a/src/libcommonserver/db/db.cpp
+++ b/src/libcommonserver/db/db.cpp
@@ -339,36 +339,32 @@ bool Db::init(const std::string &version) {
queryFree(SELECT_VERSION_REQUEST_ID);
- if (_fromVersion != version) {
- // Upgrade DB
- LOG_INFO(_logger,
- "Upgrade " << dbType().c_str() << " DB from " << _fromVersion.c_str() << " to " << version.c_str());
- if (!upgrade(_fromVersion, version)) {
- LOG_WARN(_logger, "Error in Db::upgrade");
- return false;
- }
-
- // Update version
- if (!createAndPrepareRequest(UPDATE_VERSION_REQUEST_ID, UPDATE_VERSION_REQUEST)) return false;
- if (!updateVersion(version, found)) {
- LOG_WARN(_logger, "Error in Db::updateVersion");
- return false;
- }
- if (!found) {
- LOG_WARN(_logger, "Version not found");
- return false;
- }
+ // Upgrade DB
+ LOG_INFO(_logger, "Upgrade " << dbType() << " DB from " << _fromVersion << " to " << version);
+ if (!upgrade(_fromVersion, version)) {
+ LOG_WARN(_logger, "Error in Db::upgrade");
+ return false;
+ }
- queryFree(UPDATE_VERSION_REQUEST_ID);
+ // Update version
+ if (!createAndPrepareRequest(UPDATE_VERSION_REQUEST_ID, UPDATE_VERSION_REQUEST)) return false;
+ if (!updateVersion(version, found)) {
+ LOG_WARN(_logger, "Error in Db::updateVersion");
+ return false;
}
+ if (!found) {
+ LOG_WARN(_logger, "Version not found");
+ return false;
+ }
+
+ queryFree(UPDATE_VERSION_REQUEST_ID);
} else {
// Create version table
LOG_DEBUG(_logger, "Create version table");
if (!createAndPrepareRequest(CREATE_VERSION_TABLE_ID, CREATE_VERSION_TABLE)) return false;
int errId = -1;
- std::string error;
- if (!queryExec(CREATE_VERSION_TABLE_ID, errId, error)) {
+ if (std::string error; !queryExec(CREATE_VERSION_TABLE_ID, errId, error)) {
queryFree(CREATE_VERSION_TABLE_ID);
return sqlFail(CREATE_VERSION_TABLE_ID, error);
}
diff --git a/src/libcommonserver/utility/utility_win.cpp b/src/libcommonserver/utility/utility_win.cpp
index 3fba6fb6b..148a5ef64 100644
--- a/src/libcommonserver/utility/utility_win.cpp
+++ b/src/libcommonserver/utility/utility_win.cpp
@@ -110,8 +110,7 @@ static bool moveItemToTrash_private(const SyncPath &itemPath) {
std::wstring errorStr = errorStream.str();
LOGW_WARN(Log::instance()->getLogger(), errorStr.c_str());
- sentry::Handler::captureMessage(sentry::Level::Error, "Utility::moveItemToTrash",
- "SHCreateItemFromParsingName failed");
+ sentry::Handler::captureMessage(sentry::Level::Error, "Utility::moveItemToTrash", "SHCreateItemFromParsingName failed");
fileOperation->Release();
CoUninitialize();
return false;
diff --git a/src/libparms/db/parmsdb.cpp b/src/libparms/db/parmsdb.cpp
index 3fe4360f4..4e3f68b02 100644
--- a/src/libparms/db/parmsdb.cpp
+++ b/src/libparms/db/parmsdb.cpp
@@ -60,7 +60,9 @@
"extendedLog INTEGER," \
"maxAllowedCpu INTEGER," \
"uploadSessionParallelJobs INTEGER," \
- "jobPoolCapacityFactor INTEGER);"
+ "jobPoolCapacityFactor INTEGER," \
+ "distributionChannel INTEGER" \
+ ");"
#define INSERT_PARAMETERS_REQUEST_ID "insert_parameters"
#define INSERT_PARAMETERS_REQUEST \
@@ -68,9 +70,9 @@
"syncHiddenFiles, proxyType, proxyHostName, proxyPort, proxyNeedsAuth, proxyUser, proxyToken, useBigFolderSizeLimit, " \
"bigFolderSizeLimit, darkTheme, showShortcuts, updateFileAvailable, updateTargetVersion, updateTargetVersionString, " \
"autoUpdateAttempted, seenVersion, dialogGeometry, extendedLog, maxAllowedCpu, uploadSessionParallelJobs, " \
- "jobPoolCapacityFactor) " \
+ "jobPoolCapacityFactor, distributionChannel) " \
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, " \
- "?25, ?26, ?27, ?28, ?29);"
+ "?25, ?26, ?27, ?28, ?29, ?30);"
#define UPDATE_PARAMETERS_REQUEST_ID "update_parameters"
#define UPDATE_PARAMETERS_REQUEST \
@@ -81,7 +83,7 @@
"bigFolderSizeLimit=?17, darkTheme=?18, showShortcuts=?19, updateFileAvailable=?20, updateTargetVersion=?21, " \
"updateTargetVersionString=?22, " \
"autoUpdateAttempted=?23, seenVersion=?24, dialogGeometry=?25, extendedLog=?26, maxAllowedCpu=?27, " \
- "uploadSessionParallelJobs=?28, jobPoolCapacityFactor=?29;"
+ "uploadSessionParallelJobs=?28, jobPoolCapacityFactor=?29, distributionChannel=?30;"
#define SELECT_PARAMETERS_REQUEST_ID "select_parameters"
#define SELECT_PARAMETERS_REQUEST \
@@ -89,15 +91,14 @@
"syncHiddenFiles, proxyType, proxyHostName, proxyPort, proxyNeedsAuth, proxyUser, proxyToken, useBigFolderSizeLimit, " \
"bigFolderSizeLimit, darkTheme, showShortcuts, updateFileAvailable, updateTargetVersion, updateTargetVersionString, " \
"autoUpdateAttempted, seenVersion, dialogGeometry, extendedLog, maxAllowedCpu, uploadSessionParallelJobs, " \
- "jobPoolCapacityFactor " \
+ "jobPoolCapacityFactor, distributionChannel " \
"FROM parameters;"
#define UPDATE_PARAMETERS_JOB_REQUEST_ID "update_parameters_job"
#define UPDATE_PARAMETERS_JOB_REQUEST "UPDATE parameters SET uploadSessionParallelJobs=?1, jobPoolCapacityFactor=?2;"
-// TODO : will be added later
-// #define ALTER_PARAMETERS_ADD_DISTRIBUTION_CHANNEL_REQUEST_ID "alter_parameters_add_distribution"
-// #define ALTER_PARAMETERS_ADD_DISTRIBUTION_CHANNEL_REQUEST "ALTER TABLE parameters ADD COLUMN distributionChannel INTEGER;"
+#define ALTER_PARAMETERS_ADD_DISTRIBUTION_CHANNEL_REQUEST_ID "alter_parameters_add_distribution"
+#define ALTER_PARAMETERS_ADD_DISTRIBUTION_CHANNEL_REQUEST "ALTER TABLE parameters ADD COLUMN distributionChannel INTEGER;"
//
// user
@@ -601,7 +602,7 @@ bool ParmsDb::insertDefaultParameters() {
ASSERT(queryBindValue(INSERT_PARAMETERS_REQUEST_ID, 27, parameters.maxAllowedCpu()));
ASSERT(queryBindValue(INSERT_PARAMETERS_REQUEST_ID, 28, parameters.uploadSessionParallelJobs()));
ASSERT(queryBindValue(INSERT_PARAMETERS_REQUEST_ID, 29, parameters.jobPoolCapacityFactor()));
- // ASSERT(queryBindValue(INSERT_PARAMETERS_REQUEST_ID, 30, static_cast(parameters.distributionChannel())));
+ ASSERT(queryBindValue(INSERT_PARAMETERS_REQUEST_ID, 30, static_cast(parameters.distributionChannel())));
if (!queryExec(INSERT_PARAMETERS_REQUEST_ID, errId, error)) {
LOG_WARN(_logger, "Error running query: " << INSERT_PARAMETERS_REQUEST_ID);
@@ -1006,6 +1007,11 @@ bool ParmsDb::upgrade(const std::string & /*fromVersion*/, const std::string & /
queryFree(UPDATE_PARAMETERS_JOB_REQUEST_ID);
}
+ columnName = "distributionChannel";
+ if (!addIntegerColumnIfMissing(tableName, columnName)) {
+ return false;
+ }
+
bool exist = false;
if (!tableExists("app_state", exist)) return false;
if (!exist) {
@@ -1094,7 +1100,7 @@ bool ParmsDb::updateParameters(const Parameters ¶meters, bool &found) {
ASSERT(queryBindValue(UPDATE_PARAMETERS_REQUEST_ID, 27, parameters.maxAllowedCpu()));
ASSERT(queryBindValue(UPDATE_PARAMETERS_REQUEST_ID, 28, parameters.uploadSessionParallelJobs()));
ASSERT(queryBindValue(UPDATE_PARAMETERS_REQUEST_ID, 29, parameters.jobPoolCapacityFactor()));
- // ASSERT(queryBindValue(UPDATE_PARAMETERS_REQUEST_ID, 30, static_cast(parameters.distributionChannel())));
+ ASSERT(queryBindValue(UPDATE_PARAMETERS_REQUEST_ID, 30, static_cast(parameters.distributionChannel())));
if (!queryExec(UPDATE_PARAMETERS_REQUEST_ID, errId, error)) {
LOG_WARN(_logger, "Error running query: " << UPDATE_PARAMETERS_REQUEST_ID);
@@ -1213,9 +1219,8 @@ bool ParmsDb::selectParameters(Parameters ¶meters, bool &found) {
ASSERT(queryIntValue(SELECT_PARAMETERS_REQUEST_ID, 28, intResult));
parameters.setJobPoolCapacityFactor(intResult);
- // TODO : not supported for now
- // ASSERT(queryIntValue(SELECT_PARAMETERS_REQUEST_ID, 29, intResult));
- // parameters.setDistributionChannel(static_cast(intResult));
+ ASSERT(queryIntValue(SELECT_PARAMETERS_REQUEST_ID, 29, intResult));
+ parameters.setDistributionChannel(static_cast(intResult));
ASSERT(queryResetAndClearBindings(SELECT_PARAMETERS_REQUEST_ID));
diff --git a/src/libparms/db/user.h b/src/libparms/db/user.h
index 12e462e1c..608016986 100644
--- a/src/libparms/db/user.h
+++ b/src/libparms/db/user.h
@@ -51,6 +51,8 @@ class PARMS_EXPORT User {
inline void setAvatar(std::shared_ptr> avatar) { _avatar = avatar; }
inline void setToMigrate(bool toMigrate) { _toMigrate = toMigrate; }
inline int toMigrate() const { return _toMigrate; }
+ [[nodiscard]] bool isStaff() const { return _isStaff; }
+ void setIsStaff(const bool isStaff) { _isStaff = isStaff; }
private:
log4cplus::Logger _logger;
@@ -62,6 +64,9 @@ class PARMS_EXPORT User {
std::string _avatarUrl;
std::shared_ptr> _avatar;
bool _toMigrate;
+
+ // Non DB attributes
+ bool _isStaff{false};
};
} // namespace KDC
diff --git a/src/libsyncengine/jobs/jobmanager.cpp b/src/libsyncengine/jobs/jobmanager.cpp
index 51513b47c..671469330 100644
--- a/src/libsyncengine/jobs/jobmanager.cpp
+++ b/src/libsyncengine/jobs/jobmanager.cpp
@@ -120,7 +120,7 @@ void JobManager::decreasePoolCapacity() {
LOG_DEBUG(Log::instance()->getLogger(), "Job Manager capacity set to " << _maxNbThread);
} else {
sentry::Handler::captureMessage(sentry::Level::Warning, "JobManager::defaultCallback",
- "JobManager capacity cannot be decreased");
+ "JobManager capacity cannot be decreased");
}
}
diff --git a/src/libsyncengine/jobs/jobmanager.h b/src/libsyncengine/jobs/jobmanager.h
index b7dc0d6f9..225f4fda8 100644
--- a/src/libsyncengine/jobs/jobmanager.h
+++ b/src/libsyncengine/jobs/jobmanager.h
@@ -40,7 +40,7 @@ namespace KDC {
class JobPriorityCmp {
public:
bool operator()(const std::pair, Poco::Thread::Priority> &j1,
- const std::pair, Poco::Thread::Priority> &j2) {
+ const std::pair, Poco::Thread::Priority> &j2) const {
if (j1.second == j2.second) {
// Same thread priority, use the job ID to define priority
return j1.first->jobId() > j2.first->jobId();
diff --git a/src/libsyncengine/jobs/local/localdeletejob.cpp b/src/libsyncengine/jobs/local/localdeletejob.cpp
index 5f14039d8..72b631a3a 100644
--- a/src/libsyncengine/jobs/local/localdeletejob.cpp
+++ b/src/libsyncengine/jobs/local/localdeletejob.cpp
@@ -32,7 +32,7 @@
namespace KDC {
-LocalDeleteJob::Path::Path(const SyncPath &path) : _path(path){};
+LocalDeleteJob::Path::Path(const SyncPath &path) : _path(path) {};
bool LocalDeleteJob::Path::endsWith(SyncPath &&ending) const {
if (!_path.empty() && ending.empty()) return false;
@@ -58,9 +58,8 @@ bool LocalDeleteJob::matchRelativePaths(const SyncPath &targetPath, const SyncPa
LocalDeleteJob::LocalDeleteJob(const SyncPalInfo &syncPalInfo, const SyncPath &relativePath, bool isDehydratedPlaceholder,
NodeId remoteId, bool forceToTrash /* = false */) :
- _absolutePath(syncPalInfo.localPath / relativePath),
- _syncInfo(syncPalInfo), _relativePath(relativePath), _isDehydratedPlaceholder(isDehydratedPlaceholder),
- _remoteNodeId(remoteId), _forceToTrash(forceToTrash) {}
+ _absolutePath(syncPalInfo.localPath / relativePath), _syncInfo(syncPalInfo), _relativePath(relativePath),
+ _isDehydratedPlaceholder(isDehydratedPlaceholder), _remoteNodeId(remoteId), _forceToTrash(forceToTrash) {}
LocalDeleteJob::LocalDeleteJob(const SyncPath &absolutePath) : _absolutePath(absolutePath) {
setBypassCheck(true);
diff --git a/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp b/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp
index 16dba22cd..e6b3fc052 100644
--- a/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp
+++ b/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp
@@ -43,9 +43,8 @@ namespace KDC {
DownloadJob::DownloadJob(int driveDbId, const NodeId &remoteFileId, const SyncPath &localpath, int64_t expectedSize,
SyncTime creationTime, SyncTime modtime, bool isCreate) :
- AbstractTokenNetworkJob(ApiType::Drive, 0, 0, driveDbId, 0, false),
- _remoteFileId(remoteFileId), _localpath(localpath), _expectedSize(expectedSize), _creationTime(creationTime),
- _modtimeIn(modtime), _isCreate(isCreate) {
+ AbstractTokenNetworkJob(ApiType::Drive, 0, 0, driveDbId, 0, false), _remoteFileId(remoteFileId), _localpath(localpath),
+ _expectedSize(expectedSize), _creationTime(creationTime), _modtimeIn(modtime), _isCreate(isCreate) {
_httpMethod = Poco::Net::HTTPRequest::HTTP_GET;
_customTimeout = 60;
_trials = TRIALS;
@@ -118,8 +117,8 @@ bool DownloadJob::canRun() {
}
if (_isCreate && exists) {
- LOGW_DEBUG(_logger,
- L"Item with " << Utility::formatSyncPath(_localpath) << L" already exists. Aborting current sync and restarting.");
+ LOGW_DEBUG(_logger, L"Item with " << Utility::formatSyncPath(_localpath)
+ << L" already exists. Aborting current sync and restarting.");
_exitCode = ExitCode::NeedRestart;
_exitCause = ExitCause::UnexpectedFileSystemEvent;
return false;
@@ -399,8 +398,7 @@ bool DownloadJob::handleResponse(std::istream &is) {
std::make_optional(_modtimeIn), isLink, exists)) {
LOGW_WARN(_logger, L"Error in Utility::setFileDates: " << Utility::formatSyncPath(_localpath));
// Do nothing (remote file will be updated during the next sync)
- sentry::Handler::captureMessage(sentry::Level::Warning, "DownloadJob::handleResponse",
- "Unable to set file dates");
+ sentry::Handler::captureMessage(sentry::Level::Warning, "DownloadJob::handleResponse", "Unable to set file dates");
} else if (!exists) {
LOGW_INFO(_logger, L"Item does not exist anymore. Restarting sync: " << Utility::formatSyncPath(_localpath));
_exitCode = ExitCode::DataError;
@@ -450,9 +448,9 @@ bool DownloadJob::createLink(const std::string &mimeType, const std::string &dat
return false;
}
- LOGW_DEBUG(_logger,
- L"Create symlink with target " << Utility::formatSyncPath(targetPath) << L", " << Utility::formatSyncPath(_localpath));
-
+ LOGW_DEBUG(_logger, L"Create symlink with target " << Utility::formatSyncPath(targetPath) << L", "
+ << Utility::formatSyncPath(_localpath));
+
bool isFolder = mimeType == mimeTypeSymlinkFolder;
IoError ioError = IoError::Success;
if (!IoHelper::createSymlink(targetPath, _localpath, isFolder, ioError)) {
diff --git a/src/libsyncengine/jobs/network/API_v2/getinfouserjob.cpp b/src/libsyncengine/jobs/network/API_v2/getinfouserjob.cpp
index 9b106635f..955696e2f 100644
--- a/src/libsyncengine/jobs/network/API_v2/getinfouserjob.cpp
+++ b/src/libsyncengine/jobs/network/API_v2/getinfouserjob.cpp
@@ -18,12 +18,45 @@
#include "getinfouserjob.h"
+#include "utility/jsonparserutility.h"
+
namespace KDC {
-GetInfoUserJob::GetInfoUserJob(int userDbId) : AbstractTokenNetworkJob(ApiType::Profile, userDbId, 0, 0, 0) {
+static const std::string displayNameKey = "display_name";
+static const std::string emailKey = "email";
+static const std::string avatarKey = "avatar";
+static const std::string isStaffKey = "is_staff";
+
+GetInfoUserJob::GetInfoUserJob(const int userDbId) : AbstractTokenNetworkJob(ApiType::Profile, userDbId, 0, 0, 0) {
_httpMethod = Poco::Net::HTTPRequest::HTTP_GET;
}
+bool GetInfoUserJob::handleJsonResponse(std::istream& is) {
+ if (!AbstractTokenNetworkJob::handleJsonResponse(is)) return false;
+
+ Poco::JSON::Object::Ptr dataObj = jsonRes()->getObject(dataKey);
+ if (!dataObj || dataObj->size() == 0) return false;
+
+
+ if (!JsonParserUtility::extractValue(dataObj, displayNameKey, _name)) {
+ _exitCode = ExitCode::BackError;
+ return false;
+ }
+
+ if (!JsonParserUtility::extractValue(dataObj, emailKey, _email)) {
+ _exitCode = ExitCode::BackError;
+ return false;
+ }
+
+ if (!JsonParserUtility::extractValue(dataObj, avatarKey, _avatarUrl)) {
+ _exitCode = ExitCode::BackError;
+ return false;
+ }
+
+ JsonParserUtility::extractValue(dataObj, isStaffKey, _isStaff, false);
+ return true;
+}
+
std::string GetInfoUserJob::getSpecificUrl() {
std::string str = AbstractTokenNetworkJob::getSpecificUrl();
return str;
diff --git a/src/libsyncengine/jobs/network/API_v2/getinfouserjob.h b/src/libsyncengine/jobs/network/API_v2/getinfouserjob.h
index d720c9a12..b2ea5c209 100644
--- a/src/libsyncengine/jobs/network/API_v2/getinfouserjob.h
+++ b/src/libsyncengine/jobs/network/API_v2/getinfouserjob.h
@@ -22,14 +22,27 @@
namespace KDC {
-class GetInfoUserJob : public AbstractTokenNetworkJob {
+class GetInfoUserJob final : public AbstractTokenNetworkJob {
public:
- GetInfoUserJob(int userDbId);
+ explicit GetInfoUserJob(int userDbId);
+
+ [[nodiscard]] const std::string& name() const { return _name; }
+ [[nodiscard]] const std::string& email() const { return _email; }
+ [[nodiscard]] const std::string& avatarUrl() const { return _avatarUrl; }
+ [[nodiscard]] bool isStaff() const { return _isStaff; }
+
+ protected:
+ bool handleJsonResponse(std::istream& is) override;
private:
- virtual std::string getSpecificUrl() override;
- virtual void setQueryParameters(Poco::URI &, bool &canceled) override { canceled = false; }
- inline virtual ExitInfo setData() override { return ExitCode::Ok; }
+ std::string getSpecificUrl() override;
+ void setQueryParameters(Poco::URI&, bool& canceled) override { canceled = false; }
+ ExitInfo setData() override { return ExitCode::Ok; }
+
+ std::string _name;
+ std::string _email;
+ std::string _avatarUrl;
+ bool _isStaff{false};
};
} // namespace KDC
diff --git a/src/libsyncengine/jobs/network/getappversionjob.cpp b/src/libsyncengine/jobs/network/getappversionjob.cpp
index b1d411fbe..974a2eae5 100644
--- a/src/libsyncengine/jobs/network/getappversionjob.cpp
+++ b/src/libsyncengine/jobs/network/getappversionjob.cpp
@@ -20,8 +20,6 @@
#include "utility/jsonparserutility.h"
#include "utility/utility.h"
-#include
-
namespace KDC {
static const std::string prodVersionKey = "prod_version";
@@ -47,7 +45,7 @@ GetAppVersionJob::GetAppVersionJob(const Platform platform, const std::string &a
_httpMethod = Poco::Net::HTTPRequest::HTTP_GET;
}
-std::string toStr(const Platform platform) {
+std::string GetAppVersionJob::toStr(const Platform platform) {
switch (platform) {
case Platform::MacOS:
return platformMacOsKey;
@@ -70,6 +68,21 @@ DistributionChannel toDistributionChannel(const std::string &str) {
return DistributionChannel::Unknown;
}
+std::string GetAppVersionJob::toStr(const DistributionChannel channel) {
+ switch (channel) {
+ case DistributionChannel::Prod:
+ return versionTypeProdKey;
+ case DistributionChannel::Next:
+ return versionTypeNextKey;
+ case DistributionChannel::Beta:
+ return versionTypeBetaKey;
+ case DistributionChannel::Internal:
+ return versionTypeInternalKey;
+ default:
+ return "unknown";
+ }
+}
+
std::string GetAppVersionJob::getSpecificUrl() {
std::stringstream ss;
ss << "/app-information/kstore-update/" << toStr(_platform) << "/com.infomaniak.drive/" << _appId;
@@ -123,15 +136,15 @@ bool GetAppVersionJob::handleResponse(std::istream &is) {
if (!JsonParserUtility::extractValue(obj, typeKey, versionType)) return false;
const DistributionChannel channel = toDistributionChannel(versionType);
- _versionInfo[channel].channel = channel;
+ _versionsInfo[channel].channel = channel;
- if (!JsonParserUtility::extractValue(obj, tagKey, _versionInfo[channel].tag)) return false;
- if (!JsonParserUtility::extractValue(obj, buildVersionKey, _versionInfo[channel].buildVersion)) return false;
- if (!JsonParserUtility::extractValue(obj, buildMinOsVersionKey, _versionInfo[channel].buildMinOsVersion, false))
+ if (!JsonParserUtility::extractValue(obj, tagKey, _versionsInfo[channel].tag)) return false;
+ if (!JsonParserUtility::extractValue(obj, buildVersionKey, _versionsInfo[channel].buildVersion)) return false;
+ if (!JsonParserUtility::extractValue(obj, buildMinOsVersionKey, _versionsInfo[channel].buildMinOsVersion, false))
return false;
- if (!JsonParserUtility::extractValue(obj, downloadUrlKey, _versionInfo[channel].downloadUrl)) return false;
+ if (!JsonParserUtility::extractValue(obj, downloadUrlKey, _versionsInfo[channel].downloadUrl)) return false;
- if (!_versionInfo[channel].isValid()) {
+ if (!_versionsInfo[channel].isValid()) {
LOG_WARN(_logger, "Missing mandatory value.");
return false;
}
diff --git a/src/libsyncengine/jobs/network/getappversionjob.h b/src/libsyncengine/jobs/network/getappversionjob.h
index 5b86e0f84..ab0e12cc6 100644
--- a/src/libsyncengine/jobs/network/getappversionjob.h
+++ b/src/libsyncengine/jobs/network/getappversionjob.h
@@ -30,15 +30,19 @@ class GetAppVersionJob : public AbstractNetworkJob {
GetAppVersionJob(Platform platform, const std::string &appID, const std::vector &userIdList);
~GetAppVersionJob() override = default;
- const VersionInfo &getVersionInfo(const DistributionChannel channel) {
- return _versionInfo.contains(channel) ? _versionInfo[channel] : _defaultVersionInfo;
- }
- const VersionInfo &getProdVersionInfo() {
- return _versionInfo.contains(_prodVersionChannel) ? _versionInfo[_prodVersionChannel] : _defaultVersionInfo;
- }
+ /**
+ * @brief Return the adequat version info between Production or Production-Next.
+ * @return `DistributionChannel` enum value.
+ */
+ [[nodiscard]] DistributionChannel prodVersionChannel() const { return _prodVersionChannel; }
+ [[nodiscard]] const VersionInfo &versionInfo(const DistributionChannel channel) { return _versionsInfo[channel]; }
+ [[nodiscard]] const AllVersionsInfo &versionsInfo() const { return _versionsInfo; }
std::string getUrl() override { return INFOMANIAK_API_URL + getSpecificUrl(); }
+ static std::string toStr(Platform platform);
+ static std::string toStr(DistributionChannel channel);
+
protected:
bool handleResponse(std::istream &is) override;
@@ -54,10 +58,8 @@ class GetAppVersionJob : public AbstractNetworkJob {
const Platform _platform{Platform::Unknown};
const std::string _appId;
const std::vector _userIdList;
-
- const VersionInfo _defaultVersionInfo;
DistributionChannel _prodVersionChannel{DistributionChannel::Unknown};
- std::unordered_map _versionInfo;
+ AllVersionsInfo _versionsInfo;
};
} // namespace KDC
diff --git a/src/libsyncengine/jobs/network/networkjobsparams.h b/src/libsyncengine/jobs/network/networkjobsparams.h
index 5a63275f0..c5a56004c 100644
--- a/src/libsyncengine/jobs/network/networkjobsparams.h
+++ b/src/libsyncengine/jobs/network/networkjobsparams.h
@@ -83,9 +83,6 @@ static const std::string filesKey = "files";
static const std::string idKey = "id";
static const std::string parentIdKey = "parent_id";
static const std::string nameKey = "name";
-static const std::string emailKey = "email";
-static const std::string avatarKey = "avatar";
-static const std::string displayNameKey = "display_name";
static const std::string typeKey = "type";
static const std::string sizeKey = "size";
static const std::string visibilityKey = "visibility";
diff --git a/src/libsyncengine/requests/parameterscache.cpp b/src/libsyncengine/requests/parameterscache.cpp
index 7b9294a71..b6f634afb 100644
--- a/src/libsyncengine/requests/parameterscache.cpp
+++ b/src/libsyncengine/requests/parameterscache.cpp
@@ -112,7 +112,7 @@ void ParametersCache::decreaseUploadSessionParallelThreads() {
"Upload session max parallel threads parameters set to " << newUploadSessionParallelJobs);
} else {
sentry::Handler::captureMessage(sentry::Level::Warning, "AppServer::addError",
- "Upload session max parallel threads parameters cannot be decreased");
+ "Upload session max parallel threads parameters cannot be decreased");
}
}
diff --git a/src/libsyncengine/requests/serverrequests.cpp b/src/libsyncengine/requests/serverrequests.cpp
index dc41c1852..315ef08de 100644
--- a/src/libsyncengine/requests/serverrequests.cpp
+++ b/src/libsyncengine/requests/serverrequests.cpp
@@ -787,33 +787,32 @@ ExitCode ServerRequests::createUser(const User &user, UserInfo &userInfo) {
}
// Load User info
- User userUpdated(user);
- bool updated;
- ExitCode exitCode = loadUserInfo(userUpdated, updated);
- if (exitCode != ExitCode::Ok) {
+ User updatedUser(user);
+ bool updated = false;
+ if (ExitCode exitCode = loadUserInfo(updatedUser, updated); exitCode != ExitCode::Ok) {
LOG_WARN(Log::instance()->getLogger(), "Error in loadUserInfo");
return exitCode;
}
if (updated) {
- bool found;
- if (!ParmsDb::instance()->updateUser(userUpdated, found)) {
+ bool found = false;
+ if (!ParmsDb::instance()->updateUser(updatedUser, found)) {
LOG_WARN(Log::instance()->getLogger(), "Error in ParmsDb::updateUser");
return ExitCode::DbError;
}
if (!found) {
- LOG_WARN(Log::instance()->getLogger(), "User not found for userDbId=" << userUpdated.dbId());
+ LOG_WARN(Log::instance()->getLogger(), "User not found for userDbId=" << updatedUser.dbId());
return ExitCode::DataError;
}
}
- userToUserInfo(userUpdated, userInfo);
+ userToUserInfo(updatedUser, userInfo);
return ExitCode::Ok;
}
ExitCode ServerRequests::updateUser(const User &user, UserInfo &userInfo) {
- bool found;
+ bool found = false;
if (!ParmsDb::instance()->updateUser(user, found)) {
LOG_WARN(Log::instance()->getLogger(), "Error in ParmsDb::updateUser");
return ExitCode::DbError;
@@ -825,13 +824,11 @@ ExitCode ServerRequests::updateUser(const User &user, UserInfo &userInfo) {
// Load User info
User userUpdated(user);
- bool updated;
- ExitCode exitCode = loadUserInfo(userUpdated, updated);
- if (exitCode != ExitCode::Ok) {
+ bool updated = false;
+ if (const ExitCode exitCode = loadUserInfo(userUpdated, updated); exitCode != ExitCode::Ok) {
LOG_WARN(Log::instance()->getLogger(), "Error in loadUserInfo");
return exitCode;
}
-
userToUserInfo(userUpdated, userInfo);
return ExitCode::Ok;
@@ -1680,7 +1677,7 @@ ExitCode ServerRequests::loadUserInfo(User &user, bool &updated) {
try {
job = std::make_shared(user.dbId());
} catch (const std::exception &e) {
- std::string what = e.what();
+ const std::string what = e.what();
LOG_WARN(Log::instance()->getLogger(),
"Error in GetInfoUserJob::GetInfoUserJob for userDbId=" << user.dbId() << " error=" << what.c_str());
if (what == invalidToken) {
@@ -1697,8 +1694,8 @@ ExitCode ServerRequests::loadUserInfo(User &user, bool &updated) {
return exitCode;
}
- Poco::Net::HTTPResponse::HTTPStatus httpStatus = job->getStatusCode();
- if (httpStatus == Poco::Net::HTTPResponse::HTTPStatus::HTTP_FORBIDDEN ||
+ if (const Poco::Net::HTTPResponse::HTTPStatus httpStatus = job->getStatusCode();
+ httpStatus == Poco::Net::HTTPResponse::HTTPStatus::HTTP_FORBIDDEN ||
httpStatus == Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND) {
LOG_WARN(Log::instance()->getLogger(), "Unable to get user info for userId=" << user.userId());
return ExitCode::DataError;
@@ -1707,47 +1704,34 @@ ExitCode ServerRequests::loadUserInfo(User &user, bool &updated) {
return ExitCode::NetworkError;
}
- Poco::JSON::Object::Ptr dataObj = job->jsonRes()->getObject(dataKey);
- if (dataObj && dataObj->size() != 0) {
- std::string name;
- if (!JsonParserUtility::extractValue(dataObj, displayNameKey, name)) {
- return ExitCode::BackError;
- }
- if (user.name() != name) {
- user.setName(name);
- updated = true;
- }
+ if (user.name() != job->name()) {
+ user.setName(job->name());
+ updated = true;
+ }
- std::string email;
- if (!JsonParserUtility::extractValue(dataObj, emailKey, email)) {
- return ExitCode::BackError;
- }
- if (user.email() != email) {
- user.setEmail(email);
- updated = true;
- }
+ if (user.email() != job->email()) {
+ user.setEmail(job->email());
+ updated = true;
+ }
- std::string avatarUrl;
- if (!JsonParserUtility::extractValue(dataObj, avatarKey, avatarUrl)) {
- return ExitCode::BackError;
- }
- if (user.avatarUrl() != avatarUrl) {
- if (avatarUrl.empty()) {
- user.setAvatar(nullptr);
- user.setAvatarUrl(std::string());
- } else if (user.avatarUrl() != avatarUrl) {
- // get avatarData
- user.setAvatarUrl(avatarUrl);
- exitCode = loadUserAvatar(user);
- if (exitCode != ExitCode::Ok) {
- return exitCode;
- }
- }
- updated = true;
+ if (user.avatarUrl() != job->avatarUrl()) {
+ if (job->avatarUrl().empty()) {
+ user.setAvatar(nullptr);
+ user.setAvatarUrl(std::string());
+ } else if (user.avatarUrl() != job->avatarUrl()) {
+ // get avatarData
+ user.setAvatarUrl(job->avatarUrl());
+ exitCode = loadUserAvatar(user);
}
+ updated = true;
}
- return ExitCode::Ok;
+ if (user.isStaff() != job->isStaff()) {
+ user.setIsStaff(job->isStaff());
+ updated = true;
+ }
+
+ return exitCode;
}
ExitCode ServerRequests::loadUserAvatar(User &user) {
@@ -1941,6 +1925,7 @@ void ServerRequests::userToUserInfo(const User &user, UserInfo &userInfo) {
}
userInfo.setConnected(!user.keychainKey().empty());
userInfo.setCredentialsAsked(false);
+ userInfo.setIsStaff(user.isStaff());
}
void ServerRequests::accountToAccountInfo(const Account &account, AccountInfo &accountInfo) {
@@ -2051,6 +2036,7 @@ void ServerRequests::parametersToParametersInfo(const Parameters ¶meters, Pa
}
}
parametersInfo.setMaxAllowedCpu(parameters.maxAllowedCpu());
+ parametersInfo.setDistributionChannel(parameters.distributionChannel());
}
void ServerRequests::parametersInfoToParameters(const ParametersInfo ¶metersInfo, Parameters ¶meters) {
@@ -2086,6 +2072,7 @@ void ServerRequests::parametersInfoToParameters(const ParametersInfo ¶meters
std::shared_ptr>(new std::vector(dialogGeometryArr.begin(), dialogGeometryArr.end())));
}
parameters.setMaxAllowedCpu(parametersInfo.maxAllowedCpu());
+ parameters.setDistributionChannel(parametersInfo.distributionChannel());
}
void ServerRequests::proxyConfigToProxyConfigInfo(const ProxyConfig &proxyConfig, ProxyConfigInfo &proxyConfigInfo) {
diff --git a/src/libsyncengine/syncpal/syncpal.cpp b/src/libsyncengine/syncpal/syncpal.cpp
index a0928f1d8..212a79a8f 100644
--- a/src/libsyncengine/syncpal/syncpal.cpp
+++ b/src/libsyncengine/syncpal/syncpal.cpp
@@ -516,7 +516,6 @@ void SyncPal::loadProgress(int64_t ¤tFile, int64_t &totalFiles, int64_t &c
void SyncPal::createSharedObjects() {
LOG_SYNCPAL_DEBUG(_logger, "Create shared objects");
- _interruptSync = std::make_shared(false);
_localSnapshot = std::make_shared(ReplicaSide::Local, _syncDb->rootNode());
_remoteSnapshot = std::make_shared(ReplicaSide::Remote, _syncDb->rootNode());
_localSnapshotCopy = std::make_shared(ReplicaSide::Local, _syncDb->rootNode());
@@ -534,7 +533,6 @@ void SyncPal::createSharedObjects() {
void SyncPal::freeSharedObjects() {
LOG_SYNCPAL_DEBUG(_logger, "Free shared objects");
- _interruptSync.reset();
_localSnapshot.reset();
_remoteSnapshot.reset();
_localSnapshotCopy.reset();
@@ -548,7 +546,6 @@ void SyncPal::freeSharedObjects() {
_progressInfo.reset();
// Check that there is no memory leak
- ASSERT(_interruptSync.use_count() == 0);
ASSERT(_localSnapshot.use_count() == 0);
ASSERT(_remoteSnapshot.use_count() == 0);
ASSERT(_localSnapshotCopy.use_count() == 0);
@@ -773,6 +770,11 @@ bool SyncPal::isSnapshotValid(ReplicaSide side) {
return side == ReplicaSide::Local ? _localSnapshot->isValid() : _remoteSnapshot->isValid();
}
+void SyncPal::resetSnapshotInvalidationCounters() {
+ _localFSObserverWorker->resetInvalidateCounter();
+ _remoteFSObserverWorker->resetInvalidateCounter();
+}
+
ExitCode SyncPal::addDlDirectJob(const SyncPath &relativePath, const SyncPath &localPath) {
std::optional localNodeId = std::nullopt;
bool found = false;
diff --git a/src/libsyncengine/syncpal/syncpal.h b/src/libsyncengine/syncpal/syncpal.h
index ad285600d..47412c405 100644
--- a/src/libsyncengine/syncpal/syncpal.h
+++ b/src/libsyncengine/syncpal/syncpal.h
@@ -236,6 +236,7 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this {
[[nodiscard]] bool getSyncFileItem(const SyncPath &path, SyncFileItem &item);
bool isSnapshotValid(ReplicaSide side);
+ void resetSnapshotInvalidationCounters();
ExitCode addDlDirectJob(const SyncPath &relativePath, const SyncPath &localPath);
ExitCode cancelDlDirectJobs(const std::list &fileList);
@@ -316,7 +317,6 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this {
std::shared_ptr _syncDb{nullptr};
// Shared objects
- std::shared_ptr _interruptSync{nullptr};
std::shared_ptr _localSnapshot{nullptr}; // Real time local snapshot
std::shared_ptr _remoteSnapshot{nullptr}; // Real time remote snapshot
std::shared_ptr _localSnapshotCopy{
@@ -357,7 +357,6 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this {
bool createOrOpenDb(const SyncPath &syncDbPath, const std::string &version,
const std::string &targetNodeId = std::string());
void setSyncHasFullyCompletedInParms(bool syncHasFullyCompleted);
- inline bool interruptSync() const { return *_interruptSync; }
ExitCode setListingCursor(const std::string &value, int64_t timestamp);
ExitCode listingCursor(std::string &value, int64_t ×tamp);
ExitCode updateSyncNode(SyncNodeType syncNodeType);
diff --git a/src/libsyncengine/syncpal/syncpalworker.cpp b/src/libsyncengine/syncpal/syncpalworker.cpp
index d52299145..a1de7a259 100644
--- a/src/libsyncengine/syncpal/syncpalworker.cpp
+++ b/src/libsyncengine/syncpal/syncpalworker.cpp
@@ -202,13 +202,6 @@ void SyncPalWorker::execute() {
setExitCause(ExitCause::WorkerExited);
}
break;
- } else if (interruptCondition()) {
- LOG_SYNCPAL_INFO(_logger, "***** Step " << stepName(_step).c_str() << " interruption");
-
- // Stop the step workers and restart a sync
- stopAndWaitForExitOfWorkers(stepWorkers);
- initStepFirst(stepWorkers, inputSharedObject, false);
- continue;
}
} else {
// Start workers
@@ -360,6 +353,7 @@ void SyncPalWorker::initStep(SyncStep step, std::shared_ptr (&worke
inputSharedObject[0] = nullptr;
inputSharedObject[1] = nullptr;
_syncPal->stopEstimateUpdates();
+ _syncPal->resetSnapshotInvalidationCounters();
if (!_syncPal->restart()) {
_syncPal->setSyncHasFullyCompletedInParms(true);
}
@@ -383,33 +377,9 @@ void SyncPalWorker::initStepFirst(std::shared_ptr (&workers)[2],
_syncPal->resetSharedObjects();
}
- *_syncPal->_interruptSync = false;
initStep(SyncStep::Idle, workers, inputSharedObject);
}
-bool SyncPalWorker::interruptCondition() const {
- switch (_step) {
- case SyncStep::Idle:
- return false;
- break;
- case SyncStep::UpdateDetection1:
- case SyncStep::UpdateDetection2:
- case SyncStep::Reconciliation1:
- case SyncStep::Reconciliation2:
- case SyncStep::Reconciliation3:
- case SyncStep::Reconciliation4:
- case SyncStep::Propagation1:
- case SyncStep::Propagation2:
- case SyncStep::Done:
- return _syncPal->interruptSync();
- break;
- default:
- LOG_SYNCPAL_WARN(_logger, "Invalid status");
- return false;
- break;
- }
-}
-
SyncStep SyncPalWorker::nextStep() const {
switch (_step) {
case SyncStep::Idle:
@@ -419,10 +389,9 @@ SyncStep SyncPalWorker::nextStep() const {
_syncPal->restart()))
? SyncStep::UpdateDetection1
: SyncStep::Idle;
- break;
case SyncStep::UpdateDetection1: {
- auto logNbOps = [=](const ReplicaSide side) {
- auto opsSet = _syncPal->operationSet(side);
+ auto logNbOps = [this](const ReplicaSide side) {
+ const auto opsSet = _syncPal->operationSet(side);
LOG_SYNCPAL_DEBUG(_logger, opsSet->nbOps()
<< " " << side << " operations detected (# CREATE: "
<< opsSet->nbOpsByType(OperationType::Create)
@@ -444,38 +413,29 @@ SyncStep SyncPalWorker::nextStep() const {
_syncPal->operationSet(ReplicaSide::Remote)->updated())
? SyncStep::UpdateDetection2
: SyncStep::Done;
- break;
}
case SyncStep::UpdateDetection2:
return (_syncPal->updateTree(ReplicaSide::Local)->updated() || _syncPal->updateTree(ReplicaSide::Remote)->updated())
? SyncStep::Reconciliation1
: SyncStep::Done;
- break;
case SyncStep::Reconciliation1:
return SyncStep::Reconciliation2;
- break;
case SyncStep::Reconciliation2:
- LOG_SYNCPAL_DEBUG(_logger, _syncPal->_conflictQueue->size() << " conflicts found");
+ LOG_SYNCPAL_DEBUG(_logger, _syncPal->_conflictQueue->size() << " conflicts found")
return _syncPal->_conflictQueue->empty() ? SyncStep::Reconciliation4 : SyncStep::Reconciliation3;
- break;
case SyncStep::Reconciliation3:
case SyncStep::Reconciliation4:
- LOG_SYNCPAL_DEBUG(_logger, _syncPal->_syncOps->size() << " operations generated");
+ LOG_SYNCPAL_DEBUG(_logger, _syncPal->_syncOps->size() << " operations generated")
return _syncPal->_conflictQueue->empty() ? SyncStep::Propagation1 : SyncStep::Propagation2;
- break;
case SyncStep::Propagation1:
return SyncStep::Propagation2;
- break;
case SyncStep::Propagation2:
return SyncStep::Done;
- break;
case SyncStep::Done:
return SyncStep::Idle;
- break;
default:
- LOG_SYNCPAL_WARN(_logger, "Invalid status");
+ LOG_SYNCPAL_WARN(_logger, "Invalid status")
return SyncStep::Idle;
- break;
}
}
diff --git a/src/libsyncengine/syncpal/syncpalworker.h b/src/libsyncengine/syncpal/syncpalworker.h
index 71e4a7339..b53feafe3 100644
--- a/src/libsyncengine/syncpal/syncpalworker.h
+++ b/src/libsyncengine/syncpal/syncpalworker.h
@@ -42,7 +42,6 @@ class SyncPalWorker : public ISyncWorker {
std::shared_ptr (&inputSharedObject)[2]);
void initStepFirst(std::shared_ptr (&workers)[2], std::shared_ptr (&inputSharedObject)[2],
bool reset);
- bool interruptCondition() const;
SyncStep nextStep() const;
void stopAndWaitForExitOfWorker(std::shared_ptr worker);
void stopWorkers(std::shared_ptr workers[2]);
diff --git a/src/libsyncengine/syncpal/tmpblacklistmanager.cpp b/src/libsyncengine/syncpal/tmpblacklistmanager.cpp
index a8e7f8ed0..f1604add8 100644
--- a/src/libsyncengine/syncpal/tmpblacklistmanager.cpp
+++ b/src/libsyncengine/syncpal/tmpblacklistmanager.cpp
@@ -62,7 +62,7 @@ void TmpBlacklistManager::increaseErrorCount(const NodeId &nodeId, NodeType type
insertInBlacklist(nodeId, side);
sentry::Handler::captureMessage(sentry::Level::Warning, "TmpBlacklistManager::increaseErrorCount",
- "Blacklisting item temporarily to avoid infinite loop");
+ "Blacklisting item temporarily to avoid infinite loop");
Error err(_syncPal->syncDbId(), "", nodeId, type, relativePath, ConflictType::None, InconsistencyType::None,
CancelType::TmpBlacklisted);
_syncPal->addError(err);
diff --git a/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp b/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp
index e609e8fb8..ffde7602b 100644
--- a/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp
@@ -30,13 +30,11 @@ namespace KDC {
ComputeFSOperationWorker::ComputeFSOperationWorker(std::shared_ptr syncPal, const std::string &name,
const std::string &shortName) :
- ISyncWorker(syncPal, name, shortName),
- _syncDb(syncPal->syncDb()) {}
+ ISyncWorker(syncPal, name, shortName), _syncDb(syncPal->syncDb()) {}
ComputeFSOperationWorker::ComputeFSOperationWorker(const std::shared_ptr testSyncDb, const std::string &name,
const std::string &shortName) :
- ISyncWorker(nullptr, name, shortName, true),
- _syncDb(testSyncDb) {}
+ ISyncWorker(nullptr, name, shortName, true), _syncDb(testSyncDb) {}
void ComputeFSOperationWorker::execute() {
ExitCode exitCode(ExitCode::Unknown);
diff --git a/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.cpp b/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.cpp
index 915b2a0f0..174bb8429 100644
--- a/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.cpp
@@ -22,23 +22,29 @@
namespace KDC {
-FileSystemObserverWorker::FileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name,
- const std::string &shortName, ReplicaSide side) :
- ISyncWorker(syncPal, name, shortName),
- _syncDb(syncPal->_syncDb), _snapshot(syncPal->snapshot(side)) {}
+constexpr int maxRetryBeforeInvalidation = 3;
-FileSystemObserverWorker::~FileSystemObserverWorker() {}
+FileSystemObserverWorker::FileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name,
+ const std::string &shortName, const ReplicaSide side) :
+ ISyncWorker(syncPal, name, shortName), _syncDb(syncPal->_syncDb), _snapshot(syncPal->snapshot(side)) {}
void FileSystemObserverWorker::invalidateSnapshot() {
- if (_snapshot->isValid()) {
- _snapshot->init();
- // *_interruptSync = true; // TODO : check if it is possible to avoid restarting the full sync is those cases
- LOG_SYNCPAL_DEBUG(_logger, (_snapshot->side() == ReplicaSide::Local ? "Local" : "Remote") << " snapshot invalidated");
+ if (!_snapshot->isValid()) return;
+
+ _invalidateCounter++;
+ if (_invalidateCounter < maxRetryBeforeInvalidation) {
+ LOG_SYNCPAL_DEBUG(_logger, _snapshot->side()
+ << " snapshot is not invalidated. Invalidation count: " << _invalidateCounter);
+ return;
}
+
+ _snapshot->init();
+ _invalidateCounter = 0;
+ LOG_SYNCPAL_DEBUG(_logger, _snapshot->side() << " snapshot invalidated");
}
void FileSystemObserverWorker::forceUpdate() {
- const std::lock_guard lock(_mutex);
+ const std::scoped_lock lock(_mutex);
_updating = true;
}
diff --git a/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h b/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h
index 5c9b4c2a3..7bfca5259 100644
--- a/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h
+++ b/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h
@@ -32,13 +32,13 @@ class FileSystemObserverWorker : public ISyncWorker {
public:
FileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name, const std::string &shortName,
ReplicaSide side);
- ~FileSystemObserverWorker() override;
void invalidateSnapshot();
+ void resetInvalidateCounter() { _invalidateCounter = 0; }
virtual void forceUpdate();
- virtual inline bool updating() const { return _updating; }
+ [[nodiscard]] virtual bool updating() const { return _updating; }
- std::shared_ptr snapshot() const { return _snapshot; }
+ [[nodiscard]] std::shared_ptr snapshot() const { return _snapshot; };
protected:
std::shared_ptr _syncDb;
@@ -52,10 +52,12 @@ class FileSystemObserverWorker : public ISyncWorker {
virtual ExitCode generateInitialSnapshot() = 0;
virtual ExitCode processEvents() { return ExitCode::Ok; }
- virtual bool isFolderWatcherReliable() const { return true; }
+ [[nodiscard]] virtual bool isFolderWatcherReliable() const { return true; }
private:
- virtual ReplicaSide getSnapshotType() const = 0;
+ [[nodiscard]] virtual ReplicaSide getSnapshotType() const = 0;
+
+ int _invalidateCounter{false}; // A counter used to invalidate the snapshot only after a few attempt.
friend class TestLocalFileSystemObserverWorker;
friend class TestRemoteFileSystemObserverWorker;
diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h b/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h
index 7cd59ecb4..af69a36ce 100644
--- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h
+++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h
@@ -34,14 +34,15 @@ class FolderWatcher {
FolderWatcher(LocalFileSystemObserverWorker *parent, const SyncPath &rootFolder);
virtual ~FolderWatcher() = default;
- const log4cplus::Logger &logger() const { return _logger; }
+ [[nodiscard]] const log4cplus::Logger &logger() const { return _logger; }
void start();
void stop();
+ [[nodiscard]] bool isReady() const { return _ready; }
// The FolderWatcher can only become unreliable on Linux
- inline bool isReliable() const { return _isReliable; }
- ExitInfo exitInfo() const { return _exitInfo; }
+ [[nodiscard]] bool isReliable() const { return _isReliable; }
+ [[nodiscard]] ExitInfo exitInfo() const { return _exitInfo; }
protected:
// Implement this method in your subclass with the code you want your thread to run
@@ -53,6 +54,7 @@ class FolderWatcher {
SyncPath _folder;
bool _isReliable = true;
bool _stop = false;
+ bool _ready{false};
private:
static void executeFunc(void *thisWorker);
diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp
index 8ae2d121b..6f477d9e1 100644
--- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp
@@ -57,6 +57,7 @@ void FolderWatcher_linux::startWatching() {
}
while (!_stop) {
+ _ready = true;
unsigned int avail;
ioctl(_fileDescriptor, FIONREAD,
&avail); // Since read() is blocking until something has changed, we use ioctl to check if there is changes
diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.h b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.h
index 0f5d72093..33e52094f 100644
--- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.h
+++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.h
@@ -35,9 +35,6 @@ class FolderWatcher_linux : public FolderWatcher {
void startWatching() override;
void stopWatching() override;
- /// On linux the watcher is ready when the ctor finished.
- bool _ready = true;
-
private:
int _fileDescriptor = -1;
diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp
index b878b2f05..8e032a0e3 100644
--- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp
@@ -90,6 +90,7 @@ static void callback([[maybe_unused]] ConstFSEventStreamRef streamRef, void *cli
void FolderWatcher_mac::startWatching() {
LOGW_DEBUG(_logger, L"Start watching folder: " << Utility::formatSyncPath(_folder));
LOG_DEBUG(_logger, "File system format: " << Utility::fileSystemName(_folder).c_str());
+ _ready = true;
CFStringRef path = CFStringCreateWithCString(nullptr, _folder.c_str(), kCFStringEncodingUTF8);
CFArrayRef pathsToWatch = CFArrayCreate(nullptr, (const void **) &path, 1, nullptr);
diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp
index a4a93caeb..7197bf3d4 100644
--- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp
@@ -30,10 +30,6 @@ namespace KDC {
FolderWatcher_win::FolderWatcher_win(LocalFileSystemObserverWorker *parent, const SyncPath &path) : FolderWatcher(parent, path) {}
-bool FolderWatcher_win::ready() const {
- return _ready;
-}
-
void FolderWatcher_win::changesLost() {
// Current snapshot needs to be invalidated
_parent->invalidateSnapshot();
diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h
index cc0537b8c..394aef0cc 100644
--- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h
+++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h
@@ -33,8 +33,6 @@ class FolderWatcher_win : public FolderWatcher {
public:
FolderWatcher_win(LocalFileSystemObserverWorker *parent, const SyncPath &path);
- bool ready() const;
-
void changesLost();
void changeDetected(const SyncPath &path, OperationType opType);
@@ -43,9 +41,6 @@ class FolderWatcher_win : public FolderWatcher {
void stopWatching() override;
private:
- /// Set to true once the WatcherThread is capturing events.
- bool _ready = false; // TODO : mutex???
-
HANDLE _directoryHandle = nullptr;
HANDLE _resultEventHandle = nullptr;
HANDLE _stopEventHandle = nullptr;
diff --git a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp
index 81ef4d9f3..17f5b84c6 100644
--- a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp
@@ -37,8 +37,7 @@ static const int waitForUpdateDelay = 1000; // 1sec
LocalFileSystemObserverWorker::LocalFileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name,
const std::string &shortName) :
- FileSystemObserverWorker(syncPal, name, shortName, ReplicaSide::Local),
- _rootFolder(syncPal->localPath()) {}
+ FileSystemObserverWorker(syncPal, name, shortName, ReplicaSide::Local), _rootFolder(syncPal->localPath()) {}
LocalFileSystemObserverWorker::~LocalFileSystemObserverWorker() {
LOG_SYNCPAL_DEBUG(_logger, "~LocalFileSystemObserverWorker");
diff --git a/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp b/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp
index 69bd1ce1e..4e0611647 100644
--- a/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp
@@ -47,8 +47,7 @@ namespace KDC {
RemoteFileSystemObserverWorker::RemoteFileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name,
const std::string &shortName) :
- FileSystemObserverWorker(syncPal, name, shortName, ReplicaSide::Remote),
- _driveDbId(syncPal->driveDbId()) {}
+ FileSystemObserverWorker(syncPal, name, shortName, ReplicaSide::Remote), _driveDbId(syncPal->driveDbId()) {}
RemoteFileSystemObserverWorker::~RemoteFileSystemObserverWorker() {
LOG_SYNCPAL_DEBUG(_logger, "~RemoteFileSystemObserverWorker");
diff --git a/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshotitem.cpp b/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshotitem.cpp
index f4356c88f..c0443e747 100644
--- a/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshotitem.cpp
+++ b/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshotitem.cpp
@@ -27,8 +27,7 @@ SnapshotItem::SnapshotItem(const NodeId &id) : _id(id) {}
SnapshotItem::SnapshotItem(const NodeId &id, const NodeId &parentId, const SyncName &name, SyncTime createdAt,
SyncTime lastModified, NodeType type, int64_t size, bool isLink /*= false*/, bool canWrite /*= true*/,
bool canShare /*= true*/) :
- _id(id),
- _parentId(parentId), _name(name), _createdAt(createdAt), _lastModified(lastModified), _type(type), _size(size),
+ _id(id), _parentId(parentId), _name(name), _createdAt(createdAt), _lastModified(lastModified), _type(type), _size(size),
_isLink(isLink), _canWrite(canWrite), _canShare(canShare) {
setName(name); // Needed for the computation of _normalizedName
}
diff --git a/src/libsyncengine/update_detection/update_detector/node.h b/src/libsyncengine/update_detection/update_detector/node.h
index cd9bf3b28..d5933ac34 100644
--- a/src/libsyncengine/update_detection/update_detector/node.h
+++ b/src/libsyncengine/update_detection/update_detector/node.h
@@ -126,9 +126,7 @@ class Node {
friend class UpdateTree;
// The node id should not be changed without also changing the map in the UpdateTree and the parent/child relationship in
// other nodes
- inline void setId(const std::optional &nodeId) {
- _id = nodeId;
- }
+ inline void setId(const std::optional &nodeId) { _id = nodeId; }
std::optional _idb = std::nullopt;
ReplicaSide _side = ReplicaSide::Unknown;
diff --git a/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp b/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp
index 70d1a8be5..aa5e097f8 100644
--- a/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp
+++ b/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp
@@ -30,14 +30,13 @@ namespace KDC {
UpdateTreeWorker::UpdateTreeWorker(std::shared_ptr syncPal, const std::string &name, const std::string &shortName,
ReplicaSide side) :
- ISyncWorker(syncPal, name, shortName),
- _syncDb(syncPal->_syncDb), _operationSet(syncPal->operationSet(side)), _updateTree(syncPal->updateTree(side)), _side(side) {}
+ ISyncWorker(syncPal, name, shortName), _syncDb(syncPal->_syncDb), _operationSet(syncPal->operationSet(side)),
+ _updateTree(syncPal->updateTree(side)), _side(side) {}
UpdateTreeWorker::UpdateTreeWorker(std::shared_ptr syncDb, std::shared_ptr operationSet,
std::shared_ptr updateTree, const std::string &name, const std::string &shortName,
ReplicaSide side) :
- ISyncWorker(nullptr, name, shortName),
- _syncDb(syncDb), _operationSet(operationSet), _updateTree(updateTree), _side(side) {}
+ ISyncWorker(nullptr, name, shortName), _syncDb(syncDb), _operationSet(operationSet), _updateTree(updateTree), _side(side) {}
UpdateTreeWorker::~UpdateTreeWorker() {
_operationSet.reset();
diff --git a/src/server/appserver.cpp b/src/server/appserver.cpp
index 4e5a83cca..7791f6bf2 100644
--- a/src/server/appserver.cpp
+++ b/src/server/appserver.cpp
@@ -1980,7 +1980,10 @@ void AppServer::onRequestReceived(int id, RequestNum num, const QByteArray ¶
break;
}
case RequestNum::UPDATER_VERSION_INFO: {
- VersionInfo versionInfo = _updateManager->versionInfo();
+ auto channel = DistributionChannel::Unknown;
+ QDataStream paramsStream(params);
+ paramsStream >> channel;
+ VersionInfo versionInfo = _updateManager->versionInfo(channel);
resultStream << versionInfo;
break;
}
@@ -2196,20 +2199,17 @@ void AppServer::onScheduleAppRestart() {
}
void AppServer::onShowWindowsUpdateDialog() {
- int id = 0;
-
QByteArray params;
QDataStream paramsStream(¶ms, QIODevice::WriteOnly);
paramsStream << _updateManager->versionInfo();
- CommServer::instance()->sendSignal(SignalNum::UPDATER_SHOW_DIALOG, params, id);
+ CommServer::instance()->sendSignal(SignalNum::UPDATER_SHOW_DIALOG, params);
}
void AppServer::onUpdateStateChanged(const UpdateState state) {
- int id = 0;
QByteArray params;
QDataStream paramsStream(¶ms, QIODevice::WriteOnly);
paramsStream << state;
- CommServer::instance()->sendSignal(SignalNum::UPDATER_STATE_CHANGED, params, id);
+ CommServer::instance()->sendSignal(SignalNum::UPDATER_STATE_CHANGED, params);
}
void AppServer::onRestartClientReceived() {
diff --git a/src/server/socketapi.cpp b/src/server/socketapi.cpp
index dbf1bfbae..22bc2469e 100644
--- a/src/server/socketapi.cpp
+++ b/src/server/socketapi.cpp
@@ -1323,7 +1323,7 @@ void SocketApi::processFileList(const QStringList &inFileList, std::listsetCallback(callback);
}
-ExitCode AbstractUpdater::checkUpdateAvailable(const DistributionChannel channel, UniqueId* id /*= nullptr*/) {
+ExitCode AbstractUpdater::checkUpdateAvailable(const DistributionChannel currentChannel, UniqueId* id /*= nullptr*/) {
+ _currentChannel = currentChannel;
setState(UpdateState::Checking);
- return _updateChecker->checkUpdateAvailability(channel, id);
+ return _updateChecker->checkUpdateAvailability(id);
}
void AbstractUpdater::setStateChangeCallback(const std::function& stateChangeCallback) {
@@ -43,16 +44,21 @@ void AbstractUpdater::setStateChangeCallback(const std::functionversionInfo().isValid()) {
+ if (!_updateChecker->isVersionReceived()) {
setState(UpdateState::CheckError);
LOG_WARN(Log::instance()->getLogger(), "Error while retrieving latest app version");
return;
}
- const bool available =
- CommonUtility::isVersionLower(CommonUtility::currentVersion(), _updateChecker->versionInfo().fullVersion());
- setState(available ? UpdateState::Available : UpdateState::UpToDate);
- if (available) {
+ const VersionInfo& versionInfo = _updateChecker->versionInfo(_currentChannel);
+ if (!versionInfo.isValid()) {
+ LOG_INFO(Log::instance()->getLogger(), "No valid update info retrieved for distribution channel: " << _currentChannel);
+ setState(UpdateState::UpToDate);
+ }
+
+ const bool newVersionAvailable = CommonUtility::isVersionLower(CommonUtility::currentVersion(), versionInfo.fullVersion());
+ setState(newVersionAvailable ? UpdateState::Available : UpdateState::UpToDate);
+ if (newVersionAvailable) {
LOG_INFO(Log::instance()->getLogger(), "New app version available");
} else {
LOG_INFO(Log::instance()->getLogger(), "App version is up to date");
diff --git a/src/server/updater/abstractupdater.h b/src/server/updater/abstractupdater.h
index 41cf8c82b..7a00b5627 100644
--- a/src/server/updater/abstractupdater.h
+++ b/src/server/updater/abstractupdater.h
@@ -30,21 +30,18 @@ class AbstractUpdater {
AbstractUpdater();
virtual ~AbstractUpdater() = default;
- [[nodiscard]] const VersionInfo &versionInfo() const { return _updateChecker->versionInfo(); }
+ [[nodiscard]] const VersionInfo &versionInfo(const DistributionChannel channel) const {
+ return _updateChecker->versionInfo(channel);
+ }
[[nodiscard]] const UpdateState &state() const { return _state; }
- /**
- * @brief Checks if an update is available with the currently set distribution channel.
- * @return ExitCode::Ok if no errors.
- */
- ExitCode checkUpdateAvailable() { return checkUpdateAvailable(_updateChecker->versionInfo().channel); }
/**
* @brief Updates distribution channel and checks if an update is available.
- * @param channel New distribution channel selected by the user.
+ * @param currentChannel The currently selected distribution channel.
* @param id Optional. ID of the created asynchronous job. Useful in tests.
* @return ExitCode::Ok if no errors.
*/
- ExitCode checkUpdateAvailable(DistributionChannel channel, UniqueId *id = nullptr);
+ ExitCode checkUpdateAvailable(DistributionChannel currentChannel, UniqueId *id = nullptr);
/**
* @brief Start the installation.
@@ -68,14 +65,17 @@ class AbstractUpdater {
static void unskipVersion();
[[nodiscard]] static bool isVersionSkipped(const std::string &version);
+ void setCurrentChannel(const DistributionChannel currentChannel) { _currentChannel = currentChannel; }
+
protected:
void setState(UpdateState newState);
+ DistributionChannel _currentChannel{DistributionChannel::Unknown};
+
private:
void onAppVersionReceived();
std::unique_ptr _updateChecker;
-
UpdateState _state{UpdateState::UpToDate}; // Current state of the update process.
std::function _stateChangeCallback = nullptr;
};
diff --git a/src/server/updater/sparkleupdater.mm b/src/server/updater/sparkleupdater.mm
index d52f83042..7c7fe55d4 100644
--- a/src/server/updater/sparkleupdater.mm
+++ b/src/server/updater/sparkleupdater.mm
@@ -187,8 +187,8 @@ - (BOOL)supportsGentleScheduledUpdateReminders {
}
void SparkleUpdater::onUpdateFound() {
- if (isVersionSkipped(versionInfo().fullVersion())) {
- LOG_INFO(KDC::Log::instance()->getLogger(), "Version " << versionInfo().fullVersion().c_str() << " is skipped.");
+ if (isVersionSkipped(versionInfo(_currentChannel).fullVersion())) {
+ LOG_INFO(KDC::Log::instance()->getLogger(), "Version " << versionInfo(_currentChannel).fullVersion().c_str() << " is skipped.");
return;
}
@@ -213,7 +213,7 @@ - (BOOL)supportsGentleScheduledUpdateReminders {
}
void SparkleUpdater::startInstaller() {
- reset(versionInfo().downloadUrl);
+ reset(versionInfo(_currentChannel).downloadUrl);
if (!d->updater || !d->spuStandardUserDriver) {
LOG_WARN(KDC::Log::instance()->getLogger(), "Initialization error!");
@@ -284,7 +284,7 @@ - (BOOL)supportsGentleScheduledUpdateReminders {
}
void SparkleUpdater::skipVersionCallback() {
- skipVersion(versionInfo().fullVersion());
+ skipVersion(versionInfo(_currentChannel).fullVersion());
}
} // namespace KDC
diff --git a/src/server/updater/updatechecker.cpp b/src/server/updater/updatechecker.cpp
index 4020ef1d9..0121b75b2 100644
--- a/src/server/updater/updatechecker.cpp
+++ b/src/server/updater/updatechecker.cpp
@@ -24,12 +24,11 @@
#include "jobs/network/getappversionjob.h"
#include "libcommon/utility/utility.h"
#include "log/log.h"
+#include "utility/utility.h"
namespace KDC {
-ExitCode UpdateChecker::checkUpdateAvailability(const DistributionChannel channel, UniqueId *id /*= nullptr*/) {
- _channel = channel;
-
+ExitCode UpdateChecker::checkUpdateAvailability(UniqueId *id /*= nullptr*/) {
std::shared_ptr job;
if (const auto exitCode = generateGetAppVersionJob(job); exitCode != ExitCode::Ok) return exitCode;
if (id) *id = job->jobId();
@@ -46,8 +45,43 @@ void UpdateChecker::setCallback(const std::function &callback) {
_callback = callback;
}
+class VersionInfoCmp {
+ public:
+ bool operator()(const VersionInfo &v1, const VersionInfo &v2) const {
+ if (v1.fullVersion() == v2.fullVersion()) {
+ // Same build version, use the channel to define priority
+ return v1.channel < v2.channel;
+ }
+ return CommonUtility::isVersionLower(v2.fullVersion(), v1.fullVersion());
+ }
+};
+
+const VersionInfo &UpdateChecker::versionInfo(const DistributionChannel choosedChannel) {
+ const VersionInfo &prodVersion = prodVersionInfo();
+
+ // If the user wants only `Production` versions, just return the current `Production` version.
+ if (choosedChannel == DistributionChannel::Prod) return prodVersion;
+
+ // Otherwise, we need to check if there is not a newer version in other channels.
+ const VersionInfo &betaVersion =
+ _versionsInfo.contains(DistributionChannel::Beta) ? _versionsInfo[DistributionChannel::Beta] : _defaultVersionInfo;
+ const VersionInfo &internalVersion = _versionsInfo.contains(DistributionChannel::Internal)
+ ? _versionsInfo[DistributionChannel::Internal]
+ : _defaultVersionInfo;
+ std::set, VersionInfoCmp> sortedVersionList;
+ sortedVersionList.insert(prodVersion);
+ sortedVersionList.insert(betaVersion);
+ sortedVersionList.insert(internalVersion);
+ for (const auto &versionInfo: sortedVersionList) {
+ if (versionInfo.get().channel <= choosedChannel) return versionInfo;
+ }
+
+ return _defaultVersionInfo;
+}
+
void UpdateChecker::versionInfoReceived(UniqueId jobId) {
- _versionInfo.clear();
+ _isVersionReceived = false;
+ _versionsInfo.clear();
LOG_INFO(Log::instance()->getLogger(), "App version info received");
auto job = JobManager::instance()->getJob(jobId);
@@ -65,13 +99,14 @@ void UpdateChecker::versionInfoReceived(UniqueId jobId) {
ss << errorCode.c_str() << " - " << errorDescr;
sentry::Handler::captureMessage(sentry::Level::Warning, "AbstractUpdater::checkUpdateAvailable", ss.str());
LOG_ERROR(Log::instance()->getLogger(), ss.str().c_str());
+ } else if (getAppVersionJobPtr->exitCode() != ExitCode::Ok) {
+ LOG_ERROR(Log::instance()->getLogger(), "Error in UpdateChecker::versionInfoReceived : exit code: "
+ << getAppVersionJobPtr->exitCode()
+ << ", exit cause: " << getAppVersionJobPtr->exitCause());
} else {
- _versionInfo = getAppVersionJobPtr->getProdVersionInfo();
- if (!_versionInfo.isValid()) {
- std::string error = "Invalid version info!";
- sentry::Handler::captureMessage(sentry::Level::Warning, "AbstractUpdater::checkUpdateAvailable", error);
- LOG_ERROR(Log::instance()->getLogger(), error.c_str());
- }
+ _versionsInfo = getAppVersionJobPtr->versionsInfo();
+ _prodVersionChannel = getAppVersionJobPtr->prodVersionChannel();
+ _isVersionReceived = true;
}
_callback();
diff --git a/src/server/updater/updatechecker.h b/src/server/updater/updatechecker.h
index 6f70410a8..07520dda5 100644
--- a/src/server/updater/updatechecker.h
+++ b/src/server/updater/updatechecker.h
@@ -30,21 +30,32 @@ class UpdateChecker {
virtual ~UpdateChecker() = default;
/**
- * @brief Asynchronously check for new version informations.
- * @param channel Distribution channel (i.e. Production, Beta or Internal).
+ * @brief Asynchronously check for new version information.
* @param id Optional. ID of the created asynchronous job. Useful in tests.
- * @return ExitCode::Ok if the job has been succesfully created.
+ * @return ExitCode::Ok if the job has been successfully created.
*/
- ExitCode checkUpdateAvailability(DistributionChannel channel, UniqueId *id = nullptr);
+ ExitCode checkUpdateAvailability(UniqueId *id = nullptr);
void setCallback(const std::function &callback);
- [[nodiscard]] const VersionInfo &versionInfo() const { return _versionInfo; }
+ /**
+ * @brief Return the version information. Implements some logic to always return the highest available versions according
+ * to the selected distribution channel. That means if the `Beta` version is newer than the `Internal` version, the the
+ * `Beta` version wins over the `Internal` one and must be proposed even if the user has selected the `Internal` channel.
+ * The rule is the `Production` version wins over all others, the `Beta` verison wins over the `Internal` version.
+ * @param choosedChannel The selected distribution channel.
+ * @return A reference to the found `VersionInfo` object. If not found, return a reference to default constructed, invalid
+ * `VersionInfo`object.
+ */
+ const VersionInfo &versionInfo(DistributionChannel choosedChannel);
+
+ [[nodiscard]] const std::unordered_map &versionsInfo() const { return _versionsInfo; }
+ [[nodiscard]] bool isVersionReceived() const { return _isVersionReceived; }
private:
/**
* @brief Callback used to extract the version info.
- * @param jobId ID of
+ * @param jobId ID of the terminated job.
*/
void versionInfoReceived(UniqueId jobId);
@@ -52,13 +63,26 @@ class UpdateChecker {
* @brief Create a shared pointer to the `GetAppVersionJob`. Override this method in test class to test different
* scenarios.
* @param job The `GetAppVersionJob` we want to use in `checkUpdateAvailable()`.
- * @return ExitCode::Ok if the job has been succesfully created.
+ * @return ExitCode::Ok if the job has been successfully created.
*/
virtual ExitCode generateGetAppVersionJob(std::shared_ptr &job);
+ /**
+ * @brief Return the adequate version info, according to whether the current app has been selected in the progressive
+ * update distribution process.
+ * @return const reference on a VersionInfo
+ */
+ const VersionInfo &prodVersionInfo() {
+ return _versionsInfo.contains(_prodVersionChannel) ? _versionsInfo[_prodVersionChannel] : _defaultVersionInfo;
+ }
+
std::function _callback = nullptr;
- DistributionChannel _channel = DistributionChannel::Unknown;
- VersionInfo _versionInfo; // A struct keeping all the informations about the currently available version.
+ DistributionChannel _prodVersionChannel{DistributionChannel::Unknown};
+ const VersionInfo _defaultVersionInfo;
+ AllVersionsInfo _versionsInfo;
+ bool _isVersionReceived{false};
+
+ friend class TestUpdateChecker;
};
} // namespace KDC
diff --git a/src/server/updater/updatemanager.cpp b/src/server/updater/updatemanager.cpp
index 51fa81536..db2f4ecf4 100644
--- a/src/server/updater/updatemanager.cpp
+++ b/src/server/updater/updatemanager.cpp
@@ -26,16 +26,15 @@
#include "linuxupdater.h"
#endif
-
-#include "db/parmsdb.h"
#include "libcommon/utility/utility.h"
#include "log/log.h"
#include "requests/parameterscache.h"
-#include "utility/utility.h"
namespace KDC {
UpdateManager::UpdateManager(QObject *parent) : QObject(parent) {
+ _currentChannel = ParametersCache::instance()->parameters().distributionChannel();
+
createUpdater();
connect(&_updateCheckTimer, &QTimer::timeout, this, &UpdateManager::slotTimerFired);
@@ -49,7 +48,14 @@ UpdateManager::UpdateManager(QObject *parent) : QObject(parent) {
connect(this, &UpdateManager::updateStateChanged, this, &UpdateManager::slotUpdateStateChanged, Qt::QueuedConnection);
// At startup, do a check in any case and setup distribution channel.
- QTimer::singleShot(3000, this, [this]() { setDistributionChannel(readDistributionChannelFromDb()); });
+ QTimer::singleShot(3000, this, [this]() { setDistributionChannel(_currentChannel); });
+}
+
+void UpdateManager::setDistributionChannel(const DistributionChannel channel) {
+ _currentChannel = channel;
+ _updater->checkUpdateAvailable(channel);
+ ParametersCache::instance()->parameters().setDistributionChannel(channel);
+ ParametersCache::instance()->save();
}
void UpdateManager::startInstaller() const {
@@ -62,7 +68,7 @@ void UpdateManager::startInstaller() const {
}
void UpdateManager::slotTimerFired() const {
- _updater->checkUpdateAvailable();
+ _updater->checkUpdateAvailable(_currentChannel);
}
void UpdateManager::slotUpdateStateChanged(const UpdateState newState) {
@@ -75,8 +81,9 @@ void UpdateManager::slotUpdateStateChanged(const UpdateState newState) {
break;
}
case UpdateState::ManualUpdateAvailable: {
- emit updateAnnouncement(tr("New update available."),
- tr("Version %1 is available for download.").arg(_updater->versionInfo().tag.c_str()));
+ emit updateAnnouncement(
+ tr("New update available."),
+ tr("Version %1 is available for download.").arg(_updater->versionInfo(_currentChannel).tag.c_str()));
break;
}
case UpdateState::Available: {
@@ -85,7 +92,7 @@ void UpdateManager::slotUpdateStateChanged(const UpdateState newState) {
break;
}
case UpdateState::Ready: {
- if (AbstractUpdater::isVersionSkipped(_updater->versionInfo().fullVersion())) break;
+ if (AbstractUpdater::isVersionSkipped(_updater->versionInfo(_currentChannel).fullVersion())) break;
// The new version is ready to be installed
#if defined(_WIN32)
emit showUpdateDialog();
@@ -118,8 +125,4 @@ void UpdateManager::onUpdateStateChanged(const UpdateState newState) {
emit updateStateChanged(newState);
}
-DistributionChannel UpdateManager::readDistributionChannelFromDb() const {
- return ParametersCache::instance()->parameters().distributionChannel();
-}
-
} // namespace KDC
diff --git a/src/server/updater/updatemanager.h b/src/server/updater/updatemanager.h
index ca3ef0a11..230d656ed 100644
--- a/src/server/updater/updatemanager.h
+++ b/src/server/updater/updatemanager.h
@@ -40,10 +40,10 @@ class UpdateManager final : public QObject {
public:
explicit UpdateManager(QObject *parent);
- void setDistributionChannel(const DistributionChannel channel) const {
- _updater->checkUpdateAvailable(channel);
- } // TODO : write to DB
- [[nodiscard]] const VersionInfo &versionInfo() const { return _updater->versionInfo(); }
+ void setDistributionChannel(DistributionChannel channel);
+ [[nodiscard]] const VersionInfo &versionInfo(const DistributionChannel channel = DistributionChannel::Unknown) const {
+ return _updater->versionInfo(channel == DistributionChannel::Unknown ? _currentChannel : channel);
+ }
[[nodiscard]] const UpdateState &state() const { return _updater->state(); }
void startInstaller() const;
@@ -67,10 +67,8 @@ class UpdateManager final : public QObject {
void onUpdateStateChanged(UpdateState newState);
- [[nodiscard]] DistributionChannel readDistributionChannelFromDb() const;
-
std::unique_ptr _updater;
-
+ DistributionChannel _currentChannel{DistributionChannel::Unknown};
QTimer _updateCheckTimer; /** Timer for the regular update check. */
};
diff --git a/src/server/updater/windowsupdater.cpp b/src/server/updater/windowsupdater.cpp
index 1f945e410..eb0e06c41 100644
--- a/src/server/updater/windowsupdater.cpp
+++ b/src/server/updater/windowsupdater.cpp
@@ -61,7 +61,7 @@ void WindowsUpdater::downloadUpdate() noexcept {
return;
}
- auto job = std::make_shared(filepath, versionInfo().downloadUrl);
+ auto job = std::make_shared(filepath, versionInfo(_currentChannel).downloadUrl);
const std::function callback = std::bind_front(&WindowsUpdater::downloadFinished, this);
JobManager::instance()->queueAsyncJob(job, Poco::Thread::PRIO_NORMAL, callback);
setState(UpdateState::Downloading);
@@ -110,12 +110,13 @@ void WindowsUpdater::downloadFinished(const UniqueId jobId) {
}
bool WindowsUpdater::getInstallerPath(SyncPath &path) const {
- const auto pos = versionInfo().downloadUrl.find_last_of('/');
- const auto installerName = versionInfo().downloadUrl.substr(pos + 1);
+ const auto url = versionInfo(_currentChannel).downloadUrl;
+ const auto pos = url.find_last_of('/');
+ const auto installerName = url.substr(pos + 1);
SyncPath tmpDirPath;
if (IoError ioError = IoError::Unknown; !IoHelper::tempDirectoryPath(tmpDirPath, ioError)) {
sentry::Handler::captureMessage(sentry::Level::Warning, "WindowsUpdater::getInstallerPath",
- "Impossible to retrieve installer destination directory.");
+ "Impossible to retrieve installer destination directory.");
return false;
}
path = tmpDirPath / installerName;
diff --git a/test/libcommon/log/sentry/testsentryhandler.h b/test/libcommon/log/sentry/testsentryhandler.h
index 266a5f2b8..679b489fb 100644
--- a/test/libcommon/log/sentry/testsentryhandler.h
+++ b/test/libcommon/log/sentry/testsentryhandler.h
@@ -29,9 +29,10 @@ class MockTestSentryHandler : public sentry::Handler {
MockTestSentryHandler();
int sentryUploadedEventCount() const { return _sentryUploadedEventCount; }
void captureMessage(sentry::Level level, const std::string &title, const std::string &message,
- const SentryUser &user = SentryUser()) {
+ const SentryUser &user = SentryUser()) {
_captureMessage(level, title, message, user);
}
+
private:
void sendEventToSentry(const sentry::Level level, const std::string &title, const std::string &message) const final;
mutable int _sentryUploadedEventCount = 0;
diff --git a/test/libsyncengine/jobs/local/testlocaljobs.cpp b/test/libsyncengine/jobs/local/testlocaljobs.cpp
index c75aff90c..633af5d6e 100644
--- a/test/libsyncengine/jobs/local/testlocaljobs.cpp
+++ b/test/libsyncengine/jobs/local/testlocaljobs.cpp
@@ -33,7 +33,7 @@ namespace KDC {
class LocalDeleteJobMockingTrash : public LocalDeleteJob {
public:
- explicit LocalDeleteJobMockingTrash(const SyncPath &absolutePath) : LocalDeleteJob(absolutePath){};
+ explicit LocalDeleteJobMockingTrash(const SyncPath &absolutePath) : LocalDeleteJob(absolutePath) {};
void setMoveToTrashFailed(bool failed) { _moveToTrashFailed = failed; };
protected:
diff --git a/test/libsyncengine/jobs/network/testnetworkjobs.cpp b/test/libsyncengine/jobs/network/testnetworkjobs.cpp
index cec872cdf..9c6f0c92a 100644
--- a/test/libsyncengine/jobs/network/testnetworkjobs.cpp
+++ b/test/libsyncengine/jobs/network/testnetworkjobs.cpp
@@ -350,8 +350,7 @@ void TestNetworkJobs::testGetAvatar() {
CPPUNIT_ASSERT(exitCode == ExitCode::Ok);
CPPUNIT_ASSERT(job.jsonRes());
- Poco::JSON::Object::Ptr data = job.jsonRes()->getObject(dataKey);
- std::string avatarUrl = data->get(avatarKey);
+ const std::string avatarUrl = job.avatarUrl();
GetAvatarJob avatarJob(avatarUrl);
exitCode = avatarJob.runSynchronously();
@@ -627,8 +626,9 @@ void TestNetworkJobs::testGetInfoUser() {
const ExitCode exitCode = job.runSynchronously();
CPPUNIT_ASSERT(exitCode == ExitCode::Ok);
- Poco::JSON::Object::Ptr data = job.jsonRes()->getObject(dataKey);
- // CPPUNIT_ASSERT(data->get(emailKey).toString() == _email);
+ CPPUNIT_ASSERT_EQUAL(std::string("John Doe"), job.name());
+ CPPUNIT_ASSERT_EQUAL(std::string("john.doe@nogafam.ch"), job.email());
+ CPPUNIT_ASSERT_EQUAL(false, job.isStaff());
}
void TestNetworkJobs::testGetInfoDrive() {
@@ -959,11 +959,11 @@ void TestNetworkJobs::testGetAppVersionInfo() {
GetAppVersionJob job(CommonUtility::platform(), appUid);
job.runSynchronously();
CPPUNIT_ASSERT(!job.hasHttpError());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Internal).isValid());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Beta).isValid());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Next).isValid());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Prod).isValid());
- CPPUNIT_ASSERT(job.getProdVersionInfo().isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Internal).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Beta).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Next).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Prod).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(job.prodVersionChannel()).isValid());
}
// With 1 user ID
{
@@ -974,11 +974,11 @@ void TestNetworkJobs::testGetAppVersionInfo() {
GetAppVersionJob job(CommonUtility::platform(), appUid, {user.userId()});
job.runSynchronously();
CPPUNIT_ASSERT(!job.hasHttpError());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Internal).isValid());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Beta).isValid());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Next).isValid());
- CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Prod).isValid());
- CPPUNIT_ASSERT(job.getProdVersionInfo().isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Internal).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Beta).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Next).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(DistributionChannel::Prod).isValid());
+ CPPUNIT_ASSERT(job.versionInfo(job.prodVersionChannel()).isValid());
}
// // With several user IDs
// TODO : commented out because we need valid user IDs but we have only one available in tests for now
diff --git a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp
index 2130def45..c55ac792c 100644
--- a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp
+++ b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp
@@ -42,7 +42,7 @@ using namespace CppUnit;
namespace KDC {
-const uint64_t nbFileInTestDir = 5; // Test directory contains 5 files
+constexpr uint64_t nbFileInTestDir = 5; // Test directory contains 5 files
void TestLocalFileSystemObserverWorker::setUp() {
_logger = Log::instance()->getLogger();
@@ -121,8 +121,8 @@ void TestLocalFileSystemObserverWorker::testLFSOWithInitialSnapshot() {
uint64_t fileCounter = 0;
for (const auto &id: ids) {
- const auto name = _syncPal->snapshot(ReplicaSide::Local)->name(id);
- if (name == Str(".DS_Store") || name == Str(".ds_store")) {
+ if (const auto name = _syncPal->snapshot(ReplicaSide::Local)->name(id);
+ name == Str(".DS_Store") || name == Str(".ds_store")) {
continue; // Ignore ".DS_Store"
}
@@ -176,7 +176,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithFiles() {
SyncPath sourcePath = testAbsolutePath;
SyncPath destinationPath = _subDirPath / filename;
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::moveItem(sourcePath, destinationPath, ioError);
Utility::msleep(1000); // Wait 1sec
@@ -192,7 +192,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithFiles() {
SyncPath source = testAbsolutePath;
SyncPath destinationPath = _subDirPath / Str("test_file_renamed.txt");
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::renameItem(source, destinationPath, ioError);
Utility::msleep(1000); // Wait 1sec
@@ -204,7 +204,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithFiles() {
{
/// Delete file
LOGW_DEBUG(_logger, L"***** test delete file *****");
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::deleteItem(testAbsolutePath, ioError);
Utility::msleep(1000); // Wait 1sec
@@ -232,8 +232,12 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDuplicateFileNames() {
_syncPal->_localFSObserverWorker = slowObserver;
_syncPal->_localFSObserverWorker->start();
+ auto localFSO = std::dynamic_pointer_cast(_syncPal->_localFSObserverWorker);
+ CPPUNIT_ASSERT(localFSO);
+
int count = 0;
- while (!_syncPal->snapshot(ReplicaSide::Local)->isValid()) { // Wait for the snapshot generation
+ while (!_syncPal->snapshot(ReplicaSide::Local)->isValid() ||
+ !localFSO->_folderWatcher->isReady()) { // Wait for the snapshot generation
Utility::msleep(100);
CPPUNIT_ASSERT(count++ < 20); // Do not wait more than 2s
}
@@ -295,7 +299,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDirs() {
LOGW_DEBUG(_logger, L"***** test move dir *****");
SyncPath sourcePath = testAbsolutePath;
SyncPath destinationPath = _subDirPath / dirname;
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::moveItem(sourcePath, destinationPath, ioError);
Utility::msleep(1000); // Wait 1sec
@@ -312,7 +316,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDirs() {
LOGW_DEBUG(_logger, L"***** test rename dir *****");
SyncPath sourcePath = testAbsolutePath;
SyncPath destinationPath = _subDirPath / (dirname + Str("_renamed"));
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::renameItem(sourcePath, destinationPath, ioError);
Utility::msleep(1000); // Wait 1sec
@@ -325,7 +329,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDirs() {
// Generate test item outside sync folder
SyncName dirName = Str("dir_copy");
SyncPath sourcePath = _tempDir.path() / dirName;
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::copyFileOrDirectory(_subDirPath, sourcePath, ioError);
/// Move dir from outside sync dir
@@ -351,18 +355,14 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDirs() {
void TestLocalFileSystemObserverWorker::testLFSODeleteDir() {
- NodeId itemId;
- {
- /// Delete dir and all its content
- LOGW_DEBUG(_logger, L"***** test delete dir *****");
- IoError ioError = IoError::Unknown;
- IoHelper::deleteItem(_subDirPath, ioError);
+ /// Delete dir and all its content
+ LOGW_DEBUG(_logger, L"***** test delete dir *****");
+ auto ioError = IoError::Unknown;
+ IoHelper::deleteItem(_subDirPath, ioError);
- Utility::msleep(1000); // Wait 1sec
+ Utility::msleep(1000); // Wait 1sec
- CPPUNIT_ASSERT(!_syncPal->snapshot(ReplicaSide::Local)->exists(itemId));
- CPPUNIT_ASSERT(!_syncPal->snapshot(ReplicaSide::Local)->exists(_testFiles[0].first));
- }
+ CPPUNIT_ASSERT(!_syncPal->snapshot(ReplicaSide::Local)->exists(_testFiles[0].first));
LOGW_DEBUG(_logger, L"***** Tests for copy and deletion of directories succesfully finished! *****");
}
@@ -379,7 +379,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithSpecialCases1() {
IoHelper::getFileStat(sourcePath, &fileStat, exists);
NodeId initItemId = std::to_string(fileStat.inode);
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::deleteItem(sourcePath, ioError);
//// create
KDC::testhelpers::generateOrEditTestFile(sourcePath);
@@ -410,7 +410,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithSpecialCases2() {
IoHelper::getFileStat(sourcePath, &fileStat, exists);
NodeId initItemId = std::to_string(fileStat.inode);
- IoError ioError = IoError::Unknown;
+ auto ioError = IoError::Unknown;
IoHelper::moveItem(sourcePath, destinationPath, ioError);
//// create
KDC::testhelpers::generateOrEditTestFile(sourcePath);
@@ -429,7 +429,7 @@ void TestLocalFileSystemObserverWorker::testLFSOWithSpecialCases2() {
}
void TestLocalFileSystemObserverWorker::testLFSOFastMoveDeleteMove() { // MS Office test
- LOGW_DEBUG(_logger, L"***** Test fast move/delete *****");
+ LOGW_DEBUG(_logger, L"***** Test fast move/delete *****")
_syncPal->_localFSObserverWorker->stop();
_syncPal->_localFSObserverWorker->waitForExit();
_syncPal->_localFSObserverWorker.reset();
@@ -439,15 +439,19 @@ void TestLocalFileSystemObserverWorker::testLFSOFastMoveDeleteMove() { // MS Off
_syncPal->_localFSObserverWorker = slowObserver;
_syncPal->_localFSObserverWorker->start();
+ auto localFSO = std::dynamic_pointer_cast(_syncPal->_localFSObserverWorker);
+ CPPUNIT_ASSERT(localFSO);
+
int count = 0;
- while (!_syncPal->snapshot(ReplicaSide::Local)->isValid()) { // Wait for the snapshot generation
+ while (!_syncPal->snapshot(ReplicaSide::Local)->isValid() ||
+ !localFSO->_folderWatcher->isReady()) { // Wait for the snapshot generation and folder watcher start
Utility::msleep(100);
CPPUNIT_ASSERT(count++ < 20); // Do not wait more than 2s
}
CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(_testFiles[0].first));
- IoError ioError = IoError::Unknown;
- SyncPath destinationPath = _testFiles[0].second.parent_path() / (_testFiles[0].second.filename().string() + "2");
+ auto ioError = IoError::Unknown;
+ const SyncPath destinationPath = _testFiles[0].second.parent_path() / (_testFiles[0].second.filename().string() + "2");
CPPUNIT_ASSERT(IoHelper::renameItem(_testFiles[0].second, destinationPath, ioError)); // test0.txt -> test0.txt2
CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError);
CPPUNIT_ASSERT(IoHelper::deleteItem(destinationPath, ioError)); // Delete test0.txt2 (before the previous rename is processed)
@@ -456,6 +460,8 @@ void TestLocalFileSystemObserverWorker::testLFSOFastMoveDeleteMove() { // MS Off
ioError)); // test1.txt -> test0.txt (before the previous rename and delete is processed)
CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError);
+ LOG_DEBUG(_logger, "Operations finished")
+
slowObserver->waitForUpdate();
FileStat fileStat;
@@ -484,21 +490,26 @@ void TestLocalFileSystemObserverWorker::testLFSOFastMoveDeleteMoveWithEncodingCh
FileStat fileStat;
bool exists = false;
+ auto localFSO = std::dynamic_pointer_cast(_syncPal->_localFSObserverWorker);
+ CPPUNIT_ASSERT(localFSO);
+
+ while (!_syncPal->snapshot(ReplicaSide::Local)->isValid() ||
+ !localFSO->_folderWatcher->isReady()) { // Wait for the snapshot generation
+ Utility::msleep(100);
+ CPPUNIT_ASSERT(count++ < 20); // Do not wait more than 2s
+ }
+
// Create an NFC encoded file.
SyncPath tmpDirPath = _testFiles[0].second.parent_path();
SyncPath nfcFilePath = tmpDirPath / makeNfcSyncName();
generateOrEditTestFile(nfcFilePath);
- NodeId nfcFileId;
IoHelper::getFileStat(nfcFilePath, &fileStat, exists);
- nfcFileId = std::to_string(fileStat.inode);
+ NodeId nfcFileId = std::to_string(fileStat.inode);
// Prepare the path of the NFD encoded file.
SyncPath nfdFilePath = tmpDirPath / makeNfdSyncName();
- while (!_syncPal->snapshot(ReplicaSide::Local)->isValid()) { // Wait for the snapshot generation
- Utility::msleep(100);
- CPPUNIT_ASSERT(count++ < 20); // Do not wait more than 2s
- }
+ slowObserver->waitForUpdate();
CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(nfcFileId));
@@ -522,9 +533,28 @@ void TestLocalFileSystemObserverWorker::testLFSOFastMoveDeleteMoveWithEncodingCh
CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(nfdFileId));
}
-void MockLocalFileSystemObserverWorker::waitForUpdate(long long timeoutMs) const {
+void TestLocalFileSystemObserverWorker::testInvalidateCounter() {
+ _syncPal->_localFSObserverWorker->invalidateSnapshot();
+ CPPUNIT_ASSERT_EQUAL(true, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot is not invalidated yet.
+ _syncPal->_localFSObserverWorker->invalidateSnapshot();
+ CPPUNIT_ASSERT_EQUAL(true, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot is not invalidated yet.
+ _syncPal->_localFSObserverWorker->invalidateSnapshot();
+ CPPUNIT_ASSERT_EQUAL(false, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot has been invalidated.
+
+ Utility::msleep(1000); // Wait for the snapshot to be rebuilt
+
+ CPPUNIT_ASSERT_EQUAL(true, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot is now valid again.
+ _syncPal->_localFSObserverWorker->invalidateSnapshot();
+ CPPUNIT_ASSERT_EQUAL(true, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot is not invalidated yet.
+ _syncPal->_localFSObserverWorker->invalidateSnapshot();
+ CPPUNIT_ASSERT_EQUAL(true, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot is not invalidated yet.
+ _syncPal->_localFSObserverWorker->invalidateSnapshot();
+ CPPUNIT_ASSERT_EQUAL(false, _syncPal->snapshot(ReplicaSide::Local)->isValid()); // Snapshot has been invalidated.
+}
+
+void MockLocalFileSystemObserverWorker::waitForUpdate(const long long timeoutMs) const {
using namespace std::chrono;
- auto start = system_clock::now();
+ const auto start = system_clock::now();
while (!_updating && duration_cast(system_clock::now() - start).count() < timeoutMs) {
Utility::msleep(10);
}
diff --git a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h
index 75a59e75a..e831834fa 100644
--- a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h
+++ b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h
@@ -73,6 +73,7 @@ class TestLocalFileSystemObserverWorker : public CppUnit::TestFixture {
CPPUNIT_TEST(testLFSOFastMoveDeleteMoveWithEncodingChange);
CPPUNIT_TEST(testLFSOWithSpecialCases1);
CPPUNIT_TEST(testLFSOWithSpecialCases2);
+ CPPUNIT_TEST(testInvalidateCounter);
CPPUNIT_TEST_SUITE_END();
public:
@@ -97,6 +98,7 @@ class TestLocalFileSystemObserverWorker : public CppUnit::TestFixture {
void testLFSOFastMoveDeleteMoveWithEncodingChange();
void testLFSOWithSpecialCases1();
void testLFSOWithSpecialCases2();
+ void testInvalidateCounter();
static bool vfsStatus(int, const SyncPath &, bool &, bool &, bool &, int &) { return true; };
static bool vfsPinState(int, const SyncPath &, PinState &) { return true; };
diff --git a/test/server/updater/testupdatechecker.cpp b/test/server/updater/testupdatechecker.cpp
index a8de9e0ae..f4c838144 100644
--- a/test/server/updater/testupdatechecker.cpp
+++ b/test/server/updater/testupdatechecker.cpp
@@ -21,15 +21,67 @@
#include "jobs/jobmanager.h"
#include "jobs/network/getappversionjob.h"
#include "libcommon/utility/utility.h"
+#include "requests/parameterscache.h"
#include "server/updater/updatechecker.h"
#include "utility/utility.h"
namespace KDC {
-static const std::string bigVersionJsonUpdateStr =
- R"({"result":"success","data":{"application_id":27,"prod_version":"production","version":{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},"application":{"id":27,"name":"com.infomaniak.drive","platform":"mac-os","store":"kStore","api_id":"com.infomaniak.drive","min_version":"99.99.99","next_version_rate":0,"published_versions":[{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:12","version_changelog":"test","type":"beta","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:05:44","version_changelog":"test","type":"internal","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:03:29","version_changelog":"test","type":"production-next","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]}]}}})";
-static const std::string smallVersionJsonUpdateStr =
- R"({"result":"success","data":{"application_id":27,"prod_version":"production","version":{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},"application":{"id":27,"name":"com.infomaniak.drive","platform":"mac-os","store":"kStore","api_id":"com.infomaniak.drive","min_version":"1.1.1","next_version_rate":0,"published_versions":[{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:12","version_changelog":"test","type":"beta","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:05:44","version_changelog":"test","type":"internal","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:03:29","version_changelog":"test","type":"production-next","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]}]}}})";
+static const std::string highTagValue = "99.99.99";
+static constexpr uint64_t highBuildVersionValue = 21240604;
+static const std::string mediumTagValue = "55.55.55";
+static constexpr uint64_t mediumBuildVersionValue = 20240604;
+static const std::string lowTagValue = "1.1.1";
+static constexpr uint64_t lowBuildVersionValue = 20200604;
+
+std::string generateJsonReply(const std::string &tag, uint64_t buildVersion) {
+ Poco::JSON::Object versionObj;
+ versionObj.set("tag", tag);
+ versionObj.set("tag_updated_at", "2020-06-04 15:06:37");
+ versionObj.set("version_changelog", "test");
+ versionObj.set("type", "production");
+ versionObj.set("build_version", buildVersion);
+ versionObj.set("build_min_os_version", "XXXX");
+ versionObj.set("download_link", "test");
+
+ Poco::JSON::Array publishedVersionsArray;
+ for (const auto channel:
+ {DistributionChannel::Prod, DistributionChannel::Next, DistributionChannel::Beta, DistributionChannel::Internal}) {
+ Poco::JSON::Object tmpObj;
+ tmpObj.set("tag", tag);
+ tmpObj.set("tag_updated_at", "2020-06-04 15:06:37");
+ tmpObj.set("version_changelog", "test");
+ tmpObj.set("type", GetAppVersionJob::toStr(channel));
+ tmpObj.set("build_version", buildVersion);
+ tmpObj.set("build_min_os_version", "XXXX");
+ tmpObj.set("download_link", "test");
+ publishedVersionsArray.add(tmpObj);
+ }
+
+ Poco::JSON::Object applicationObj;
+ applicationObj.set("id", "27");
+ applicationObj.set("name", "com.infomaniak.drive");
+ applicationObj.set("platform", "mac-os");
+ applicationObj.set("store", "kStore");
+ applicationObj.set("api_id", "com.infomaniak.drive");
+ applicationObj.set("min_version", "3.6.2");
+ applicationObj.set("next_version_rate", "0");
+ applicationObj.set("published_versions", publishedVersionsArray);
+
+ Poco::JSON::Object dataObj;
+ dataObj.set("application_id", "27");
+ dataObj.set("prod_version", "production");
+ dataObj.set("version", versionObj);
+ dataObj.set("application", applicationObj);
+
+ Poco::JSON::Object mainObj;
+ mainObj.set("result", "success");
+ mainObj.set("data", dataObj);
+
+ std::ostringstream out;
+ mainObj.stringify(out);
+ return out.str();
+}
class GetAppVersionJobTest final : public GetAppVersionJob {
public:
@@ -37,9 +89,12 @@ class GetAppVersionJobTest final : public GetAppVersionJob {
GetAppVersionJob(platform, appID), _updateShouldBeAvailable(updateShouldBeAvailable) {}
void runJob() noexcept override {
- const std::istringstream iss(_updateShouldBeAvailable ? bigVersionJsonUpdateStr : smallVersionJsonUpdateStr);
+ const auto str = _updateShouldBeAvailable ? generateJsonReply(highTagValue, highBuildVersionValue)
+ : generateJsonReply(lowTagValue, lowBuildVersionValue);
+ const std::istringstream iss(str);
std::istream is(iss.rdbuf());
GetAppVersionJob::handleResponse(is);
+ _exitCode = ExitCode::Ok;
}
private:
@@ -60,15 +115,19 @@ class UpdateCheckerTest final : public UpdateChecker {
bool _updateShouldBeAvailable{false};
};
+void TestUpdateChecker::setUp() {
+ ParametersCache::instance(true);
+}
+
void TestUpdateChecker::testCheckUpdateAvailable() {
// Version is higher than current version
{
UpdateCheckerTest testObj;
UniqueId jobId = 0;
testObj.setUpdateShoudBeAvailable(true);
- testObj.checkUpdateAvailability(DistributionChannel::Internal, &jobId);
+ testObj.checkUpdateAvailability(&jobId);
while (!JobManager::instance()->isJobFinished(jobId)) Utility::msleep(10);
- CPPUNIT_ASSERT(testObj.versionInfo().isValid());
+ CPPUNIT_ASSERT(testObj.versionInfo(DistributionChannel::Beta).isValid());
}
// Version is lower than current version
@@ -76,10 +135,102 @@ void TestUpdateChecker::testCheckUpdateAvailable() {
UpdateCheckerTest testObj;
UniqueId jobId = 0;
testObj.setUpdateShoudBeAvailable(false);
- testObj.checkUpdateAvailability(DistributionChannel::Internal, &jobId);
+ testObj.checkUpdateAvailability(&jobId);
while (!JobManager::instance()->isJobFinished(jobId)) Utility::msleep(10);
- CPPUNIT_ASSERT(testObj.versionInfo().isValid());
+ CPPUNIT_ASSERT(testObj.versionInfo(DistributionChannel::Beta).isValid());
}
}
+enum VersionValue { High, Medium, Low };
+
+const std::string &tag(const VersionValue versionNumber) {
+ switch (versionNumber) {
+ case High:
+ return highTagValue;
+ case Medium:
+ return mediumTagValue;
+ case Low:
+ return lowTagValue;
+ }
+ return lowTagValue;
+}
+
+uint64_t buildVersion(const VersionValue versionNumber) {
+ switch (versionNumber) {
+ case High:
+ return highBuildVersionValue;
+ case Medium:
+ return mediumBuildVersionValue;
+ case Low:
+ return lowBuildVersionValue;
+ }
+ return lowBuildVersionValue;
+}
+
+VersionInfo getVersionInfo(const DistributionChannel channel, const VersionValue versionNumber) {
+ VersionInfo versionInfo;
+ versionInfo.channel = channel;
+ versionInfo.tag = tag(versionNumber);
+ versionInfo.buildVersion = buildVersion(versionNumber);
+ versionInfo.downloadUrl = "test";
+ return versionInfo;
+}
+
+void TestUpdateChecker::testVersionInfo() {
+ UpdateChecker testObj;
+ testObj._prodVersionChannel = DistributionChannel::Prod;
+
+ auto testFunc = [&testObj](const VersionValue expectedValue, const DistributionChannel expectedChannel,
+ const DistributionChannel selectedChannel, const std::vector &versionsNumber,
+ const CPPUNIT_NS::SourceLine &sourceline) {
+ testObj._versionsInfo.clear();
+ testObj._versionsInfo.try_emplace(DistributionChannel::Prod,
+ getVersionInfo(DistributionChannel::Prod, versionsNumber[0]));
+ testObj._versionsInfo.try_emplace(DistributionChannel::Beta,
+ getVersionInfo(DistributionChannel::Beta, versionsNumber[1]));
+ testObj._versionsInfo.try_emplace(DistributionChannel::Internal,
+ getVersionInfo(DistributionChannel::Internal, versionsNumber[2]));
+ const auto &versionInfo = testObj.versionInfo(selectedChannel);
+ CPPUNIT_NS::assertEquals(expectedChannel, versionInfo.channel, sourceline, "");
+ CPPUNIT_NS::assertEquals(tag(expectedValue), versionInfo.tag, sourceline, "");
+ CPPUNIT_NS::assertEquals(buildVersion(expectedValue), versionInfo.buildVersion, sourceline, "");
+ };
+
+ // selected version: Prod
+ /// versions values: Prod > Beta > Internal
+ testFunc(High, DistributionChannel::Prod, DistributionChannel::Prod, {High, Medium, Low}, CPPUNIT_SOURCELINE());
+ /// versions values: Internal > Beta > Prod
+ testFunc(Low, DistributionChannel::Prod, DistributionChannel::Prod, {Low, Medium, High}, CPPUNIT_SOURCELINE());
+ /// versions values: Prod == Beta == Internal
+ testFunc(Medium, DistributionChannel::Prod, DistributionChannel::Prod, {Medium, Medium, Medium}, CPPUNIT_SOURCELINE());
+
+ // selected version: Beta
+ /// versions values: Prod > Beta > Internal
+ testFunc(High, DistributionChannel::Prod, DistributionChannel::Beta, {High, Medium, Low}, CPPUNIT_SOURCELINE());
+ /// versions values: Internal > Beta > Prod
+ testFunc(Medium, DistributionChannel::Beta, DistributionChannel::Beta, {Low, Medium, High}, CPPUNIT_SOURCELINE());
+ /// versions values: Prod == Beta == Internal
+ testFunc(Medium, DistributionChannel::Prod, DistributionChannel::Beta, {Medium, Medium, Medium}, CPPUNIT_SOURCELINE());
+
+ // selected version: Internal
+ /// versions values: Prod > Beta > Internal
+ testFunc(High, DistributionChannel::Prod, DistributionChannel::Internal, {High, Medium, Low}, CPPUNIT_SOURCELINE());
+ /// versions values: Internal > Beta > Prod
+ testFunc(High, DistributionChannel::Internal, DistributionChannel::Internal, {Low, Medium, High}, CPPUNIT_SOURCELINE());
+ /// versions values: Beta > Prod > Internal
+ testFunc(High, DistributionChannel::Beta, DistributionChannel::Internal, {Medium, High, Low}, CPPUNIT_SOURCELINE());
+ /// versions values: Prod > Internal > Beta
+ testFunc(High, DistributionChannel::Prod, DistributionChannel::Internal, {High, Low, Medium}, CPPUNIT_SOURCELINE());
+ /// versions values: Beta > Internal > Prod
+ testFunc(High, DistributionChannel::Beta, DistributionChannel::Internal, {Low, High, Medium}, CPPUNIT_SOURCELINE());
+ /// versions values: Prod == Beta == Internal
+ testFunc(Medium, DistributionChannel::Prod, DistributionChannel::Internal, {Medium, Medium, Medium}, CPPUNIT_SOURCELINE());
+ /// versions values: Beta == Prod > Internal
+ testFunc(High, DistributionChannel::Prod, DistributionChannel::Internal, {High, High, Low}, CPPUNIT_SOURCELINE());
+ /// versions values: Beta == Internal > Prod
+ testFunc(Medium, DistributionChannel::Beta, DistributionChannel::Internal, {Low, Medium, Medium}, CPPUNIT_SOURCELINE());
+ /// versions values: Prod == Internal > Beta
+ testFunc(Medium, DistributionChannel::Prod, DistributionChannel::Internal, {Medium, Low, Medium}, CPPUNIT_SOURCELINE());
+}
+
} // namespace KDC
diff --git a/test/server/updater/testupdatechecker.h b/test/server/updater/testupdatechecker.h
index 63662b07b..dac0898db 100644
--- a/test/server/updater/testupdatechecker.h
+++ b/test/server/updater/testupdatechecker.h
@@ -26,10 +26,15 @@ class TestUpdateChecker final : public CppUnit::TestFixture {
public:
CPPUNIT_TEST_SUITE(TestUpdateChecker);
CPPUNIT_TEST(testCheckUpdateAvailable);
+ CPPUNIT_TEST(testVersionInfo);
CPPUNIT_TEST_SUITE_END();
+ public:
+ void setUp() override;
+
protected:
void testCheckUpdateAvailable();
+ void testVersionInfo();
};
} // namespace KDC
diff --git a/translations/client_de.ts b/translations/client_de.ts
index 13091c170..99c88ec31 100644
--- a/translations/client_de.ts
+++ b/translations/client_de.ts
@@ -178,15 +178,6 @@
ABSCHLIESSEN
-
-
-
- Dieser Ordner ist nicht mit Lite Sync kompatibel.<br>
-Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync deaktiviert.<br>
-<a style="%1" href="%2">Mehr erfahren</a>
-
@@ -202,6 +193,15 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
Alle Ihre Dateien befinden sich in diesem Ordner, sobald die Einrichtung abgeschlossen ist. Sie können dort neue Dateien ablegen und mit Ihrem kDrive synchronisieren.
+
+
+
+ Dieser Ordner ist nicht mit Lite Sync kompatibel.<br>
+Bitte wählen Sie einen anderen Ordner. Wenn Sie fortfahren, wird Lite Sync deaktiviert.<br>
+<a style="%1" href="%2">Weitere Informationen</a>
+
@@ -280,7 +280,7 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
Der kDrive Client läuft bereits!
-
+
Der Benutzer %1 ist nicht angemeldet. Bitte melden Sie sich erneut an.
@@ -288,12 +288,12 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
KDC::AppServer
-
+
Die kDrive-Anwendung läuft bereits!
-
+
%1 und %n andere Datei(en) wurde(n) gelöscht.
@@ -301,13 +301,13 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
-
+
%1 names a file.
%1 wurde gelöscht.
-
+
%1 und %n andere Datei(en) wurde(n) hinzugefügt.
@@ -315,13 +315,13 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
-
+
%1 names a file.
%1 wurde hinzugefügt.
-
+
%1 und %n andere Datei(en) wurde(n) aktualisiert.
@@ -329,13 +329,13 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
-
+
%1 names a file.
%1 wurde aktualisiert.
-
+
%1 wurde zu %2 verschoben und %n andere Datei(en) wurde(n) verschoben.
@@ -343,17 +343,17 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
-
+
%1 wurde zu %2 verschoben.
-
+
Synchronisierungsaktivität
-
+
Ein neuer Ordner mit einer Grösse von mehr als %1 MB wurde zum Laufwerk %2 hinzugefügt, Sie müssen die Synchronisierung überprüfen: %3.
@@ -368,6 +368,74 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
Aktuell befinden sich keine Unterordner auf dem Server.
+
+ KDC::BetaProgramDialog
+
+
+
+ Beenden Sie das Beta-Programm
+
+
+
+
+ Nehmen Sie am Beta-Programm teil
+
+
+
+
+ Erhalten Sie frühzeitigen Zugang zu neuen Versionen der Anwendung, bevor sie für die Allgemeinheit freigegeben werden, und beteiligen Sie sich an der Verbesserung der Anwendung, indem Sie uns Ihre Kommentare schicken.
+
+
+
+
+ Profitieren Sie von Beta-Updates für Anwendungen
+
+
+
+
+ Nein
+
+
+
+
+ Öffentliche Beta-Version
+
+
+
+
+ Interne Betaversion
+
+
+
+
+ Ich verstehe
+
+
+
+
+ Sind Sie sicher, dass Sie das Beta-Programm verlassen wollen?
+
+
+
+
+ Speichern
+
+
+
+
+ Abbrechen
+
+
+
+
+ Ihre aktuelle Version der Anwendung ist möglicherweise zu aktuell. Ihre Auswahl wird ab dem nächsten verfügbaren Update wirksam.
+
+
+
+
+ Beta-Versionen können unerwartet verlassen werden oder Instabilitäten verursachen.
+
+
KDC::BigFoldersDialog
@@ -411,17 +479,17 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten:
Es wurden keine Synchronisierungsordner konfiguriert.
-
+
Synthese
-
+
Einstellungen
-
+
Beenden
@@ -466,22 +534,22 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten:
%1 (Synchronisierung wurde angehalten)
-
+
Möchten Sie die Synchronisierungen des Kontos <i>%1</i> wirklich entfernen?<br><b>Hinweis:</b> Dadurch werden <b>keine</b> Dateien gelöscht.
-
+
ALLE SYNC. ENTFERNEN
-
+
ABBRECHEN
-
+
Synchronisierungen konnten nicht gestartet werden!
@@ -1221,57 +1289,68 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten:
KDC::FixConflictingFilesDialog
-
+
+
Link %1 kann nicht geöffnet werden.
-
+
Konflikt(e) lösen
-
-
- <b>Was möchten Sie mit %1 in Konflikt stehenden Elementen tun, die nicht in kDrive synchronisiert sind?</b>
+
+
+ <b>Was möchten Sie mit %1 in Konflikt stehenden Elementen tun?</b>
-
-
- Synchronisieren Sie die lokale Version meiner Artikel in kDrive.
+
+
+ Meine Änderungen speichern und die Versionen anderer Benutzer ersetzen.
-
-
- Verschieben Sie die lokale Version meiner Artikel in den Papierkorb des Computers.
+
+
+ Meine Änderungen rückgängig machen und die Versionen anderer Benutzer behalten.
-
-
- Löschen Sie die lokale Version meiner Artikel dauerhaft.
+
+
+ Ihre Änderungen können unwiederbringlich gelöscht werden. Sie können über die kDrive-Webanwendung nicht wiederhergestellt werden.
-
+
+
+ <a style=%1 href="%2">Mehr erfahren</a>
+
+
+
+
+ Ihre Änderungen werden dauerhaft gelöscht. Sie können über die kDrive-Webanwendung nicht wiederhergestellt werden.
+
+
+
Artikel anzeigen
-
+
BESTÄTIGEN
-
+
ABBRECHEN
-
-
- Wenn ein Element sowohl auf dem Computer als auch auf kDrive geändert wurde oder wenn auf dem Computer ein Element mit einem Namen erstellt wurde, der bereits auf kDrive vorhanden ist, benennt kDrive Ihr lokales Element um und lädt die kDrive-Version auf Ihren Computer herunter, um es nicht zu verlieren beliebige Daten.<br>
+
+
+ An diesen Dateien wurden von mehreren Benutzern an mehreren Orten (online auf kDrive, auf einem Computer oder einem Mobiltelefon) Änderungen vorgenommen. Ordner, die diese Dateien enthalten, wurden möglicherweise auch gelöscht.<br>
-
+
Die lokale Version Ihres Artikels ist <b>nicht mit kDrive synchronisiert</b>. <a style="color: #489EF3" href="%1">Weitere Informationen</a>
@@ -1351,12 +1430,12 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten:
KDC::LargeFolderConfirmation
-
+
Um Bestätigung bitten, bevor Ordner synchronisiert werden, die grösser sind als
-
+
MB
@@ -1470,8 +1549,8 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten:
Please select another folder. If you continue Lite Sync will be disabled.<br>
<a style="%1" href="%2">Learn more</a>
Dieser Ordner ist nicht mit Lite Sync kompatibel.<br>
-Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync deaktiviert.<br>
-<a style="%1" href="%2">Mehr erfahren</a>
+Bitte wählen Sie einen anderen Ordner. Wenn Sie fortfahren, wird Lite Sync deaktiviert.<br>
+<a style="%1" href="%2">Weitere Informationen</a>
@@ -1487,12 +1566,12 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
KDC::Logger
-
+
Fehler
-
+
<nobr>File '%1'<br/>kann nicht zum Schreiben geöffnet werden.<br/><br/>Die Log-Datei kann <b>nicht</b> gespeichert werden!</nobr>
@@ -1513,24 +1592,24 @@ Bitte wählen Sie einen anderen Ordner aus. Wenn Sie fortfahren, wird Lite Sync
KDC::ParametersDialog
-
+
Ordnerpfad %1 kann nicht geöffnet werden.
-
+
Übertragung abgeschlossen!<br>Weitere Einzelheiten enthält der Identifier <b>%1</b> in den Fehlerberichten.
-
+
Übertragung fehlgeschlagen!
Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu senden: <a style="%1" href="%2">%2</a>
-
+
Kein kDrive eingerichtet!
@@ -1781,66 +1860,61 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
Der für diesen Artikel durchgeführte Vorgang ist fehlgeschlagen.<br>Der Artikel wurde vorübergehend auf die schwarze Liste gesetzt.
-
-
-
- Verschieben in den Papierkorb fehlgeschlagen.
-
Die Datei ist zu groß, um hochgeladen zu werden. Sie wurde vorübergehend auf die schwarze Liste gesetzt.
+
+
+
+ Es ist nicht möglich, die Datei herunterzuladen.
+
+
+
+
+ Sie haben Ihr Kontingent überschritten. Erhöhen Sie Ihr Speicherplatzkontingent, um den Datei-Upload wieder zu aktivieren.
+
+
+
+
+ Auf den Synchronisationsordner kann nicht zugegriffen werden (Fehler %1).<br>Bitte überprüfen Sie, ob Sie Lese- und Schreibrechte für diesen Ordner haben.
+
- Ein vorhandenes Element hat einen identischen Namen mit denselben Groß- und Kleinschreibungsoptionen (gleiche Groß- und Kleinbuchstaben).<br>Es wurde vorübergehend auf die schwarze Liste gesetzt.
+ Ein vorhandenes Element hat einen identischen Namen mit derselben Groß- und Kleinschreibung (dieselbe Groß- und Kleinschreibung).<br>Es wurde vorübergehend auf die schwarze Liste gesetzt.
- Der Elementname enthält ein nicht unterstütztes Zeichen.<br>Es wurde vorübergehend auf die schwarze Liste gesetzt.
+ Der Artikelname enthält ein nicht unterstütztes Zeichen.<br>Er wurde vorübergehend auf die schwarze Liste gesetzt.
- Dieser Elementname ist von Ihrem Betriebssystem reserviert.<br>Es wurde vorübergehend auf die schwarze Liste gesetzt.
+ Dieser Elementname ist von Ihrem Betriebssystem reserviert.<br>Er wurde vorübergehend auf die schwarze Liste gesetzt.
- Der Elementname ist zu lang.<br>Es wurde vorübergehend auf die schwarze Liste gesetzt.
+ Der Artikelname ist zu lang.<br>Er wurde vorübergehend auf die schwarze Liste gesetzt.
- Der Elementpfad ist zu lang.<br>Es wurde ignoriert.
+ Der Elementpfad ist zu lang.<br>Er wurde ignoriert.
- Der Elementname enthält ein aktuelles UNICODE-Zeichen, das von Ihrem Dateisystem noch nicht unterstützt wird.<br>Es wurde von der Synchronisierung ausgeschlossen.
+ Der Elementname enthält ein neues UNICODE-Zeichen, das von Ihrem Dateisystem noch nicht unterstützt wird.<br>Es wurde von der Synchronisierung ausgeschlossen.
Der Elementname stimmt mit dem Namen eines anderen Elements im selben Verzeichnis überein.<br>Es wurde vorübergehend auf die schwarze Liste gesetzt. Erwägen Sie, doppelte Elemente zu entfernen.
-
-
-
- Es ist nicht möglich, die Datei herunterzuladen.
-
-
-
-
- Sie haben Ihr Kontingent überschritten. Erhöhen Sie Ihr Speicherplatzkontingent, um den Datei-Upload wieder zu aktivieren.
-
-
-
-
- Auf den Synchronisationsordner kann nicht zugegriffen werden (Fehler %1).<br>Bitte überprüfen Sie, ob Sie Lese- und Schreibrechte für diesen Ordner haben.
-
@@ -1863,7 +1937,7 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
-
+
Synchronisierungsfehler.
@@ -1873,12 +1947,12 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
Zugriff auf Element nicht möglich.<br>Bitte korrigieren Sie die Lese- und Schreibberechtigungen.
-
+
Systemfehler.
-
+
Es ist ein technischer Fehler aufgetreten.<br>Bitte leeren Sie den Verlauf. Wenn der Fehler weiterhin besteht, wenden Sie sich an unser Support-Team.
@@ -1907,122 +1981,137 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
KDC::PreferencesWidget
-
+
Allgemein
-
+
Dunkles Thema aktivieren
-
+
Einfarbige Schaltflächen aktivieren
-
+
kDrive beim Start öffnen
-
-
- Verschiebe gelöschte Dateien in den Papierkorb
-
-
-
+
Erweitert
-
+
Debugging-Informationen
-
+
<a style="%1" href="%2">Debugging-Ordner öffnen</a>
-
+
Auszuschliessende Dateien
-
+
Proxy-Server
-
+
Ordner %1 kann nicht geöffnet werden.
-
+
Link %1 kann nicht geöffnet werden.
-
+
Ungültiger Link %1.
-
+
Synchronisierte Ordner im Navigationsbereich des Datei-Explorers anzeigen
-
+
Sie müssen Ihre geöffneten Datei-Explorer neu starten, damit diese Änderung wirksam wird.
-
+
Lite Sync
-
+
Sprache
-
+
Ein Prozess ist fehlgeschlagen.
-
+
+
+ Gelöschte Dateien in den Papierkorb meines Computers verschieben
+
+
+
+
+ Einige Dateien oder Ordner werden möglicherweise nicht in den Papierkorb des Computers verschoben.
+
+
+
+
+ Sie können bereits synchronisierte Dateien jederzeit aus dem Papierkorb der kDrive-Webanwendung abrufen.
+
+
+
+
+ <a style=%1 href="%2">Mehr erfahren</a>
+
+
+
Englisch
-
+
Französisch
-
+
Deutsch
-
+
Spanisch
-
+
Italienisch
-
+
Standard
@@ -2208,35 +2297,35 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
KDC::SocketApi
-
-
-
+
+
+
Link zur privaten Freigabe kopieren
-
-
+
+
Die erneute Freigabe dieser Datei ist nicht erlaubt
-
-
+
+
Die erneute Freigabe dieses Ordners ist nicht erlaubt
-
-
-
-
+
+
+
+
Öffentlichen Freigabelink kopieren
-
-
+
+
Im Browser öffnen
@@ -2360,43 +2449,48 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
Für 1 weitere Woche
-
+
+
+ Senden Sie Feedback
+
+
+
Aktualisieren Sie die kDrive-App
-
+
Diese kDrive-App-Version wird nicht mehr unterstützt. Um auf die neuesten Funktionen und Verbesserungen zuzugreifen, aktualisieren Sie bitte.
-
-
+
+
Aktualisieren
-
+
Bitte laden Sie die neueste Version auf der Website herunter.
-
+
Update-Download läuft
-
+
Auf der Suche nach Updates...
-
+
Manuelles Update
-
+
Nicht verfügbar
@@ -2406,27 +2500,27 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
kDrive beenden
-
+
Fehler und Informationen anzeigen
-
+
Informationen anzeigen
-
+
Sie können Dateien <a style="%1" href="%2">von Ihrem Computer</a> oder von <a style="%1" href="%3">kdrive.infomaniak.com</a> synchronisieren.
-
+
Im Ordner öffnen
-
+
Weitere Aktionen
@@ -2446,53 +2540,53 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
Aktivität
-
-
+
+
Nicht implementiert!
-
+
Kein synchronisierter Ordner für diesen kDrive!
-
+
Kein kDrive eingerichtet!
-
+
Zugang zu Website %1 nicht möglich.
-
+
Webversion %1 öffnen
-
+
Benachrichtigungen deaktiviert bis %1
-
+
Benachrichtigungen deaktivieren
-
+
Hilfe benötigt
-
+
Link %1 kann nicht geöffnet werden.
-
+
Ungültiger Link %1.
@@ -2502,12 +2596,12 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
Ordner-URL %1 kann nicht geöffnet werden.
-
+
Drive-Einstellungen
-
+
Anwendungseinstellungen
@@ -2543,12 +2637,12 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
KDC::UpdateManager
-
+
Neues Update verfügbar.
-
+
Version %1 ist zum Download verfügbar.
@@ -2564,65 +2658,90 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
KDC::VersionWidget
-
+
<a style="%1" href="%2">%3</a>
-
+
<a style="%1" href="%2">Versionshinweis anzeigen</a>
-
+
Version
-
+
AKTUALISIEREN
-
+
%1 ist auf dem neuesten Stand!
-
+
Überprüfe Update auf dem Server...
-
+
Ein Update ist verfügbar: %1.<br>Bitte laden Sie es <a style="%2" href="%3">hier</a> herunter.
-
+
Ein Update ist verfügbar: %1
-
+
Herunterladen von %1. Bitte warten…
-
+
Es konnte nicht nach neuen Updates gesucht werden.
-
+
Während der Aktualisierung ist ein Fehler aufgetreten.
-
+
Das Update konnte nicht heruntergeladen werden.
+
+
+
+ Beta-Programm
+
+
+
+
+ Frühzeitiger Zugang zu neuen Versionen der Anwendung
+
+
+
+
+ Beitreten
+
+
+
+
+ Ändern Sie
+
+
+
+
+ Beenden
+
QObject
@@ -2647,37 +2766,37 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
Kann keinen gültigen Pfad finden
-
+
Kein gültiger Ordner ausgewählt!
-
+
Der ausgewählte Pfad existiert nicht!
-
+
Der ausgewählte Pfad ist kein Ordner!
-
+
Sie haben keine Schreibberechtigung für den ausgewählten Ordner!
-
+
Der lokale Ordner %1 enthält einen bereits synchronisierten Ordner. Bitte wählen Sie einen anderen!
-
+
Der lokale Ordner %1 befindet sich in einem bereits synchronisierten Ordner. Bitte wählen Sie einen anderen!
-
+
Der lokale Ordner %1 ist bereits auf demselben Drive synchronisiert. Bitte wählen Sie einen anderen!
@@ -2797,23 +2916,23 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send
utility
-
+
Lokalen Speicherplatz freigeben abbrechen
-
-
+
+
Lokal verfügbar machen abbrechen
-
+
Lokalen Speicherplatz freigeben
-
+
Immer lokal verfügbar machen
diff --git a/translations/client_es.ts b/translations/client_es.ts
index cd7f9561a..be158a699 100644
--- a/translations/client_es.ts
+++ b/translations/client_es.ts
@@ -178,15 +178,6 @@
FIN
-
-
-
- Este carpeta no es compatible con Lite Sync.<br>
-Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.<br>
-<a style="%1" href="%2">Aprende más</a>
-
@@ -202,6 +193,15 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
Encontrarás todos tus archivos en esta carpeta cuando se complete la configuración. Puedes arrastrar nuevos archivos para sincronizarlos en tu kDrive.
+
+
+
+ Esta carpeta no es compatible con Lite Sync.<br>
+Seleccione otra carpeta. Si continúa, Lite Sync se deshabilitará.<br>
+<a style="%1" href="%2">Más información</a>
+
@@ -280,7 +280,7 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
¡El cliente de kDrive ya se está ejecutando!
-
+
El usuario %1 no está conectado. Inicia sesión de nuevo.
@@ -288,12 +288,12 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
KDC::AppServer
-
+
¡La aplicación kDrive ya se está ejecutando!
-
+
%1 y otros %n archivo(s) han sido borrados.
@@ -301,13 +301,13 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
-
+
%1 names a file.
%1 ha sido eliminado.
-
+
%1 y % otros archivo(s) han sido añadidos.
@@ -315,13 +315,13 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
-
+
%1 names a file.
%1 ha sido añadido.
-
+
%1 y otros %n archivo(s) han sido actualizados.
@@ -329,13 +329,13 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
-
+
%1 names a file.
%1 ha sido actualizado.
-
+
%1 ha sido movido a %2 y otros %n archivo(s) han sido movidos.
@@ -343,17 +343,17 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
-
+
%1 ha sido movido a %2.
-
+
Actividad de sincronización
-
+
Se ha añadido una nueva carpeta mayor a %1 MB en el drive %2, debes validar su sincronización: %3.
@@ -368,6 +368,74 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
No hay subcarpetas actualmente en el servidor.
+
+ KDC::BetaProgramDialog
+
+
+
+ Abandonar el programa beta
+
+
+
+
+ Únase al programa beta
+
+
+
+
+ Obtenga acceso anticipado a las nuevas versiones de la aplicación antes de que se publiquen para el público en general, y participe en la mejora de la aplicación enviándonos sus comentarios.
+
+
+
+
+ Benefíciese de las actualizaciones beta de las aplicaciones
+
+
+
+
+ No
+
+
+
+
+ Versión beta pública
+
+
+
+
+ Versión beta interna
+
+
+
+
+ Comprendo
+
+
+
+
+ ¿Estás seguro de que quieres abandonar el programa beta?
+
+
+
+
+ Guardar
+
+
+
+
+ Cancelar
+
+
+
+
+ Es posible que su versión actual de la aplicación sea demasiado reciente, su elección será efectiva a partir de la próxima actualización disponible.
+
+
+
+
+ Las versiones beta pueden salir inesperadamente o causar inestabilidades.
+
+
KDC::BigFoldersDialog
@@ -411,17 +479,17 @@ Selecciona los que quieras sincronizar:
No hay carpetas de sincronización configuradas.
-
+
Síntesis
-
+
Preferencias
-
+
Salir
@@ -466,22 +534,22 @@ Selecciona los que quieras sincronizar:
%1 (sincronización en pausa)
-
+
¿Realmente desea eliminar las sincronizaciones de la cuenta <i>%1</i>?<br><b>Nota:</b> Esto <b>no</b> eliminará ningún archivo.
-
+
ELIMINAR TODAS LAS SINC.
-
+
CANCELAR
-
+
¡Error al iniciar las sincronizaciones!
@@ -1219,57 +1287,68 @@ Selecciona los que quieras sincronizar:
KDC::FixConflictingFilesDialog
-
+
+
No se puede abrir el enlace %1.
-
+
Resolver conflictos
-
-
- <b>¿Qué desea hacer con %1 elementos en conflicto que no están sincronizados en kDrive?</b>
+
+
+ <b>¿Qué desea hacer con los %1 elementos en conflicto?</b>
-
-
- Sincronizar la versión local de mis artículos en kDrive.
+
+
+ Guardar mis cambios y reemplazar las versiones de otros usuarios.
-
-
- Mueve la versión local de mis artículos a la papelera de la computadora.
+
+
+ Deshacer mis cambios y conservar las versiones de otros usuarios.
-
-
- Eliminar permanentemente la versión local de mis artículos.
+
+
+ Es posible que los cambios se eliminen de forma permanente. No se pueden restaurar desde la aplicación web de kDrive.
+
+
+
+
+ <a style=%1 href="%2">Más información</a>
-
+
+
+ Los cambios se eliminarán de forma permanente. No se podrán restaurar desde la aplicación web de kDrive.
+
+
+
Mostrar artículo(s)
-
+
VALIDAR
-
+
CANCELAR
-
-
- Cuando un elemento se modifica tanto en la computadora como en kDrive o cuando se crea un elemento en la computadora con un nombre que ya existe en kDrive, kDrive cambia el nombre de su elemento local y descarga la versión de kDrive en su computadora para no perderlo. cualquier dato.<br>
+
+
+ Varios usuarios han realizado modificaciones en estos archivos en distintos lugares (en línea, en kDrive, en un ordenador o en un dispositivo móvil). Es posible que también se hayan eliminado las carpetas que contienen estos archivos.<br>
-
+
La versión local de su elemento <b>no está sincronizada</b> con kDrive. <a style="color: #489EF3" href="%1">Más información</a>
@@ -1349,12 +1428,12 @@ Selecciona los que quieras sincronizar:
KDC::LargeFolderConfirmation
-
+
Solicitar confirmación antes de sincronizar carpetas mayores de
-
+
MB
@@ -1467,9 +1546,9 @@ Selecciona los que quieras sincronizar:
- Este carpeta no es compatible con Lite Sync.<br>
-Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.<br>
-<a style="%1" href="%2">Aprende más</a>
+ Esta carpeta no es compatible con Lite Sync.<br>
+Seleccione otra carpeta. Si continúa, Lite Sync se deshabilitará.<br>
+<a style="%1" href="%2">Más información</a>
@@ -1485,12 +1564,12 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
KDC::Logger
-
+
Error
-
+
<nobr>El archivo '%1'<br/>no se puede abrir para escritura.<br/><br/>¡El archivo de registro <b>no</b> se puede guardar!</nobr>
@@ -1511,24 +1590,24 @@ Por favor seleccione otro carpeta. Si continúa, Lite Sync será deshabilitado.&
KDC::ParametersDialog
-
+
No es posible abrir la ruta de la carpeta %1.
-
+
¡Transmisión realizada!<br>Consulta el identificador <b>%1</b> en informes de fallos.
-
+
¡La transmisión falló!
Por favor, utilice el siguiente enlace para enviar los registros al soporte: <a style="%1" href="%2">%2</a>
-
+
¡No hay ningún kDrive configurado!
@@ -1779,35 +1858,45 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
La operación realizada en este artículo ha fallado.<br>El artículo ha sido incluido temporalmente en la lista negra.
-
-
-
-
-
El archivo es demasiado grande para ser cargado. Ha sido temporalmente bloqueado.
+
+
+
+ Imposible descargar el archivo.
+
+
+
+
+ Has excedido tu cuota. Aumente su cuota de espacio para volver a habilitar la carga de archivos.
+
+
+
+
+ La carpeta de sincronización es inaccesible (error %1).<br>Por favor, compruebe que tiene acceso de lectura y escritura a esta carpeta.
+
- Un elemento existente tiene un nombre idéntico con las mismas opciones de mayúsculas y minúsculas.<br>Se ha incluido temporalmente en la lista negra.
+ Un elemento existente tiene un nombre idéntico con las mismas opciones de mayúsculas y minúsculas (mismas letras mayúsculas y minúsculas).<br>Ha sido incluido temporalmente en la lista negra.
- El nombre del elemento contiene un carácter no compatible.<br>Se ha incluido temporalmente en la lista negra.
+ El nombre del artículo contiene un carácter no compatible.<br>Se ha incluido temporalmente en la lista negra.
- El nombre de este elemento está reservado por su sistema operativo.<br>Se ha incluido temporalmente en la lista negra.
+ Este nombre de artículo está reservado por su sistema operativo.<br>Se ha incluido temporalmente en la lista negra.
- El nombre del elemento es demasiado largo.<br>Se ha incluido temporalmente en la lista negra.
+ El nombre del artículo es demasiado largo.<br>Ha sido incluido temporalmente en la lista negra.
@@ -1822,22 +1911,7 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
- El nombre del elemento coincide con el nombre de otro elemento en el mismo directorio.<br>Se ha incluido temporalmente en la lista negra. Considere eliminar los elementos duplicados.
-
-
-
-
- Imposible descargar el archivo.
-
-
-
-
- Has excedido tu cuota. Aumente su cuota de espacio para volver a habilitar la carga de archivos.
-
-
-
-
- La carpeta de sincronización es inaccesible (error %1).<br>Por favor, compruebe que tiene acceso de lectura y escritura a esta carpeta.
+ El nombre del elemento coincide con el nombre de otro elemento del mismo directorio.<br>Se ha incluido temporalmente en la lista negra. Considere eliminar los elementos duplicados.
@@ -1861,7 +1935,7 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
-
+
Error de sincronización.
@@ -1871,12 +1945,12 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
No se puede acceder al elemento.<br>Por favor, corrija los permisos de lectura y escritura.
-
+
Error del sistema.
-
+
Se ha producido un error técnico.<br>Vacíe el historial y, si el error persiste, póngase en contacto con nuestro equipo de soporte.
@@ -1905,122 +1979,137 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
KDC::PreferencesWidget
-
+
General
-
+
Activar tema oscuro
-
+
Activar iconos monocromos
-
+
Ejecutar kDrive al arrancar
-
-
- Mueve los archivos eliminados a la papelera
-
-
-
+
Avanzado
-
+
Información de depuración
-
+
<a style="%1" href="%2">Abrir carpeta de depuración</a>
-
+
Archivos a excluir
-
+
Servidor proxy
-
+
No se puede abrir la carpeta %1.
-
+
No se puede abrir el enlace %1.
-
+
Enlace %1 no válido.
-
+
Mostrar carpetas sincronizadas en el panel de navegación del Explorador de archivos
-
+
Debes reiniciar los Exploradores de archivos abiertos para que este cambio surta efecto.
-
+
Lite Sync
-
+
Idioma
-
+
Algún proceso no se ha ejecutado.
-
+
+
+ Mueva los archivos eliminados a la papelera de mi computadora
+
+
+
+
+ Es posible que algunos archivos o carpetas no se muevan a la papelera de la computadora.
+
+
+
+
+ Siempre puedes recuperar archivos ya sincronizados desde la papelera de la aplicación web kDrive.
+
+
+
+
+ <a style=%1 href="%2">Más información</a>
+
+
+
Inglés
-
+
Francés
-
+
Alemán
-
+
Español
-
+
Italiano
-
+
Predeterminado
@@ -2206,35 +2295,35 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
KDC::SocketApi
-
-
-
+
+
+
Copiar enlace para compartir privado
-
-
+
+
No está permitido volver a compartir este archivo
-
-
+
+
No está permitido volver a compartir esta carpeta
-
-
-
-
+
+
+
+
Copiar enlace para compartir públicamente
-
-
+
+
Abrir en navegador
@@ -2358,43 +2447,48 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
Por 1 semana más
-
+
+
+ Enviar comentarios
+
+
+
Actualizar la aplicación kDrive
-
+
Esta versión de la aplicación kDrive ya no es compatible. Para acceder a las últimas funciones y mejoras, actualice.
-
-
+
+
Actualizar
-
+
Descargue la última versión del sitio web.
-
+
Descarga de actualización en curso
-
+
Buscando actualización...
-
+
Actualización manual
-
+
Indisponible
@@ -2404,27 +2498,27 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
Salir de kDrive
-
+
Mostrar errores e informaciones
-
+
Mostrar información
-
+
Puedes sincronizar archivos <a style="%1" href="%2">desde tu ordenador</a> o en <a style="%1" href="%3">kdrive.infomaniak.com</a>.
-
+
Abrir en carpeta
-
+
Más acciones
@@ -2444,53 +2538,53 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
Actividad
-
-
+
+
¡No implementado!
-
+
¡No hay ninguna carpeta sincronizada para este Drive!
-
+
¡No hay ningún kDrive configurado!
-
+
No es posible acceder al sitio web %1.
-
+
Abrir la versión web de %1
-
+
Notificaciones deshabilitadas hasta %1
-
+
Deshabilitar Notificaciones
-
+
Se necesita ayuda
-
+
No se puede abrir el enlace %1.
-
+
Enlace %1 no válido.
@@ -2500,12 +2594,12 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
No es posible abrir la url de carpeta %1.
-
+
Parámetros de drive
-
+
Preferencias de aplicación
@@ -2541,12 +2635,12 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
KDC::UpdateManager
-
+
Nueva actualización disponible.
-
+
La versión %1 está disponible para su descarga.
@@ -2562,65 +2656,90 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
KDC::VersionWidget
-
+
<a style="%1" href="%2">%3</a>
-
+
<a style="%1" href="%2">Mostrar nota de lanzamiento</a>
-
+
Versión
-
+
ACTUALIZAR
-
+
¡%1 está actualizado!
-
+
Comprobando actualización en servidor...
-
+
Hay una actualización disponible: %1. Descárgala <a style="%2" href="%3">aquí</a>.
-
+
Hay disponible una actualización: %1
-
+
Descargando %1. Espera...
-
+
No se puede comprobar si hay actualizaciones.
-
+
Se ha producido un error durante la actualización.
-
+
No se ha podido descargar la actualización.
+
+
+
+ Programa Beta
+
+
+
+
+ Obtenga acceso anticipado a las nuevas versiones de la aplicación
+
+
+
+
+ Contacto
+
+
+
+
+ Modifique
+
+
+
+
+ Salir
+
QObject
@@ -2645,37 +2764,37 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
No se puede encontrar una ruta válida
-
+
¡No se ha seleccionado una carpeta válida!
-
+
¡La ruta seleccionada no existe!
-
+
¡La ruta seleccionada no es una carpeta!
-
+
¡No tienes permiso para escribir en la carpeta seleccionada!
-
+
La carpeta local %1 contiene una carpeta ya sincronizada.¡Elige otra!
-
+
La carpeta local %1 está contenida en una carpeta ya sincronizada.¡Elige otra!
-
+
La carpeta local %1 ya está sincronizada en el mismo drive.¡Elige otra!
@@ -2795,23 +2914,23 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: <
utility
-
+
Cancelar liberar espacio local
-
-
+
+
Cancelar disponible localmente
-
+
Liberar espacio local
-
+
Siempre disponible en local
diff --git a/translations/client_fr.ts b/translations/client_fr.ts
index c2ea1ad6d..542144286 100644
--- a/translations/client_fr.ts
+++ b/translations/client_fr.ts
@@ -178,15 +178,6 @@
TERMINER
-
-
-
- Ce dossier n'est pas compatible avec la Lite Sync.<br>
-Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera désactivée.<br>
-<a style="%1" href="%2">En savoir plus</a>
-
@@ -202,6 +193,15 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
Vous retrouverez tous vos fichiers dans ce dossier une fois la configuration terminée. Vous pouvez y glisser de nouveaux fichiers pour les synchroniser avec votre kDrive.
+
+
+
+ Ce dossier n'est pas compatible avec Lite Sync.<br>
+Veuillez sélectionner un autre dossier. Si vous continuez, Lite Sync sera désactivé.<br>
+<a style="%1" href="%2">En savoir plus</a>
+
@@ -280,7 +280,7 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
Le client kDrive est déjà en cours d'exécution !
-
+
L'utilisateur %1 n'est pas connecté. Veuillez vous reconnecter.
@@ -288,12 +288,12 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
KDC::AppServer
-
+
L'application kDrive est déjà en cours d'exécution!
-
+
%1 a été supprimé.
@@ -301,13 +301,13 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
-
+
%1 names a file.
%1 a été supprimé.
-
+
%1 et %n autre(s) fichier(s) ont été ajouté(s).
@@ -315,13 +315,13 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
-
+
%1 names a file.
%1 a été ajouté.
-
+
%1 a été mis à jour.
@@ -329,13 +329,13 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
-
+
%1 names a file.
%1 a été mis à jour.
-
+
%1 a été déplacé vers %2.
@@ -343,17 +343,17 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
-
+
%1 a été déplacé vers %2.
-
+
Activité de synchronisation
-
+
Un nouveau dossier supérieur à %1 Mo a été ajouté dans le lecteur %2, vous devez valider sa synchronisation: %3.
@@ -367,6 +367,74 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
Aucun sous-dossier sur le serveur.
+
+ KDC::BetaProgramDialog
+
+
+
+ Quitter le programme bêta
+
+
+
+
+ Rejoindre le programme bêta
+
+
+
+
+ Bénéficiez d'un accès anticipé aux nouvelles versions de l'application avant qu'elles ne soient diffusées au grand public et participez à l'amélioration de l'application en nous faisant part de vos commentaires.
+
+
+
+
+ Bénéficier des mises à jour de la version bêta des applications
+
+
+
+
+ Non
+
+
+
+
+ Version bêta publique
+
+
+
+
+ Version bêta interne
+
+
+
+
+ Je comprends
+
+
+
+
+ Êtes-vous sûr de vouloir quitter le programme bêta ?
+
+
+
+
+ Enregistrer
+
+
+
+
+ Annuler
+
+
+
+
+ Votre version actuelle de l'application est peut-être trop récente, votre choix sera effectif lorsque la prochaine mise à jour sera disponible.
+
+
+
+
+ Les versions bêta peuvent quitter de manière inattendue ou provoquer des instabilités.
+
+
KDC::BigFoldersDialog
@@ -415,18 +483,18 @@ Sélectionnez ceux que vous souhaitez synchroniser:
Aucun dossier à synchroniser n'est configuré.
-
+
Synthèse
-
+
Préférences
Préférences
-
+
Quitter
@@ -471,22 +539,22 @@ Sélectionnez ceux que vous souhaitez synchroniser:
%1 (Synchronisation en pause)
-
+
Voulez-vous vraiment supprimer les synchronisations du compte <i>%1</i> ?<br><b>Remarque :</b> Cela <b>ne</b> supprimera aucun fichier.
-
+
SUPPRIMER TOUTES LES SYNC
-
+
ANNULER
-
+
Échec du démarrage des synchronisations !
@@ -1219,57 +1287,68 @@ Sélectionnez ceux que vous souhaitez synchroniser:
KDC::FixConflictingFilesDialog
-
+
+
Impossible d’ouvrir le lien %1.
-
+
Résoudre le(s) conflit(s)
-
-
- <b>Que souhaitez-vous faire avec les %1 éléments en conflit qui ne sont pas synchronisés dans kDrive ?</b>
+
+
+ <b>Que voulez-vous faire avec les %1 éléments en conflit ?</b>
-
-
- Synchroniser la version locale de mes éléments dans kDrive.
+
+
+ Enregistrer mes modifications et remplacer les versions des autres utilisateurs.
-
-
- Déplacer la version locale de mes éléments vers la corbeille de l'ordinateur.
+
+
+ Annuler mes modifications et conserver les versions des autres utilisateurs.
-
-
- Supprimer définitivement la version locale de mes éléments.
+
+
+ Vos modifications peuvent être supprimées définitivement. Elles ne peuvent pas être restaurées à partir de l'application Web kDrive.
-
+
+
+ <a style=%1 href="%2">En savoir plus</a>
+
+
+
+
+ Vos modifications seront définitivement supprimées. Elles ne pourront pas être restaurées à partir de l'application Web kDrive.
+
+
+
Voir l'(les) élément(s)
-
+
VALIDER
-
+
ANNULER
-
-
- Lorsqu'un élément a été modifié à la fois sur l'ordinateur et sur kDrive ou lorsqu'un élément a été créé sur l'ordinateur avec un nom qui existe déjà sur kDrive, kDrive renomme votre élément local pour ne pas le perdre et télécharge la version de kDrive sur votre ordinateur.<br>
+
+
+ Des modifications ont été apportées à ces fichiers par plusieurs utilisateurs à plusieurs endroits (en ligne sur kDrive, un ordinateur ou un mobile). Les dossiers contenant ces fichiers peuvent également avoir été supprimés.<br>
-
+
La version locale de votre élément <b>n'est pas synchronisée</b> avec kDrive. <a style="color: #489EF3" href="%1">En savoir plus</a>
@@ -1349,12 +1428,12 @@ Sélectionnez ceux que vous souhaitez synchroniser:
KDC::LargeFolderConfirmation
-
+
Demander confirmation avant de synchroniser les dossiers supérieurs à
-
+
Mo
@@ -1467,8 +1546,8 @@ Sélectionnez ceux que vous souhaitez synchroniser:
- Ce dossier n'est pas compatible avec la Lite Sync.<br>
-Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera désactivée.<br>
+ Ce dossier n'est pas compatible avec Lite Sync.<br>
+Veuillez sélectionner un autre dossier. Si vous continuez, Lite Sync sera désactivé.<br>
<a style="%1" href="%2">En savoir plus</a>
@@ -1485,12 +1564,12 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
KDC::Logger
-
+
Erreur
-
+
<nobr>Le fichier '%1'<br/>ne peut être ouvert en écriture.<br/><br/>Le fichier de journalisation <b>ne peut pas</b> être enregistré !</nobr>
@@ -1511,22 +1590,22 @@ Veuillez sélectionner un autre dossier. Si vous continuez, la Lite Sync sera d
KDC::ParametersDialog
-
+
Impossible d’ouvrir le dossier de débogage %1.
-
+
Transmission effectuée !<br>Veuillez vous référer à l'identifiant <b>%1</b> dans vos rapports de bugs.
-
+
Aucun kDrive configuré !
-
+
Échec de la transmission !
@@ -1779,16 +1858,26 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
L'opération effectuée sur cet élément a échoué.<br>L'article a été temporairement mis sur liste noire.
-
-
-
- Le déplacement vers la corbeille a échoué.
-
Le fichier est trop volumineux pour être téléchargé. Il a été temporairement mis sur liste noire.
+
+
+
+ Impossible de télécharger le fichier.
+
+
+
+
+ Vous avez dépassé votre quota. Augmentez votre quota d'espace pour réactiver le téléchargement de fichiers.
+
+
+
+
+ Le dossier de synchronisation est inaccessible (erreur %1).<br>Veuillez vérifier que vous avez accès en lecture et en écriture à ce dossier.
+
@@ -1812,32 +1901,17 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
- Le chemin d'accès à l'élément est trop long.<br>Il a été ignoré.
+ Le chemin de l'élément est trop long.<br>Il a été ignoré.
- Le nom de l'élément contient un caractère UNICODE récent qui n'est pas encore pris en charge par votre système de fichiers. <br>Il a été exclu de la synchronisation.
+ Le nom de l'élément contient un caractère UNICODE récent qui n'est pas encore pris en charge par votre système de fichiers.<br>Il a été exclu de la synchronisation.
- Le nom de l'élément coïncide avec le nom d'un autre élément dans le même répertoire.<br>Il a été temporairement mis sur liste noire. Envisagez de supprimer les éléments en double.
-
-
-
-
- Impossible de télécharger le fichier.
-
-
-
-
- Vous avez dépassé votre quota. Augmentez votre quota d'espace pour réactiver le téléchargement de fichiers.
-
-
-
-
- Le dossier de synchronisation est inaccessible (erreur %1).<br>Veuillez vérifier que vous avez accès en lecture et en écriture à ce dossier.
+ Le nom de l'élément coïncide avec le nom d'un autre élément dans le même répertoire.<br>Il a été temporairement mis sur liste noire. Pensez à supprimer les éléments en double.
@@ -1861,7 +1935,7 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
-
+
Erreur de synchronisation.
@@ -1871,12 +1945,12 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Impossible d'accéder à l'élément.<br>Veuillez corriger les autorisations de lecture et d'écriture.
-
+
Erreur système.
-
+
Une erreur technique s'est produite.<br>Veuillez vider l'historique et si l'erreur persiste, contactez notre équipe d'assistance.
@@ -1905,122 +1979,137 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
KDC::PreferencesWidget
-
+
Paramètres
-
+
Activer le thème foncé
-
+
Activer les icônes monochromes
-
+
Lancer kDrive au démarrage
-
-
- Déplacez les fichiers supprimés dans la corbeille
-
-
-
+
Avancé
-
+
Informations de débogage
-
+
<a style="%1" href="%2">Ouvrir le dossier de débogage</a>
-
+
Fichiers à exclure
-
+
Serveur proxy
-
+
Impossible d'ouvrir le dossier %1.
-
+
Impossible d’ouvrir le lien %1.
-
+
Lien %1 invalide.
-
+
Afficher les dossiers synchronisés dans le volet de navigation de l'Explorateur de Fichier
-
+
Vous devez redémarrer vos explorateurs de fichiers ouverts pour que cette modification prenne effet.
-
+
Lite Sync
-
+
Langue
-
+
Certains processus n'ont pas pu s'exécuter.
-
+
+
+ Déplacer les fichiers supprimés vers la corbeille de mon ordinateur
+
+
+
+
+ Certains fichiers ou dossiers peuvent ne pas être déplacés vers la corbeille de l'ordinateur.
+
+
+
+
+ Vous pouvez toujours récupérer les fichiers déjà synchronisés depuis la corbeille de l'application Web kDrive.
+
+
+
+
+ <a style=%1 href="%2">En savoir plus</a>
+
+
+
Anglais
-
+
Français
-
+
Allemand
-
+
Espagnol
-
+
Italien
-
+
Défaut
@@ -2206,35 +2295,35 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
KDC::SocketApi
-
-
-
+
+
+
Copier le lien de partage privé
-
-
+
+
Le partage de ce fichier n'est pas autorisé
-
-
+
+
Le partage de ce dossier n'est pas autorisé
-
-
-
-
+
+
+
+
Copier le lien de partage public
-
-
+
+
Ouvrir dans le navigateur web
@@ -2358,43 +2447,48 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Pour 1 semaine de plus
-
+
+
+ Envoyer des commentaires
+
+
+
Mettre à jour l'application kDrive
-
+
Cette version de l'application kDrive n'est plus prise en charge. Pour accéder aux dernières fonctionnalités et améliorations, veuillez mettre à jour.
-
-
+
+
Mise à jour
-
+
Veuillez télécharger la dernière version sur le site Web.
-
+
Téléchargement de la mise à jour en cours
-
+
Recherche d'une mise à jour...
-
+
Mise à jour manuelle
-
+
Indisponible
@@ -2404,27 +2498,27 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Quitter kDrive
-
+
Afficher les erreurs et les informations
-
+
Afficher les informations
-
+
Vous pouvez synchroniser les fichiers <a style="%1" href="%2">depuis votre ordinateur</a> ou sur <a style="%1" href="%3">kdrive.infomaniak.com</ un>.
-
+
Ouvrir dans le dossier
-
+
Autres actions
@@ -2444,53 +2538,53 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Activité
-
-
+
+
Non implémenté !
-
+
Aucun fichier synchronisé pour ce kDrive !
-
+
Aucun kDrive configuré !
-
+
Impossible d’accéder au site %1.
-
+
Ouvrir la version Web de %1
-
+
Notifications désactivées jusqu’à %1
-
+
Désactiver les notifications
-
+
Besoin d’aide
-
+
Impossible d’ouvrir le lien %1.
-
+
Lien %1 invalide.
@@ -2500,12 +2594,12 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Impossible d’ouvrir l’URL du dossier %1.
-
+
Paramètres du kDrive
-
+
Préférences de l’application
@@ -2541,12 +2635,12 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
KDC::UpdateManager
-
+
Nouvelle mise à jour disponible.
-
+
La version %1 est disponible au téléchargement.
@@ -2562,65 +2656,90 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
KDC::VersionWidget
-
+
<a style="%1" href="%2">%3</a>
-
+
<a style="%1" href="%2">Afficher la note de version</a>
-
+
Version
-
+
METTRE A JOUR
-
+
%1 est à jour !
-
+
Vérification de la mise à jour sur le serveur...
-
+
Une mise à jour est disponible : %1.<br>Veuillez la télécharger depuis <a style="%2" href="%3">ici</a>.
-
+
Une mise à jour est disponible: %1
-
+
Téléchargement %1 en cours. Veuillez patienter…
-
+
Impossible de vérifier la présence de nouvelles mises à jour.
-
+
Une erreur s'est produite lors de la mise à jour.
-
+
Impossible de télécharger la mise à jour.
+
+
+
+ Programme bêta
+
+
+
+
+ Bénéficier d'un accès anticipé aux nouvelles versions de l'application
+
+
+
+
+ Rejoindre
+
+
+
+
+ Modifier
+
+
+
+
+ Quitter
+
QObject
@@ -2645,37 +2764,37 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Impossible de trouver un chemin valide
-
+
Aucun dossier valable sélectionné!
-
+
Le chemin sélectionné n’existe pas!
-
+
Le chemin sélectionné n'est pas un dossier!
-
+
Vous n'avez pas la permission d'écrire dans le dossier sélectionné!
-
+
Le dossier local %1 contient un dossier déjà synchronisé. Veuillez en choisir un autre !
-
+
Le dossier local %1 est contenu dans un dossier déjà synchronisé. Veuillez en choisir un autre !
-
+
Le dossier local %1 est déjà synchronisé sur le même lecteur. Veuillez en choisir un autre !
@@ -2860,18 +2979,18 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style=
Démarrage de la synchronisation
-
+
Libérer de l’espace local
-
+
Annuler libérer l'espace local
-
-
+
+
Annuler rendre disponible localement
@@ -2910,7 +3029,7 @@ You can add one from the kDrive settings.
Ajoutez-en un depuis la configuration du kDrive.
-
+
Rendre toujours disponible localement
diff --git a/translations/client_it.ts b/translations/client_it.ts
index 6827e328f..5e71a487a 100644
--- a/translations/client_it.ts
+++ b/translations/client_it.ts
@@ -178,15 +178,6 @@
FINE
-
-
-
- Questa cartella non è compatibile con Lite Sync.<br>
-Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<br>
-<a style="%1" href="%2">Scopri di più</a>
-
@@ -202,6 +193,15 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
Al termine della configurazione tutti i tuoi file saranno in questa cartella. Puoi trascinare nuovi file in questa cartella per sincronizzarli con il tuo kDrive.
+
+
+
+ Questa cartella non è compatibile con Lite Sync.<br>
+Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<br>
+<a style="%1" href="%2">Scopri di più</a>
+
@@ -280,7 +280,7 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
Il client kDrive è già in esecuzione!
-
+
L'utente %1 non è connesso. Effettuare nuovamente il login.
@@ -288,12 +288,12 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
KDC::AppServer
-
+
L'applicazione kDrive è già in esecuzione!
-
+
%1 e %n altri file sono stati rimossi.
@@ -301,13 +301,13 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
-
+
%1 names a file.
%1 è stato rimosso.
-
+
%1 e %n altri file sono stati aggiunti.
@@ -315,13 +315,13 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
-
+
%1 names a file.
%1 è stato aggiunto.
-
+
%1 e %n altri file sono stati aggiornati.
@@ -329,13 +329,13 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
-
+
%1 names a file.
%1 è stato aggiornato.
-
+
%1 è stato spostato in %2 e %n altri file sono stati spostati.
@@ -343,17 +343,17 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
-
+
%1 è stato spostato in %2.
-
+
Sincronizza attività
-
+
È stata aggiunta una nuova cartella più grande di %1 MB nell'unità %2, devi convalidarne la sincronizzazione: %3.
@@ -368,6 +368,74 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
Attualmente non ci sono sottocartelle sul server.
+
+ KDC::BetaProgramDialog
+
+
+
+ Abbandonare il programma beta
+
+
+
+
+ Partecipare al programma beta
+
+
+
+
+ Ottenete l'accesso anticipato alle nuove versioni dell'applicazione prima che vengano rilasciate al pubblico e partecipate al miglioramento dell'applicazione inviandoci i vostri commenti.
+
+
+
+
+ Beneficiare degli aggiornamenti beta delle applicazioni
+
+
+
+
+ No
+
+
+
+
+ Versione beta pubblica
+
+
+
+
+ Versione beta interna
+
+
+
+
+ Capisco
+
+
+
+
+ Siete sicuri di voler abbandonare il programma beta?
+
+
+
+
+ Salva
+
+
+
+
+ Annulla
+
+
+
+
+ La tua attuale versione dell'applicazione potrebbe essere troppo recente, la tua scelta sarà effettiva dal prossimo aggiornamento disponibile.
+
+
+
+
+ Le versioni beta possono uscire inaspettatamente o causare instabilità.
+
+
KDC::BigFoldersDialog
@@ -411,17 +479,17 @@ Seleziona quelli che desideri sincronizzare:
Non è stata configurata alcuna cartella per la sincronizzazione.
-
+
Sintesi
-
+
Preferenze
-
+
Esci
@@ -466,22 +534,22 @@ Seleziona quelli che desideri sincronizzare:
%1 (Sincronizzazione sospesa)
-
+
Vuoi davvero rimuovere le sincronizzazioni dell'account <i>%1</i> ?<br><b>Nota:</b> questo <b>non</b> eliminerà alcun file.
-
+
RIMUOVERE TUTTE LE SINC
-
+
ANNULLA
-
+
Impossibile avviare la sincronizzazione!
@@ -1219,57 +1287,68 @@ Seleziona quelli che desideri sincronizzare:
KDC::FixConflictingFilesDialog
-
+
+
Impossibile aprire il link %1.
-
+
Risolvere i conflitti
-
-
- <b>Cosa vuoi fare con gli %1 elementi in conflitto che non sono sincronizzati in kDrive?</b>
+
+
+ <b>Cosa vuoi fare con gli elementi in conflitto %1?</b>
-
-
- Sincronizza la versione locale dei miei articoli in kDrive.
+
+
+ Salva le mie modifiche e sostituisci le versioni degli altri utenti.
-
-
- Sposta la versione locale dei miei articoli nel cestino del computer.
+
+
+ Annulla le mie modifiche e conserva le versioni degli altri utenti.
-
-
- Elimina definitivamente la versione locale dei miei articoli.
+
+
+ Le tue modifiche potrebbero essere eliminate definitivamente. Non possono essere ripristinate dall'applicazione web kDrive.
+
+
+
+
+ <a style=%1 href="%2">Scopri di più</a>
+
+
+
+
+ Le tue modifiche saranno eliminate definitivamente. Non potranno essere ripristinate dall'applicazione web kDrive.
-
+
Mostra articolo(i)
-
+
CONVALIDA
-
+
ANNULLA
-
-
- Quando un elemento è stato modificato sia sul computer che sul kDrive o quando un elemento è stato creato sul computer con un nome già esistente sul kDrive, kDrive rinomina l'elemento locale e scarica la versione di kDrive sul tuo computer per non perdere tutti i dati.<br>
+
+
+ Sono state apportate modifiche a questi file da diversi utenti in diversi posti (online su kDrive, un computer o un dispositivo mobile). Le cartelle contenenti questi file potrebbero anche essere state eliminate.<br>
-
+
La versione locale del tuo elemento <b>non è sincronizzata</b> con kDrive. <a style="color: #489EF3" href="%1">Ulteriori informazioni</a>
@@ -1349,12 +1428,12 @@ Seleziona quelli che desideri sincronizzare:
KDC::LargeFolderConfirmation
-
+
Chiedi conferma prima di sincronizzare cartelle più grandi di
-
+
MB
@@ -1467,7 +1546,7 @@ Seleziona quelli che desideri sincronizzare:
- Questa cartella non è compatibile con Lite Sync.<br>
+ Questa cartella non è compatibile con Lite Sync.<br>
Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<br>
<a style="%1" href="%2">Scopri di più</a>
@@ -1485,12 +1564,12 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
KDC::Logger
-
+
Errore
-
+
<nobr>Il file '%1'<br/>non può essere aperto in scrittura.<br/><br/>Il risultato del log <b>non</b> può essere salvato!</nobr>
@@ -1511,24 +1590,24 @@ Seleziona un'altra cartella. Se continui, Lite Sync verrà disabilitato.<
KDC::ParametersDialog
-
+
Impossibile aprire il percorso della cartella %1.
-
+
Invio completato!<br>Fai riferimento all'identificatore <b>%1</b> nelle segnalazioni di bug.
-
+
Trasmissione fallita!
Per favore, utilizza il seguente link per inviare i log al supporto: <a style="%1" href="%2">%2</a>
-
+
Nessun kDrive configurato!
@@ -1779,16 +1858,26 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
L'operazione eseguita su questo articolo non è riuscita.<br>L'articolo è stato temporaneamente inserito nella lista nera.
-
-
-
- Spostamento nel cestino non riuscito.
-
Il file è troppo grande per essere caricato. È stato temporaneamente inserito nella lista nera.
+
+
+
+ Impossibile scaricare il file.
+
+
+
+
+ Hai superato la tua quota. Aumenta la tua quota di spazio per riattivare il caricamento dei file.
+
+
+
+
+ La cartella di sincronizzazione è inaccessibile (errore %1).<br>Verificare di avere accesso in lettura e scrittura a questa cartella.
+
@@ -1802,7 +1891,7 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
- Questo nome dell'elemento è riservato dal tuo sistema operativo.<br>È stato temporaneamente inserito nella blacklist.
+ Il nome di questo elemento è riservato dal tuo sistema operativo.<br>È stato temporaneamente inserito nella blacklist.
@@ -1817,28 +1906,13 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
- Il nome dell'elemento contiene un carattere UNICODE recente non ancora supportato dal tuo file system.<br>È stato escluso dalla sincronizzazione.
+ Il nome dell'elemento contiene un carattere UNICODE recente non ancora supportato dal file system.<br>È stato escluso dalla sincronizzazione.
Il nome dell'elemento coincide con il nome di un altro elemento nella stessa directory.<br>È stato temporaneamente inserito nella blacklist. Prendi in considerazione la rimozione degli elementi duplicati.
-
-
-
- Impossibile scaricare il file.
-
-
-
-
- Hai superato la tua quota. Aumenta la tua quota di spazio per riattivare il caricamento dei file.
-
-
-
-
- La cartella di sincronizzazione è inaccessibile (errore %1).<br>Verificare di avere accesso in lettura e scrittura a questa cartella.
-
@@ -1861,7 +1935,7 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
-
+
Errore di sincronizzazione.
@@ -1871,12 +1945,12 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
Impossibile accedere all'elemento.<br>Si prega di correggere i permessi di lettura e scrittura.
-
+
Errore di sistema.
-
+
Si è verificato un errore tecnico.<br>Svuota la cronologia e, se l'errore persiste, contatta il nostro team di assistenza.
@@ -1905,122 +1979,137 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
KDC::PreferencesWidget
-
+
Generale
-
+
Attiva tema scuro
-
+
Attiva icone monocolore
-
+
Lancia kDrive all'avvio del sistema
-
-
- Sposta i file eliminati nel cestino
-
-
-
+
Avanzate
-
+
Informazioni di debug
-
+
<a style="%1" href="%2">Apri cartella di debug</a>
-
+
File da escludere
-
+
Server proxy
-
+
Impossibile aprire la cartella %1.
-
+
Impossibile aprire il link %1.
-
+
Link %1 non valido.
-
+
Mostra le cartelle sincronizzate nel riquadro di navigazione di Esplora file
-
+
È necessario riavviare gli strumenti di esplorazione di file aperti affinché questa modifica abbia effetto.
-
+
Lite Sync
-
+
Lingua
-
+
Non è possibile eseguire alcuni processi.
-
+
+
+ Sposta i file eliminati nel cestino del mio computer
+
+
+
+
+ Alcuni file o cartelle potrebbero non essere spostati nel cestino del computer.
+
+
+
+
+ Puoi sempre recuperare i file già sincronizzati dal cestino dell'applicazione web kDrive.
+
+
+
+
+ <a style=%1 href="%2">Scopri di più</a>
+
+
+
Inglese
-
+
Francese
-
+
Tedesco
-
+
Spagnolo
-
+
Italiano
-
+
Predefinito
@@ -2206,35 +2295,35 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
KDC::SocketApi
-
-
-
+
+
+
Copia il collegamento di condivisione privata
-
-
+
+
Ricondivisione di questo file non consentita
-
-
+
+
Ricondivisione di questa cartella non consentita
-
-
-
-
+
+
+
+
Copia il collegamento di condivisione pubblica
-
-
+
+
Apri nel browser
@@ -2358,43 +2447,48 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
Per 1 altra settimana
-
+
+
+ Invia feedback
+
+
+
Aggiorna l'app kDrive
-
+
Questa versione dell'app kDrive non è più supportata. Per accedere alle funzionalità e ai miglioramenti più recenti, aggiornare.
-
-
+
+
Aggiornamento
-
+
Si prega di scaricare l'ultima versione dal sito web.
-
+
Download dell'aggiornamento in corso
-
+
In cerca di aggiornamento...
-
+
Aggiornamento manuale
-
+
Non disponibile
@@ -2404,27 +2498,27 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
Esci da kDrive
-
+
Mostra errori e informazioni
-
+
Mostra informazioni
-
+
Puoi sincronizzare i file <a style="%1" href="%2">dal tuo computer</a> o su <a style="%1" href="%3">kdrive.infomaniak.com</a>.
-
+
Apri nella cartella
-
+
Più azioni
@@ -2444,53 +2538,53 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
Attività
-
-
+
+
Non implementato!
-
+
Nessuna cartella sincronizzata per questo kDrive!
-
+
Nessun kDrive configurato!
-
+
Impossibile accedere al sito web %1.
-
+
Apri la versione web %1
-
+
Notifiche disabilitate fino al %1
-
+
Disabilita notifiche
-
+
Hai bisogno di aiuto
-
+
Impossibile aprire il link %1.
-
+
Link %1 non valido.
@@ -2500,12 +2594,12 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
Impossibile aprire l'URL della cartella %1.
-
+
Parametri unità
-
+
Preferenze dell'applicazione
@@ -2541,12 +2635,12 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
KDC::UpdateManager
-
+
È disponibile un nuovo aggiornamento.
-
+
La versione %1 è disponibile per il download.
@@ -2562,65 +2656,90 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
KDC::VersionWidget
-
+
<a style="%1" href="%2">%3</a>
-
+
<a style="%1" href="%2">Mostra nota di rilascio</a>
-
+
Versione
-
+
AGGIORNAMENTO
-
+
%1 è aggiornato!
-
+
Controllo dell'aggiornamento sul server...
-
+
È disponibile un aggiornamento: %1.<br>Scaricalo da <a style="%2" href="%3">qui</a>.
-
+
È disponibile un aggiornamento: %1
-
+
Download in corso %1. Attendere...
-
+
Impossibile verificare la presenza di nuovi aggiornamenti.
-
+
Si è verificato un errore durante l'aggiornamento.
-
+
Impossibile scaricare l'aggiornamento.
+
+
+
+ Beta program
+
+
+
+
+ Ottenere l'accesso anticipato alle nuove versioni dell'applicazione
+
+
+
+
+ Contatto
+
+
+
+
+ Modificare
+
+
+
+
+ Esci
+
QObject
@@ -2645,37 +2764,37 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
Impossibile trovare un percorso valido
-
+
Nessuna cartella valida selezionata!
-
+
Il percorso selezionato non esiste!
-
+
Il percorso selezionato non è una cartella!
-
+
Non disponi dell'autorizzazione di scrittura per la cartella selezionata!
-
+
La cartella locale %1 contiene una cartella già sincronizzata. Scegline un'altra!
-
+
La cartella locale %1 è contenuta in una cartella già sincronizzata. Scegline un'altra!
-
+
La cartella locale %1 è già sincronizzata sulla stessa unità. Scegline un'altra!
@@ -2796,23 +2915,23 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style
utility
-
+
Libera spazio locale
-
+
Annulla liberazione di spazio locale
-
-
+
+
Annulla rendere disponibile localmente
-
+
Rendi sempre disponibile localmente
diff --git a/translations/translate.py b/translations/translate.py
index a7a868a16..26f6011cf 100755
--- a/translations/translate.py
+++ b/translations/translate.py
@@ -54,9 +54,9 @@
translator = deepl.Translator(deepl_key)
target_lang = [
- 'FR',
'DE',
'ES',
+ 'FR',
'IT'
]