diff --git a/resources.qrc b/resources.qrc
index f233501fc..976c534b7 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -51,5 +51,6 @@
src/gui/tray/EnforcedPlainTextLabel.qml
theme/Style/Style.qml
theme/Style/qmldir
+ src/gui/filedetails/NCRadioButton.qml
diff --git a/src/gui/filedetails/NCRadioButton.qml b/src/gui/filedetails/NCRadioButton.qml
new file mode 100644
index 000000000..05e879164
--- /dev/null
+++ b/src/gui/filedetails/NCRadioButton.qml
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) by Oleksandr Zolotov
+ *
+ * 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 2 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.
+ */
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import Style 1.0
+
+RadioButton {
+ id: root
+ property int indicatorItemWidth: Style.radioButtonIndicatorSize
+ property int indicatorItemHeight: Style.radioButtonIndicatorSize
+ property string color: Style.ncTextColor
+ readonly property int radius: Style.radioButtonCustomRadius
+
+ indicator: Rectangle {
+ implicitWidth: root.indicatorItemWidth
+ implicitHeight: root.indicatorItemHeight
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.leftMargin: Style.radioButtonCustomMarginLeftOuter
+ radius: root.radius
+ border.color: root.color
+ border.width: Style.normalBorderWidth
+ Rectangle {
+ anchors.fill: parent
+ visible: root.checked
+ color: root.color
+ radius: root.radius
+ anchors.margins: Style.radioButtonCustomMarginLeftInner
+ }
+ }
+}
diff --git a/src/gui/filedetails/ShareDelegate.qml b/src/gui/filedetails/ShareDelegate.qml
index 913dec2e0..e48deaf4d 100644
--- a/src/gui/filedetails/ShareDelegate.qml
+++ b/src/gui/filedetails/ShareDelegate.qml
@@ -33,11 +33,13 @@ GridLayout {
signal resetPasswordField
signal showPasswordSetError(string errorMessage);
+ signal toggleHideDownload(bool enable)
signal toggleAllowEditing(bool enable)
signal toggleAllowResharing(bool enable)
signal togglePasswordProtect(bool enable)
signal toggleExpirationDate(bool enable)
signal toggleNoteToRecipient(bool enable)
+ signal permissionModeChanged(int permissionMode)
signal setLinkShareLabel(string label)
signal setExpireDate(var milliseconds) // Since QML ints are only 32 bits, use a variant
@@ -236,9 +238,11 @@ GridLayout {
onToggleAllowEditing: root.toggleAllowEditing(enable)
onToggleAllowResharing: root.toggleAllowResharing(enable)
+ onToggleHideDownload: root.toggleHideDownload(enable)
onTogglePasswordProtect: root.togglePasswordProtect(enable)
onToggleExpirationDate: root.toggleExpirationDate(enable)
onToggleNoteToRecipient: root.toggleNoteToRecipient(enable)
+ onPermissionModeChanged: root.permissionModeChanged(permissionMode)
onSetLinkShareLabel: root.setLinkShareLabel(label)
onSetExpireDate: root.setExpireDate(milliseconds) // Since QML ints are only 32 bits, use a variant
diff --git a/src/gui/filedetails/ShareDetailsPage.qml b/src/gui/filedetails/ShareDetailsPage.qml
index 03dd7c52d..a4bcc7caf 100644
--- a/src/gui/filedetails/ShareDetailsPage.qml
+++ b/src/gui/filedetails/ShareDetailsPage.qml
@@ -32,9 +32,11 @@ Page {
signal toggleAllowEditing(bool enable)
signal toggleAllowResharing(bool enable)
+ signal toggleHideDownload(bool enable)
signal togglePasswordProtect(bool enable)
signal toggleExpirationDate(bool enable)
signal toggleNoteToRecipient(bool enable)
+ signal permissionModeChanged(int permissionMode)
signal setLinkShareLabel(string label)
signal setExpireDate(var milliseconds) // Since QML ints are only 32 bits, use a variant
@@ -65,16 +67,21 @@ Page {
readonly property string linkShareLabel: shareModelData.linkShareLabel ?? ""
readonly property bool editingAllowed: shareModelData.editingAllowed
+ readonly property bool hideDownload: shareModelData.hideDownload
readonly property bool noteEnabled: shareModelData.noteEnabled
readonly property bool expireDateEnabled: shareModelData.expireDateEnabled
readonly property bool expireDateEnforced: shareModelData.expireDateEnforced
readonly property bool passwordProtectEnabled: shareModelData.passwordProtectEnabled
readonly property bool passwordEnforced: shareModelData.passwordEnforced
- readonly property bool isSecureFileDropLink: shareModelData.isSecureFileDropLink
+ readonly property bool isSharePermissionChangeInProgress: shareModelData.isSharePermissionChangeInProgress
+ readonly property bool isHideDownloadInProgress: shareModelData.isHideDownloadInProgress
+ readonly property int currentPermissionMode: shareModelData.currentPermissionMode
readonly property bool isLinkShare: shareModelData.shareType === ShareModel.ShareTypeLink
- property bool waitingForEditingAllowedChange: false
+ readonly property bool isFolderItem: shareModelData.sharedItemType === ShareModel.SharedItemTypeFolder
+ readonly property bool isEncryptedItem: shareModelData.sharedItemType === ShareModel.SharedItemTypeEncryptedFile || shareModelData.sharedItemType === ShareModel.SharedItemTypeEncryptedFolder || shareModelData.sharedItemType === ShareModel.SharedItemTypeEncryptedTopLevelFolder
+
property bool waitingForNoteEnabledChange: false
property bool waitingForExpireDateEnabledChange: false
property bool waitingForPasswordProtectEnabledChange: false
@@ -108,11 +115,6 @@ Page {
waitingForExpireDateChange = false;
}
- function resetEditingAllowedField() {
- editingAllowedMenuItem.checked = editingAllowed;
- waitingForEditingAllowedChange = false;
- }
-
function resetNoteEnabledField() {
noteEnabledMenuItem.checked = noteEnabled;
waitingForNoteEnabledChange = false;
@@ -135,8 +137,6 @@ Page {
resetPasswordField();
resetLinkShareLabelField();
resetExpireDateField();
-
- resetEditingAllowedField();
resetNoteEnabledField();
resetExpireDateEnabledField();
resetPasswordProtectEnabledField();
@@ -154,8 +154,6 @@ Page {
onPasswordChanged: resetPasswordField()
onLinkShareLabelChanged: resetLinkShareLabelField()
onExpireDateChanged: resetExpireDateField()
-
- onEditingAllowedChanged: resetEditingAllowedField()
onNoteEnabledChanged: resetNoteEnabledField()
onExpireDateEnabledChanged: resetExpireDateEnabledField()
onPasswordProtectEnabledChanged: resetPasswordProtectEnabledField()
@@ -313,34 +311,124 @@ Page {
}
}
- // On these checkables, the clicked() signal is called after
- // the check state changes.
- CheckBox {
- id: editingAllowedMenuItem
+ Loader {
+ Layout.fillWidth: true
+ active: !root.isFolderItem && !root.isEncryptedItem
+ visible: active
+ sourceComponent: CheckBox {
+ spacing: moreMenu.indicatorSpacing
+ padding: moreMenu.itemPadding
+ indicator.width: moreMenu.indicatorItemWidth
+ indicator.height: moreMenu.indicatorItemWidth
+ checkable: true
+ checked: root.editingAllowed
+ text: qsTr("Allow upload and editing")
+ enabled: !root.isSharePermissionChangeInProgress
+
+ onClicked: root.toggleAllowEditing(checked)
+
+ NCBusyIndicator {
+ anchors.fill: parent
+ visible: root.isSharePermissionChangeInProgress
+ running: visible
+ z: 1
+ }
+ }
+ }
+
+ Loader {
+ Layout.fillWidth: true
+ active: root.isFolderItem && !root.isEncryptedItem
+ visible: active
+ sourceComponent: ColumnLayout {
+ id: permissionRadioButtonsLayout
+ spacing: 0
+ width: parent.width
+
+ ButtonGroup {
+ id: permissionModeRadioButtonsGroup
+ }
+
+ NCRadioButton {
+ readonly property int permissionMode: ShareModel.ModeViewOnly
+ Layout.fillWidth: true
+ ButtonGroup.group: permissionModeRadioButtonsGroup
+ enabled: !root.isSharePermissionChangeInProgress
+ checked: root.currentPermissionMode === permissionMode
+ text: qsTr("View only")
+ indicatorItemWidth: moreMenu.indicatorItemWidth
+ indicatorItemHeight: moreMenu.indicatorItemWidth
+ spacing: moreMenu.indicatorSpacing
+ padding: moreMenu.itemPadding
+ onClicked: root.permissionModeChanged(permissionMode)
+ }
+
+ NCRadioButton {
+ readonly property int permissionMode: ShareModel.ModeUploadAndEditing
+ Layout.fillWidth: true
+ ButtonGroup.group: permissionModeRadioButtonsGroup
+ enabled: !root.isSharePermissionChangeInProgress
+ checked: root.currentPermissionMode === permissionMode
+ text: qsTr("Allow upload and editing")
+ indicatorItemWidth: moreMenu.indicatorItemWidth
+ indicatorItemHeight: moreMenu.indicatorItemWidth
+ spacing: moreMenu.indicatorSpacing
+ padding: moreMenu.itemPadding
+ onClicked: root.permissionModeChanged(permissionMode)
+
+ NCBusyIndicator {
+ anchors.fill: parent
+ visible: root.isSharePermissionChangeInProgress
+ running: visible
+ z: 1
+ }
+ }
+
+ NCRadioButton {
+ readonly property int permissionMode: ShareModel.ModeFileDropOnly
+ Layout.fillWidth: true
+ ButtonGroup.group: permissionModeRadioButtonsGroup
+ enabled: !root.isSharePermissionChangeInProgress
+ checked: root.currentPermissionMode === permissionMode
+ text: qsTr("File drop (upload only)")
+ indicatorItemWidth: moreMenu.indicatorItemWidth
+ indicatorItemHeight: moreMenu.indicatorItemWidth
+ spacing: moreMenu.indicatorSpacing
+ padding: moreMenu.itemPadding
+ onClicked: root.permissionModeChanged(permissionMode)
+ }
+ }
+ }
+
+ Loader {
Layout.fillWidth: true
- spacing: moreMenu.indicatorSpacing
- padding: moreMenu.itemPadding
- indicator.width: moreMenu.indicatorItemWidth
- indicator.height: moreMenu.indicatorItemWidth
+ active: root.isLinkShare
+ visible: active
+ sourceComponent: ColumnLayout {
+ CheckBox {
+ id: hideDownloadEnabledMenuItem
- checkable: true
- checked: root.editingAllowed
- text: qsTr("Allow editing")
- enabled: !root.waitingForEditingAllowedChange
- visible: !root.isSecureFileDropLink
+ anchors.left: parent.left
+ anchors.right: parent.right
- onClicked: {
- root.toggleAllowEditing(checked);
- root.waitingForEditingAllowedChange = true;
- }
+ spacing: moreMenu.indicatorSpacing
+ padding: moreMenu.itemPadding
+ indicator.width: moreMenu.indicatorItemWidth
+ indicator.height: moreMenu.indicatorItemWidth
+ checked: root.hideDownload
+ text: qsTr("Hide download")
+ enabled: !root.isHideDownloadInProgress
+ onClicked: root.toggleHideDownload(checked);
- NCBusyIndicator {
- anchors.fill: parent
- visible: root.waitingForEditingAllowedChange
- running: visible
- z: 1
+ NCBusyIndicator {
+ anchors.fill: parent
+ visible: root.isHideDownloadInProgress
+ running: visible
+ z: 1
+ }
+ }
}
}
diff --git a/src/gui/filedetails/ShareView.qml b/src/gui/filedetails/ShareView.qml
index 20a63963c..a1c80bb0f 100644
--- a/src/gui/filedetails/ShareView.qml
+++ b/src/gui/filedetails/ShareView.qml
@@ -241,9 +241,11 @@ ColumnLayout {
onToggleAllowEditing: shareModel.toggleShareAllowEditingFromQml(model.share, enable)
onToggleAllowResharing: shareModel.toggleShareAllowResharingFromQml(model.share, enable)
+ onToggleHideDownload: shareModel.toggleHideDownloadFromQml(model.share, enable)
onTogglePasswordProtect: shareModel.toggleSharePasswordProtectFromQml(model.share, enable)
onToggleExpirationDate: shareModel.toggleShareExpirationDateFromQml(model.share, enable)
onToggleNoteToRecipient: shareModel.toggleShareNoteToRecipientFromQml(model.share, enable)
+ onPermissionModeChanged: shareModel.changePermissionModeFromQml(model.share, permissionMode)
onSetLinkShareLabel: shareModel.setLinkShareLabelFromQml(model.share, label)
onSetExpireDate: shareModel.setShareExpireDateFromQml(model.share, milliseconds)
diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp
index df7acb873..8cb513b34 100644
--- a/src/gui/filedetails/sharemodel.cpp
+++ b/src/gui/filedetails/sharemodel.cpp
@@ -81,7 +81,11 @@ QHash ShareModel::roleNames() const
roles[PasswordRole] = "password";
roles[PasswordEnforcedRole] = "passwordEnforced";
roles[EditingAllowedRole] = "editingAllowed";
- roles[IsSecureFileDropLinkRole] = "isSecureFileDropLink";
+ roles[CurrentPermissionModeRole] = "currentPermissionMode";
+ roles[SharedItemTypeRole] = "sharedItemType";
+ roles[IsSharePermissionsChangeInProgress] = "isSharePermissionChangeInProgress";
+ roles[HideDownloadEnabledRole] = "hideDownload";
+ roles[IsHideDownloadEnabledChangeInProgress] = "isHideDownloadInProgress";
return roles;
}
@@ -107,6 +111,8 @@ QVariant ShareModel::data(const QModelIndex &index, const int role) const
return linkShare->getLabel();
case NoteEnabledRole:
return !linkShare->getNote().isEmpty();
+ case HideDownloadEnabledRole:
+ return linkShare->getHideDownload();
case NoteRole:
return linkShare->getNote();
case ExpireDateEnabledRole:
@@ -151,8 +157,22 @@ QVariant ShareModel::data(const QModelIndex &index, const int role) const
return expireDateEnforcedForShare(share);
case EnforcedMaximumExpireDateRole:
return enforcedMaxExpireDateForShare(share);
- case IsSecureFileDropLinkRole:
- return _isSecureFileDropSupportedFolder && share->getPermissions().testFlag(OCC::SharePermission::SharePermissionCreate);
+ case CurrentPermissionModeRole: {
+ if (share->getPermissions() == OCC::SharePermission::SharePermissionCreate) {
+ return QVariant::fromValue(SharePermissionsMode::ModeFileDropOnly);
+ } else if ((share->getPermissions() & SharePermissionRead) && (share->getPermissions() & SharePermissionCreate)
+ && (share->getPermissions() & SharePermissionUpdate) && (share->getPermissions() & SharePermissionDelete)) {
+ return QVariant::fromValue(SharePermissionsMode::ModeUploadAndEditing);
+ } else {
+ return QVariant::fromValue(SharePermissionsMode::ModeViewOnly);
+ }
+ }
+ case SharedItemTypeRole:
+ return static_cast(_sharedItemType);
+ case IsSharePermissionsChangeInProgress:
+ return _sharePermissionsChangeInProgress;
+ case IsHideDownloadEnabledChangeInProgress:
+ return _hideDownloadEnabledChangeInProgress;
case PasswordProtectEnabledRole:
return share->isPasswordSet();
case PasswordRole:
@@ -250,9 +270,15 @@ void ShareModel::updateData()
_numericFileId = fileRecord.numericFileId();
- _isEncryptedItem = fileRecord.isE2eEncrypted();
- _isSecureFileDropSupportedFolder =
- fileRecord.isE2eEncrypted() && fileRecord.e2eMangledName().isEmpty() && _accountState->account()->secureFileDropSupported();
+ if (fileRecord.isDirectory()) {
+ if (fileRecord.isE2eEncrypted()) {
+ _sharedItemType = fileRecord.e2eMangledName().isEmpty() ? SharedItemType::SharedItemTypeEncryptedTopLevelFolder : SharedItemType::SharedItemTypeEncryptedFolder;
+ } else {
+ _sharedItemType = SharedItemType::SharedItemTypeFolder;
+ }
+ } else {
+ _sharedItemType = fileRecord.isE2eEncrypted() ? SharedItemType::SharedItemTypeEncryptedFile : SharedItemType::SharedItemTypeFile;
+ }
// Will get added when shares are fetched if no link shares are fetched
_placeholderLinkShare.reset(new Share(_accountState->account(),
@@ -386,9 +412,9 @@ void ShareModel::handleSecureFileDropLinkShare()
void ShareModel::handleLinkShare()
{
- if (!_isEncryptedItem) {
+ if (!isEncryptedItem()) {
handlePlaceholderLinkShare();
- } else if (_isSecureFileDropSupportedFolder) {
+ } else if (isSecureFileDropSupportedFolder()) {
handleSecureFileDropLinkShare();
}
}
@@ -456,7 +482,7 @@ void ShareModel::setupInternalLinkShare()
_accountState->account().isNull() ||
_localPath.isEmpty() ||
_privateLinkUrl.isEmpty() ||
- _isEncryptedItem) {
+ isEncryptedItem()) {
return;
}
@@ -466,6 +492,30 @@ void ShareModel::setupInternalLinkShare()
Q_EMIT internalLinkReady();
}
+void ShareModel::setSharePermissionChangeInProgress(const QString &shareId, const bool isInProgress)
+{
+ if (isInProgress == _sharePermissionsChangeInProgress) {
+ return;
+ }
+
+ _sharePermissionsChangeInProgress = isInProgress;
+
+ const auto shareIndex = _shareIdIndexHash.value(shareId);
+ Q_EMIT dataChanged(shareIndex, shareIndex, {IsSharePermissionsChangeInProgress});
+}
+
+void ShareModel::setHideDownloadEnabledChangeInProgress(const QString &shareId, const bool isInProgress)
+{
+ if (isInProgress == _hideDownloadEnabledChangeInProgress) {
+ return;
+ }
+
+ _hideDownloadEnabledChangeInProgress = isInProgress;
+
+ const auto shareIndex = _shareIdIndexHash.value(shareId);
+ Q_EMIT dataChanged(shareIndex, shareIndex, {IsHideDownloadEnabledChangeInProgress});
+}
+
void ShareModel::slotAddShare(const SharePtr &share)
{
if (share.isNull()) {
@@ -515,6 +565,7 @@ void ShareModel::slotAddShare(const SharePtr &share)
connect(linkShare.data(), &LinkShare::nameSet, this, [this, shareId]{ slotShareNameSet(shareId); });
connect(linkShare.data(), &LinkShare::labelSet, this, [this, shareId]{ slotShareLabelSet(shareId); });
connect(linkShare.data(), &LinkShare::expireDateSet, this, [this, shareId]{ slotShareExpireDateSet(shareId); });
+ connect(linkShare.data(), &LinkShare::hideDownloadSet, this, [this, shareId] { slotHideDownloadSet(shareId); });
} else if (const auto userGroupShare = share.objectCast()) {
connect(userGroupShare.data(), &UserGroupShare::noteSet, this, [this, shareId]{ slotShareNoteSet(shareId); });
connect(userGroupShare.data(), &UserGroupShare::expireDateSet, this, [this, shareId]{ slotShareExpireDateSet(shareId); });
@@ -582,7 +633,7 @@ QString ShareModel::displayStringForShare(const SharePtr &share) const
{
if (const auto linkShare = share.objectCast()) {
- const auto isSecureFileDropShare = _isSecureFileDropSupportedFolder && linkShare->getPermissions().testFlag(OCC::SharePermission::SharePermissionCreate);
+ const auto isSecureFileDropShare = isSecureFileDropSupportedFolder() && linkShare->getPermissions().testFlag(OCC::SharePermission::SharePermissionCreate);
const auto displayString = isSecureFileDropShare ? tr("Secure file drop link") : tr("Share link");
@@ -708,7 +759,8 @@ void ShareModel::slotSharePermissionsSet(const QString &shareId)
const auto sharePersistentModelIndex = _shareIdIndexHash.value(shareId);
const auto shareModelIndex = index(sharePersistentModelIndex.row());
- Q_EMIT dataChanged(shareModelIndex, shareModelIndex, { EditingAllowedRole });
+ Q_EMIT dataChanged(shareModelIndex, shareModelIndex, { EditingAllowedRole, CurrentPermissionModeRole });
+ setSharePermissionChangeInProgress(shareId, false);
}
void ShareModel::slotSharePasswordSet(const QString &shareId)
@@ -722,6 +774,18 @@ void ShareModel::slotSharePasswordSet(const QString &shareId)
Q_EMIT dataChanged(shareModelIndex, shareModelIndex, { PasswordProtectEnabledRole, PasswordRole });
}
+void ShareModel::slotHideDownloadSet(const QString &shareId)
+{
+ if (shareId.isEmpty() || !_shareIdIndexHash.contains(shareId)) {
+ return;
+ }
+
+ const auto sharePersistentModelIndex = _shareIdIndexHash.value(shareId);
+ const auto shareModelIndex = index(sharePersistentModelIndex.row());
+ Q_EMIT dataChanged(shareModelIndex, shareModelIndex, {HideDownloadEnabledRole});
+ setHideDownloadEnabledChangeInProgress(shareId, false);
+}
+
void ShareModel::slotShareNoteSet(const QString &shareId)
{
if (shareId.isEmpty() || !_shareIdIndexHash.contains(shareId)) {
@@ -768,37 +832,56 @@ void ShareModel::slotShareExpireDateSet(const QString &shareId)
// ----------------------- Shares modification slots ----------------------- //
-void ShareModel::toggleShareAllowEditing(const SharePtr &share, const bool enable) const
+void ShareModel::toggleShareAllowEditing(const SharePtr &share, const bool enable)
{
- if (share.isNull()) {
+ if (share.isNull() || _sharePermissionsChangeInProgress) {
return;
}
auto permissions = share->getPermissions();
enable ? permissions |= SharePermissionUpdate : permissions &= ~SharePermissionUpdate;
+ setSharePermissionChangeInProgress(share->getId(), true);
share->setPermissions(permissions);
}
-void ShareModel::toggleShareAllowEditingFromQml(const QVariant &share, const bool enable) const
+void ShareModel::toggleShareAllowEditingFromQml(const QVariant &share, const bool enable)
{
const auto ptr = share.value();
toggleShareAllowEditing(ptr, enable);
}
-void ShareModel::toggleShareAllowResharing(const SharePtr &share, const bool enable) const
+void ShareModel::toggleShareAllowResharing(const SharePtr &share, const bool enable)
{
- if (share.isNull()) {
+ if (share.isNull() || _sharePermissionsChangeInProgress) {
return;
}
auto permissions = share->getPermissions();
enable ? permissions |= SharePermissionShare : permissions &= ~SharePermissionShare;
+ setSharePermissionChangeInProgress(share->getId(), true);
share->setPermissions(permissions);
}
-void ShareModel::toggleShareAllowResharingFromQml(const QVariant &share, const bool enable) const
+void ShareModel::toggleHideDownloadFromQml(const QVariant &share, const bool enable)
+{
+ const auto sharePtr = share.value();
+ if (sharePtr.isNull() || _hideDownloadEnabledChangeInProgress) {
+ return;
+ }
+
+ const auto linkShare = sharePtr.objectCast();
+
+ if (linkShare.isNull()) {
+ return;
+ }
+
+ setHideDownloadEnabledChangeInProgress(linkShare->getId(), true);
+ linkShare->setHideDownload(enable);
+}
+
+void ShareModel::toggleShareAllowResharingFromQml(const QVariant &share, const bool enable)
{
const auto ptr = share.value();
toggleShareAllowResharing(ptr, enable);
@@ -867,6 +950,42 @@ void ShareModel::toggleShareNoteToRecipientFromQml(const QVariant &share, const
toggleShareNoteToRecipient(ptr, enable);
}
+void ShareModel::changePermissionModeFromQml(const QVariant &share, const SharePermissionsMode permissionMode)
+{
+ const auto sharePtr = share.value();
+ if (sharePtr.isNull() || _sharePermissionsChangeInProgress) {
+ return;
+ }
+
+ const auto shareIndex = _shareIdIndexHash.value(sharePtr->getId());
+
+ if (!checkIndex(shareIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::ParentIsInvalid)) {
+ qCWarning(lcShareModel) << "Can't change permission mode for:" << sharePtr->getId() << ", invalid share index: " << shareIndex;
+ return;
+ }
+
+ const auto currentPermissionMode = shareIndex.data(ShareModel::CurrentPermissionModeRole).value();
+
+ if (currentPermissionMode == permissionMode) {
+ return;
+ }
+
+ SharePermissions perm = SharePermissionRead;
+ switch (permissionMode) {
+ case SharePermissionsMode::ModeViewOnly:
+ break;
+ case SharePermissionsMode::ModeUploadAndEditing:
+ perm |= SharePermissionCreate | SharePermissionUpdate | SharePermissionDelete;
+ break;
+ case SharePermissionsMode::ModeFileDropOnly:
+ perm = SharePermissionCreate;
+ break;
+ }
+
+ setSharePermissionChangeInProgress(sharePtr->getId(), true);
+ sharePtr->setPermissions(perm);
+}
+
void ShareModel::setLinkShareLabel(const QSharedPointer &linkShare, const QString &label) const
{
if (linkShare.isNull()) {
@@ -944,7 +1063,7 @@ void ShareModel::setShareNoteFromQml(const QVariant &share, const QString ¬e)
void ShareModel::createNewLinkShare() const
{
- if (_isEncryptedItem && !_isSecureFileDropSupportedFolder) {
+ if (isEncryptedItem() && !isSecureFileDropSupportedFolder()) {
qCWarning(lcShareModel) << "Attempt to create a link share for non-root encrypted folder or a file.";
return;
}
@@ -952,7 +1071,7 @@ void ShareModel::createNewLinkShare() const
if (_manager) {
const auto askOptionalPassword = _accountState->account()->capabilities().sharePublicLinkAskOptionalPassword();
const auto password = askOptionalPassword ? createRandomPassword() : QString();
- if (_isSecureFileDropSupportedFolder) {
+ if (isSecureFileDropSupportedFolder()) {
_manager->createSecureFileDropShare(_sharePath, {}, password);
return;
}
@@ -1089,6 +1208,17 @@ bool ShareModel::validCapabilities() const
_accountState->account()->capabilities().isValid();
}
+bool ShareModel::isSecureFileDropSupportedFolder() const
+{
+ return _sharedItemType == SharedItemType::SharedItemTypeEncryptedTopLevelFolder && _accountState->account()->secureFileDropSupported();
+}
+
+bool ShareModel::isEncryptedItem() const
+{
+ return _sharedItemType == SharedItemType::SharedItemTypeEncryptedFile || _sharedItemType == SharedItemType::SharedItemTypeEncryptedFolder
+ || _sharedItemType == SharedItemType::SharedItemTypeEncryptedTopLevelFolder;
+}
+
bool ShareModel::sharingEnabled() const
{
return validCapabilities() &&
diff --git a/src/gui/filedetails/sharemodel.h b/src/gui/filedetails/sharemodel.h
index 9d1071298..71458fcc7 100644
--- a/src/gui/filedetails/sharemodel.h
+++ b/src/gui/filedetails/sharemodel.h
@@ -57,7 +57,11 @@ public:
PasswordRole,
PasswordEnforcedRole,
EditingAllowedRole,
- IsSecureFileDropLinkRole,
+ CurrentPermissionModeRole,
+ SharedItemTypeRole,
+ IsSharePermissionsChangeInProgress,
+ HideDownloadEnabledRole,
+ IsHideDownloadEnabledChangeInProgress,
};
Q_ENUM(Roles)
@@ -79,6 +83,23 @@ public:
ShareTypeSecureFileDropPlaceholderLink = Share::TypeSecureFileDropPlaceholderLink,
};
Q_ENUM(ShareType);
+
+ enum class SharedItemType {
+ SharedItemTypeUndefined = -1,
+ SharedItemTypeFile,
+ SharedItemTypeFolder,
+ SharedItemTypeEncryptedFile,
+ SharedItemTypeEncryptedFolder,
+ SharedItemTypeEncryptedTopLevelFolder,
+ };
+ Q_ENUM(SharedItemType);
+
+ enum class SharePermissionsMode {
+ ModeViewOnly,
+ ModeUploadAndEditing,
+ ModeFileDropOnly,
+ };
+ Q_ENUM(SharePermissionsMode);
explicit ShareModel(QObject *parent = nullptr);
@@ -135,16 +156,18 @@ public slots:
void deleteShare(const OCC::SharePtr &share) const;
void deleteShareFromQml(const QVariant &share) const;
- void toggleShareAllowEditing(const OCC::SharePtr &share, const bool enable) const;
- void toggleShareAllowEditingFromQml(const QVariant &share, const bool enable) const;
- void toggleShareAllowResharing(const OCC::SharePtr &share, const bool enable) const;
- void toggleShareAllowResharingFromQml(const QVariant &share, const bool enable) const;
+ void toggleHideDownloadFromQml(const QVariant &share, const bool enable);
+ void toggleShareAllowEditing(const OCC::SharePtr &share, const bool enable);
+ void toggleShareAllowEditingFromQml(const QVariant &share, const bool enable);
+ void toggleShareAllowResharing(const OCC::SharePtr &share, const bool enable);
+ void toggleShareAllowResharingFromQml(const QVariant &share, const bool enable);
void toggleSharePasswordProtect(const OCC::SharePtr &share, const bool enable);
void toggleSharePasswordProtectFromQml(const QVariant &share, const bool enable);
void toggleShareExpirationDate(const OCC::SharePtr &share, const bool enable) const;
void toggleShareExpirationDateFromQml(const QVariant &share, const bool enable) const;
void toggleShareNoteToRecipient(const OCC::SharePtr &share, const bool enable) const;
void toggleShareNoteToRecipientFromQml(const QVariant &share, const bool enable) const;
+ void changePermissionModeFromQml(const QVariant &share, const SharePermissionsMode permissionMode);
void setLinkShareLabel(const QSharedPointer &linkShare, const QString &label) const;
void setLinkShareLabelFromQml(const QVariant &linkShare, const QString &label) const;
@@ -164,6 +187,8 @@ private slots:
void handleSecureFileDropLinkShare();
void handleLinkShare();
void setupInternalLinkShare();
+ void setSharePermissionChangeInProgress(const QString &shareId, const bool isInProgress);
+ void setHideDownloadEnabledChangeInProgress(const QString &shareId, const bool isInProgress);
void slotPropfindReceived(const QVariantMap &result);
void slotServerError(const int code, const QString &message);
@@ -176,6 +201,7 @@ private slots:
void slotSharePermissionsSet(const QString &shareId);
void slotSharePasswordSet(const QString &shareId);
void slotShareNoteSet(const QString &shareId);
+ void slotHideDownloadSet(const QString &shareId);
void slotShareNameSet(const QString &shareId);
void slotShareLabelSet(const QString &shareId);
void slotShareExpireDateSet(const QString &shareId);
@@ -187,9 +213,13 @@ private:
[[nodiscard]] long long enforcedMaxExpireDateForShare(const SharePtr &share) const;
[[nodiscard]] bool expireDateEnforcedForShare(const SharePtr &share) const;
[[nodiscard]] bool validCapabilities() const;
+ [[nodiscard]] bool isSecureFileDropSupportedFolder() const;
+ [[nodiscard]] bool isEncryptedItem() const;
bool _fetchOngoing = false;
bool _hasInitialShareFetchCompleted = false;
+ bool _sharePermissionsChangeInProgress = false;
+ bool _hideDownloadEnabledChangeInProgress = false;
SharePtr _placeholderLinkShare;
SharePtr _internalLinkShare;
SharePtr _secureFileDropPlaceholderLinkShare;
@@ -201,8 +231,7 @@ private:
QString _sharePath;
SharePermissions _maxSharingPermissions;
QByteArray _numericFileId;
- bool _isEncryptedItem = false;
- bool _isSecureFileDropSupportedFolder = false;
+ SharedItemType _sharedItemType = SharedItemType::SharedItemTypeUndefined;
SyncJournalFileLockInfo _filelockState;
QString _privateLinkUrl;
diff --git a/src/gui/ocssharejob.cpp b/src/gui/ocssharejob.cpp
index dd370f338..2eee30c1a 100644
--- a/src/gui/ocssharejob.cpp
+++ b/src/gui/ocssharejob.cpp
@@ -134,6 +134,18 @@ void OcsShareJob::setLabel(const QString &shareId, const QString &label)
start();
}
+void OcsShareJob::setHideDownload(const QString &shareId, const bool hideDownload)
+{
+ appendPath(shareId);
+ setVerb("PUT");
+
+ const auto value = QString::fromLatin1(hideDownload ? QByteArrayLiteral("true") : QByteArrayLiteral("false"));
+ addParam(QStringLiteral("hideDownload"), value);
+ _value = hideDownload;
+
+ start();
+}
+
void OcsShareJob::createLinkShare(const QString &path,
const QString &name,
const QString &password)
diff --git a/src/gui/ocssharejob.h b/src/gui/ocssharejob.h
index ba75f94c0..2abaf80a4 100644
--- a/src/gui/ocssharejob.h
+++ b/src/gui/ocssharejob.h
@@ -102,6 +102,11 @@ public:
*/
void setLabel(const QString &shareId, const QString &label);
+ /**
+ * Set share hideDownload flag
+ */
+ void setHideDownload(const QString &shareId, const bool hideDownload);
+
/**
* Create a new link share
*
diff --git a/src/gui/sharemanager.cpp b/src/gui/sharemanager.cpp
index c285331e2..00f812c31 100644
--- a/src/gui/sharemanager.cpp
+++ b/src/gui/sharemanager.cpp
@@ -203,7 +203,8 @@ LinkShare::LinkShare(AccountPtr account,
const QUrl &url,
const QDate &expireDate,
const QString ¬e,
- const QString &label)
+ const QString &label,
+ const bool hideDownload)
: Share(account, id, uidowner, ownerDisplayName, path, Share::TypeLink, isPasswordSet, permissions)
, _name(name)
, _token(token)
@@ -211,6 +212,7 @@ LinkShare::LinkShare(AccountPtr account,
, _expireDate(expireDate)
, _url(url)
, _label(label)
+ , _hideDownload(hideDownload)
{
}
@@ -239,6 +241,11 @@ QString LinkShare::getLabel() const
return _label;
}
+bool LinkShare::getHideDownload() const
+{
+ return _hideDownload;
+}
+
void LinkShare::setName(const QString &name)
{
createShareJob(&LinkShare::slotNameSet)->setName(getId(), name);
@@ -270,6 +277,11 @@ void LinkShare::setLabel(const QString &label)
createShareJob(&LinkShare::slotLabelSet)->setLabel(getId(), label);
}
+void LinkShare::setHideDownload(const bool hideDownload)
+{
+ createShareJob(&LinkShare::slotHideDownloadSet)->setHideDownload(getId(), hideDownload);
+}
+
template
OcsShareJob *LinkShare::createShareJob(const LinkShareSlot slotFunction) {
auto *job = new OcsShareJob(_account);
@@ -308,6 +320,16 @@ void LinkShare::slotLabelSet(const QJsonDocument &, const QVariant &label)
}
}
+void LinkShare::slotHideDownloadSet(const QJsonDocument &jsonDoc, const QVariant &hideDownload)
+{
+ Q_UNUSED(jsonDoc);
+ if (!hideDownload.isValid()) {
+ return;
+ }
+ _hideDownload = hideDownload.toBool();
+ emit hideDownloadSet();
+}
+
UserGroupShare::UserGroupShare(AccountPtr account,
const QString &id,
const QString &owner,
@@ -583,7 +605,8 @@ QSharedPointer ShareManager::parseLinkShare(const QJsonObject &data)
url,
expireDate,
note,
- data.value("label").toString()));
+ data.value("label").toString(),
+ data.value("hide_download").toInt() == 1));
}
SharePtr ShareManager::parseShare(const QJsonObject &data) const
diff --git a/src/gui/sharemanager.h b/src/gui/sharemanager.h
index 37f5ee6f9..fa74ceaf3 100644
--- a/src/gui/sharemanager.h
+++ b/src/gui/sharemanager.h
@@ -132,6 +132,7 @@ signals:
void shareDeleted();
void serverError(int code, const QString &message);
void passwordSet();
+ void hideDownloadSet();
void passwordSetError(int statusCode, const QString &message);
public slots:
@@ -197,6 +198,7 @@ class LinkShare : public Share
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameSet)
Q_PROPERTY(QString note READ getNote WRITE setNote NOTIFY noteSet)
Q_PROPERTY(QString label READ getLabel WRITE setLabel NOTIFY labelSet)
+ Q_PROPERTY(bool hideDownload READ getHideDownload WRITE setHideDownload NOTIFY hideDownloadSet)
Q_PROPERTY(QDate expireDate READ getExpireDate WRITE setExpireDate NOTIFY expireDateSet)
Q_PROPERTY(QString token READ getToken CONSTANT)
@@ -213,7 +215,8 @@ public:
const QUrl &url,
const QDate &expireDate,
const QString ¬e,
- const QString &label);
+ const QString &label,
+ const bool hideDownload);
/*
* Get the share link
@@ -250,6 +253,11 @@ public:
*/
[[nodiscard]] QString getLabel() const;
+ /*
+ * Returns if the link share's hideDownload is true or false
+ */
+ [[nodiscard]] bool getHideDownload() const;
+
/*
* Returns the token of the link share.
*/
@@ -291,18 +299,25 @@ public slots:
* Set the label of the share link.
*/
void setLabel(const QString &label);
+
+ /*
+ * Set the hideDownload flag of the share link.
+ */
+ void setHideDownload(const bool hideDownload);
signals:
void expireDateSet();
void noteSet();
void nameSet();
void labelSet();
+ void hideDownloadSet();
private slots:
void slotNoteSet(const QJsonDocument &, const QVariant &value);
void slotExpireDateSet(const QJsonDocument &reply, const QVariant &value);
void slotNameSet(const QJsonDocument &, const QVariant &value);
void slotLabelSet(const QJsonDocument &, const QVariant &value);
+ void slotHideDownloadSet(const QJsonDocument &jsonDoc, const QVariant &hideDownload);
private:
QString _name;
@@ -311,6 +326,7 @@ private:
QDate _expireDate;
QUrl _url;
QString _label;
+ bool _hideDownload = false;
};
class UserGroupShare : public Share
diff --git a/theme/Style/Style.qml b/theme/Style/Style.qml
index 5c0453fe6..1d788c55d 100644
--- a/theme/Style/Style.qml
+++ b/theme/Style/Style.qml
@@ -122,6 +122,11 @@ QtObject {
readonly property int unifiedSearchResultSectionItemVerticalPadding: 8
readonly property int unifiedSearchResultNothingFoundHorizontalMargin: 10
+ readonly property int radioButtonCustomMarginLeftInner: 4
+ readonly property int radioButtonCustomMarginLeftOuter: 5
+ readonly property int radioButtonCustomRadius: 9
+ readonly property int radioButtonIndicatorSize: 16
+
readonly property var fontMetrics: FontMetrics {}
readonly property int activityContentSpace: 4