mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
1064 lines
35 KiB
C++
1064 lines
35 KiB
C++
/*
|
|
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "sharemodel.h"
|
|
|
|
#include <QFileInfo>
|
|
#include <QTimeZone>
|
|
|
|
#include "account.h"
|
|
#include "folderman.h"
|
|
#include "theme.h"
|
|
#include "wordlist.h"
|
|
|
|
namespace {
|
|
|
|
static const auto placeholderLinkShareId = QStringLiteral("__placeholderLinkShareId__");
|
|
static const auto internalLinkShareId = QStringLiteral("__internalLinkShareId__");
|
|
|
|
QString createRandomPassword()
|
|
{
|
|
const auto words = OCC::WordList::getRandomWords(10);
|
|
|
|
const auto addFirstLetter = [](const QString ¤t, const QString &next) -> QString {
|
|
return current + next.at(0);
|
|
};
|
|
|
|
return std::accumulate(std::cbegin(words), std::cend(words), QString(), addFirstLetter);
|
|
}
|
|
}
|
|
|
|
namespace OCC {
|
|
|
|
Q_LOGGING_CATEGORY(lcShareModel, "com.nextcloud.sharemodel")
|
|
|
|
ShareModel::ShareModel(QObject *parent)
|
|
: QAbstractListModel(parent)
|
|
{
|
|
}
|
|
|
|
// ---------------------- QAbstractListModel methods ---------------------- //
|
|
|
|
int ShareModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
if(parent.isValid() || !_accountState || _localPath.isEmpty()) {
|
|
return 0;
|
|
}
|
|
|
|
return _shares.count();
|
|
}
|
|
|
|
QHash<int, QByteArray> ShareModel::roleNames() const
|
|
{
|
|
auto roles = QAbstractListModel::roleNames();
|
|
roles[ShareRole] = "share";
|
|
roles[ShareTypeRole] = "shareType";
|
|
roles[ShareIdRole] = "shareId";
|
|
roles[IconUrlRole] = "iconUrl";
|
|
roles[AvatarUrlRole] = "avatarUrl";
|
|
roles[LinkRole] = "link";
|
|
roles[LinkShareNameRole] = "linkShareName";
|
|
roles[LinkShareLabelRole] = "linkShareLabel";
|
|
roles[NoteEnabledRole] = "noteEnabled";
|
|
roles[NoteRole] = "note";
|
|
roles[ExpireDateEnabledRole] = "expireDateEnabled";
|
|
roles[ExpireDateEnforcedRole] = "expireDateEnforced";
|
|
roles[ExpireDateRole] = "expireDate";
|
|
roles[EnforcedMaximumExpireDateRole] = "enforcedMaximumExpireDate";
|
|
roles[PasswordProtectEnabledRole] = "passwordProtectEnabled";
|
|
roles[PasswordRole] = "password";
|
|
roles[PasswordEnforcedRole] = "passwordEnforced";
|
|
roles[EditingAllowedRole] = "editingAllowed";
|
|
|
|
return roles;
|
|
}
|
|
|
|
QVariant ShareModel::data(const QModelIndex &index, const int role) const
|
|
{
|
|
Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
|
|
|
|
const auto share = _shares.at(index.row());
|
|
|
|
if (!share) {
|
|
return {};
|
|
}
|
|
|
|
// Some roles only provide values for the link and user/group share types
|
|
if(const auto linkShare = share.objectCast<LinkShare>()) {
|
|
switch(role) {
|
|
case LinkRole:
|
|
return linkShare->getLink();
|
|
case LinkShareNameRole:
|
|
return linkShare->getName();
|
|
case LinkShareLabelRole:
|
|
return linkShare->getLabel();
|
|
case NoteEnabledRole:
|
|
return !linkShare->getNote().isEmpty();
|
|
case NoteRole:
|
|
return linkShare->getNote();
|
|
case ExpireDateEnabledRole:
|
|
return linkShare->getExpireDate().isValid();
|
|
case ExpireDateRole:
|
|
{
|
|
const auto startOfExpireDayUTC = linkShare->getExpireDate().startOfDay(QTimeZone::utc());
|
|
return startOfExpireDayUTC.toMSecsSinceEpoch();
|
|
}
|
|
}
|
|
|
|
} else if (const auto userGroupShare = share.objectCast<UserGroupShare>()) {
|
|
switch(role) {
|
|
case NoteEnabledRole:
|
|
return !userGroupShare->getNote().isEmpty();
|
|
case NoteRole:
|
|
return userGroupShare->getNote();
|
|
case ExpireDateEnabledRole:
|
|
return userGroupShare->getExpireDate().isValid();
|
|
case ExpireDateRole:
|
|
{
|
|
const auto startOfExpireDayUTC = userGroupShare->getExpireDate().startOfDay(QTimeZone::utc());
|
|
return startOfExpireDayUTC.toMSecsSinceEpoch();
|
|
}
|
|
}
|
|
} else if (share->getShareType() == Share::TypeInternalLink && role == LinkRole) {
|
|
return _privateLinkUrl;
|
|
}
|
|
|
|
switch(role) {
|
|
case Qt::DisplayRole:
|
|
return displayStringForShare(share);
|
|
case ShareRole:
|
|
return QVariant::fromValue(share);
|
|
case ShareTypeRole:
|
|
return share->getShareType();
|
|
case ShareIdRole:
|
|
return share->getId();
|
|
case IconUrlRole:
|
|
return iconUrlForShare(share);
|
|
case AvatarUrlRole:
|
|
return avatarUrlForShare(share);
|
|
case ExpireDateEnforcedRole:
|
|
return expireDateEnforcedForShare(share);
|
|
case EnforcedMaximumExpireDateRole:
|
|
return enforcedMaxExpireDateForShare(share);
|
|
case PasswordProtectEnabledRole:
|
|
return share->isPasswordSet();
|
|
case PasswordRole:
|
|
if (!share->isPasswordSet() || !_shareIdRecentlySetPasswords.contains(share->getId())) {
|
|
return {};
|
|
}
|
|
return _shareIdRecentlySetPasswords.value(share->getId());
|
|
case PasswordEnforcedRole:
|
|
return _accountState && _accountState->account() && _accountState->account()->capabilities().isValid() &&
|
|
((share->getShareType() == Share::TypeEmail && _accountState->account()->capabilities().shareEmailPasswordEnforced()) ||
|
|
(share->getShareType() == Share::TypeLink && _accountState->account()->capabilities().sharePublicLinkEnforcePassword()));
|
|
case EditingAllowedRole:
|
|
return share->getPermissions().testFlag(SharePermissionUpdate);
|
|
|
|
// Deal with roles that only return certain values for link or user/group share types
|
|
case NoteEnabledRole:
|
|
case ExpireDateEnabledRole:
|
|
return false;
|
|
case LinkRole:
|
|
case LinkShareNameRole:
|
|
case LinkShareLabelRole:
|
|
case NoteRole:
|
|
case ExpireDateRole:
|
|
return {};
|
|
}
|
|
|
|
qCWarning(lcShareModel) << "Got unknown role" << role
|
|
<< "for share of type" << share->getShareType()
|
|
<< "so returning null value.";
|
|
return {};
|
|
}
|
|
|
|
// ---------------------- Internal model data methods ---------------------- //
|
|
|
|
void ShareModel::resetData()
|
|
{
|
|
beginResetModel();
|
|
|
|
_folder = nullptr;
|
|
_sharePath.clear();
|
|
_maxSharingPermissions = {};
|
|
_numericFileId.clear();
|
|
_privateLinkUrl.clear();
|
|
_filelockState = {};
|
|
_manager.clear();
|
|
_shares.clear();
|
|
_fetchOngoing = false;
|
|
_hasInitialShareFetchCompleted = false;
|
|
_sharees.clear();
|
|
|
|
Q_EMIT sharePermissionsChanged();
|
|
Q_EMIT fetchOngoingChanged();
|
|
Q_EMIT hasInitialShareFetchCompletedChanged();
|
|
Q_EMIT shareesChanged();
|
|
|
|
endResetModel();
|
|
}
|
|
|
|
void ShareModel::updateData()
|
|
{
|
|
resetData();
|
|
|
|
if (_localPath.isEmpty() || !_accountState || _accountState->account().isNull()) {
|
|
qCWarning(lcShareModel) << "Not updating share model data. Local path is:" << _localPath
|
|
<< "Is account state null:" << !_accountState;
|
|
return;
|
|
}
|
|
|
|
if (!sharingEnabled()) {
|
|
qCWarning(lcShareModel) << "Server does not support sharing";
|
|
return;
|
|
}
|
|
|
|
_folder = FolderMan::instance()->folderForPath(_localPath);
|
|
|
|
if (!_folder) {
|
|
qCWarning(lcShareModel) << "Could not update share model data for" << _localPath << "no responsible folder found";
|
|
resetData();
|
|
return;
|
|
}
|
|
|
|
qCDebug(lcShareModel) << "Updating share model data now.";
|
|
|
|
const auto relPath = _localPath.mid(_folder->cleanPath().length() + 1);
|
|
_sharePath = _folder->remotePathTrailingSlash() + relPath;
|
|
|
|
SyncJournalFileRecord fileRecord;
|
|
auto resharingAllowed = true; // lets assume the good
|
|
|
|
if(_folder->journalDb()->getFileRecord(relPath, &fileRecord) && fileRecord.isValid()) {
|
|
if (!fileRecord._remotePerm.isNull() &&
|
|
!fileRecord._remotePerm.hasPermission(RemotePermissions::CanReshare)) {
|
|
|
|
resharingAllowed = false;
|
|
}
|
|
}
|
|
|
|
_maxSharingPermissions = resharingAllowed ? SharePermissions(_accountState->account()->capabilities().shareDefaultPermissions()) : SharePermissions({});
|
|
Q_EMIT sharePermissionsChanged();
|
|
|
|
_numericFileId = fileRecord.numericFileId();
|
|
|
|
// Will get added when shares are fetched if no link shares are fetched
|
|
_placeholderLinkShare.reset(new Share(_accountState->account(),
|
|
placeholderLinkShareId,
|
|
_accountState->account()->id(),
|
|
_accountState->account()->davDisplayName(),
|
|
_sharePath,
|
|
Share::TypePlaceholderLink));
|
|
|
|
_internalLinkShare.reset(new Share(_accountState->account(),
|
|
internalLinkShareId,
|
|
_accountState->account()->id(),
|
|
_accountState->account()->davDisplayName(),
|
|
_sharePath,
|
|
Share::TypeInternalLink));
|
|
|
|
auto job = new PropfindJob(_accountState->account(), _sharePath);
|
|
job->setProperties(
|
|
QList<QByteArray>()
|
|
<< "http://open-collaboration-services.org/ns:share-permissions"
|
|
<< "http://owncloud.org/ns:fileid" // numeric file id for fallback private link generation
|
|
<< "http://owncloud.org/ns:privatelink");
|
|
job->setTimeout(10 * 1000);
|
|
connect(job, &PropfindJob::result, this, &ShareModel::slotPropfindReceived);
|
|
connect(job, &PropfindJob::finishedWithError, this, [&](const QNetworkReply *reply) {
|
|
qCWarning(lcShareModel) << "Propfind for" << _sharePath << "failed";
|
|
_fetchOngoing = false;
|
|
Q_EMIT fetchOngoingChanged();
|
|
Q_EMIT serverError(reply->error(), reply->errorString());
|
|
});
|
|
|
|
_fetchOngoing = true;
|
|
Q_EMIT fetchOngoingChanged();
|
|
job->start();
|
|
|
|
initShareManager();
|
|
}
|
|
|
|
void ShareModel::initShareManager()
|
|
{
|
|
if (!_accountState || _accountState->account().isNull()) {
|
|
return;
|
|
}
|
|
|
|
bool sharingPossible = true;
|
|
if (!publicLinkSharesEnabled()) {
|
|
qCWarning(lcSharing) << "Link shares have been disabled";
|
|
sharingPossible = false;
|
|
} else if (!canShare()) {
|
|
qCWarning(lcSharing) << "The file cannot be shared because it does not have sharing permission.";
|
|
sharingPossible = false;
|
|
}
|
|
|
|
if (_manager.isNull() && sharingPossible) {
|
|
_manager.reset(new ShareManager(_accountState->account(), this));
|
|
connect(_manager.data(), &ShareManager::sharesFetched, this, &ShareModel::slotSharesFetched);
|
|
connect(_manager.data(), &ShareManager::shareCreated, this, [&]{ _manager->fetchShares(_sharePath); });
|
|
connect(_manager.data(), &ShareManager::linkShareCreated, this, &ShareModel::slotAddShare);
|
|
connect(_manager.data(), &ShareManager::linkShareRequiresPassword, this, &ShareModel::requestPasswordForLinkShare);
|
|
connect(_manager.data(), &ShareManager::serverError, this, [this](const int code, const QString &message){
|
|
_hasInitialShareFetchCompleted = true;
|
|
Q_EMIT hasInitialShareFetchCompletedChanged();
|
|
emit serverError(code, message);
|
|
});
|
|
|
|
_manager->fetchShares(_sharePath);
|
|
}
|
|
}
|
|
|
|
void ShareModel::handlePlaceholderLinkShare()
|
|
{
|
|
// We want to add the placeholder if there are no link shares and
|
|
// if we are not already showing the placeholder link share
|
|
auto linkSharePresent = false;
|
|
auto placeholderLinkSharePresent = false;
|
|
|
|
for (const auto &share : qAsConst(_shares)) {
|
|
const auto shareType = share->getShareType();
|
|
|
|
if (!linkSharePresent && shareType == Share::TypeLink) {
|
|
linkSharePresent = true;
|
|
} else if (!placeholderLinkSharePresent && shareType == Share::TypePlaceholderLink) {
|
|
placeholderLinkSharePresent = true;
|
|
}
|
|
|
|
if(linkSharePresent && placeholderLinkSharePresent) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (linkSharePresent && placeholderLinkSharePresent) {
|
|
slotRemoveShareWithId(placeholderLinkShareId);
|
|
} else if (!linkSharePresent && !placeholderLinkSharePresent) {
|
|
slotAddShare(_placeholderLinkShare);
|
|
}
|
|
}
|
|
|
|
void ShareModel::slotPropfindReceived(const QVariantMap &result)
|
|
{
|
|
_fetchOngoing = false;
|
|
Q_EMIT fetchOngoingChanged();
|
|
|
|
const QVariant receivedPermissions = result["share-permissions"];
|
|
if (!receivedPermissions.toString().isEmpty()) {
|
|
_maxSharingPermissions = static_cast<SharePermissions>(receivedPermissions.toInt());
|
|
Q_EMIT sharePermissionsChanged();
|
|
qCInfo(lcShareModel) << "Received sharing permissions for" << _sharePath << _maxSharingPermissions;
|
|
}
|
|
|
|
const auto privateLinkUrl = result["privatelink"].toString();
|
|
const auto numericFileId = result["fileid"].toByteArray();
|
|
|
|
if (!privateLinkUrl.isEmpty()) {
|
|
qCInfo(lcShareModel) << "Received private link url for" << _sharePath << privateLinkUrl;
|
|
_privateLinkUrl = privateLinkUrl;
|
|
} else if (!numericFileId.isEmpty()) {
|
|
qCInfo(lcShareModel) << "Received numeric file id for" << _sharePath << numericFileId;
|
|
_privateLinkUrl = _accountState->account()->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded);
|
|
}
|
|
|
|
setupInternalLinkShare();
|
|
}
|
|
|
|
void ShareModel::slotSharesFetched(const QList<SharePtr> &shares)
|
|
{
|
|
if(!_hasInitialShareFetchCompleted) {
|
|
_hasInitialShareFetchCompleted = true;
|
|
Q_EMIT hasInitialShareFetchCompletedChanged();
|
|
}
|
|
|
|
qCInfo(lcSharing) << "Fetched" << shares.count() << "shares";
|
|
|
|
for (const auto &share : shares) {
|
|
if (share.isNull() ||
|
|
share->account().isNull() ||
|
|
share->getUidOwner() != share->account()->davUser()) {
|
|
|
|
continue;
|
|
}
|
|
|
|
slotAddShare(share);
|
|
}
|
|
|
|
handlePlaceholderLinkShare();
|
|
}
|
|
|
|
void ShareModel::setupInternalLinkShare()
|
|
{
|
|
if (!_accountState ||
|
|
_accountState->account().isNull() ||
|
|
_localPath.isEmpty() ||
|
|
_privateLinkUrl.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
beginInsertRows({}, _shares.count(), _shares.count());
|
|
_shares.append(_internalLinkShare);
|
|
endInsertRows();
|
|
Q_EMIT internalLinkReady();
|
|
}
|
|
|
|
void ShareModel::slotAddShare(const SharePtr &share)
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
const auto shareId = share->getId();
|
|
QModelIndex shareModelIndex;
|
|
|
|
if (_shareIdIndexHash.contains(shareId)) {
|
|
const auto sharePersistentModelIndex = _shareIdIndexHash.value(shareId);
|
|
const auto shareIndex = sharePersistentModelIndex.row();
|
|
|
|
_shares.replace(shareIndex, share);
|
|
|
|
shareModelIndex = index(sharePersistentModelIndex.row());
|
|
Q_EMIT dataChanged(shareModelIndex, shareModelIndex);
|
|
} else {
|
|
const auto shareIndex = _shares.count();
|
|
|
|
beginInsertRows({}, _shares.count(), _shares.count());
|
|
_shares.append(share);
|
|
endInsertRows();
|
|
|
|
slotAddSharee(share->getShareWith());
|
|
|
|
shareModelIndex = index(shareIndex);
|
|
}
|
|
|
|
const QPersistentModelIndex sharePersistentIndex(shareModelIndex);
|
|
_shareIdIndexHash.insert(shareId, sharePersistentIndex);
|
|
|
|
connect(share.data(), &Share::serverError, this, &ShareModel::slotServerError);
|
|
connect(share.data(), &Share::passwordSetError, this, [this, shareId](const int code, const QString &message) {
|
|
_shareIdRecentlySetPasswords.remove(shareId);
|
|
slotSharePasswordSet(shareId);
|
|
Q_EMIT passwordSetError(shareId, code, message);
|
|
});
|
|
|
|
// Passing shareId by reference here will cause crashing, so we pass by value
|
|
connect(share.data(), &Share::shareDeleted, this, [this, shareId]{ slotRemoveShareWithId(shareId); });
|
|
connect(share.data(), &Share::permissionsSet, this, [this, shareId]{ slotSharePermissionsSet(shareId); });
|
|
connect(share.data(), &Share::passwordSet, this, [this, shareId]{ slotSharePasswordSet(shareId); });
|
|
|
|
if (const auto linkShare = share.objectCast<LinkShare>()) {
|
|
connect(linkShare.data(), &LinkShare::noteSet, this, [this, shareId]{ slotShareNoteSet(shareId); });
|
|
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); });
|
|
} else if (const auto userGroupShare = share.objectCast<UserGroupShare>()) {
|
|
connect(userGroupShare.data(), &UserGroupShare::noteSet, this, [this, shareId]{ slotShareNoteSet(shareId); });
|
|
connect(userGroupShare.data(), &UserGroupShare::expireDateSet, this, [this, shareId]{ slotShareExpireDateSet(shareId); });
|
|
}
|
|
|
|
if (_manager) {
|
|
connect(_manager.data(), &ShareManager::serverError, this, &ShareModel::slotServerError);
|
|
}
|
|
|
|
handlePlaceholderLinkShare();
|
|
Q_EMIT sharesChanged();
|
|
}
|
|
|
|
void ShareModel::slotRemoveShareWithId(const QString &shareId)
|
|
{
|
|
if (_shares.empty() || shareId.isEmpty() || !_shareIdIndexHash.contains(shareId)) {
|
|
return;
|
|
}
|
|
|
|
_shareIdRecentlySetPasswords.remove(shareId);
|
|
const auto shareIndex = _shareIdIndexHash.take(shareId);
|
|
|
|
if (!checkIndex(shareIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::ParentIsInvalid)) {
|
|
qCWarning(lcShareModel) << "Won't remove share with id:" << shareId
|
|
<< ", invalid share index: " << shareIndex;
|
|
return;
|
|
}
|
|
|
|
const auto share = shareIndex.data(ShareModel::ShareRole).value<SharePtr>();
|
|
const auto sharee = share->getShareWith();
|
|
slotRemoveSharee(sharee);
|
|
|
|
beginRemoveRows({}, shareIndex.row(), shareIndex.row());
|
|
_shares.removeAt(shareIndex.row());
|
|
endRemoveRows();
|
|
|
|
handlePlaceholderLinkShare();
|
|
|
|
Q_EMIT sharesChanged();
|
|
}
|
|
|
|
void ShareModel::slotServerError(const int code, const QString &message)
|
|
{
|
|
qCWarning(lcShareModel) << "Error from server" << code << message;
|
|
Q_EMIT serverError(code, message);
|
|
}
|
|
|
|
void ShareModel::slotAddSharee(const ShareePtr &sharee)
|
|
{
|
|
if(!sharee) {
|
|
return;
|
|
}
|
|
|
|
_sharees.append(sharee);
|
|
Q_EMIT shareesChanged();
|
|
}
|
|
|
|
void ShareModel::slotRemoveSharee(const ShareePtr &sharee)
|
|
{
|
|
_sharees.removeAll(sharee);
|
|
Q_EMIT shareesChanged();
|
|
}
|
|
|
|
QString ShareModel::displayStringForShare(const SharePtr &share) const
|
|
{
|
|
if (const auto linkShare = share.objectCast<LinkShare>()) {
|
|
const auto displayString = tr("Share link");
|
|
|
|
if (!linkShare->getLabel().isEmpty()) {
|
|
return QStringLiteral("%1 (%2)").arg(displayString, linkShare->getLabel());
|
|
}
|
|
|
|
return displayString;
|
|
} else if (share->getShareType() == Share::TypePlaceholderLink) {
|
|
return tr("Link share");
|
|
} else if (share->getShareType() == Share::TypeInternalLink) {
|
|
return tr("Internal link");
|
|
} else if (share->getShareWith()) {
|
|
return share->getShareWith()->format();
|
|
}
|
|
|
|
qCWarning(lcShareModel) << "Unable to provide good display string for share";
|
|
return QStringLiteral("Share");
|
|
}
|
|
|
|
QString ShareModel::iconUrlForShare(const SharePtr &share) const
|
|
{
|
|
const auto iconsPath = QStringLiteral("image://svgimage-custom-color/");
|
|
|
|
switch(share->getShareType()) {
|
|
case Share::TypeInternalLink:
|
|
return QString(iconsPath + QStringLiteral("external.svg"));
|
|
case Share::TypePlaceholderLink:
|
|
case Share::TypeLink:
|
|
return QString(iconsPath + QStringLiteral("public.svg"));
|
|
case Share::TypeEmail:
|
|
return QString(iconsPath + QStringLiteral("email.svg"));
|
|
case Share::TypeRoom:
|
|
return QString(iconsPath + QStringLiteral("wizard-talk.svg"));
|
|
case Share::TypeUser:
|
|
return QString(iconsPath + QStringLiteral("user.svg"));
|
|
case Share::TypeGroup:
|
|
return QString(iconsPath + QStringLiteral("wizard-groupware.svg"));
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
|
|
QString ShareModel::avatarUrlForShare(const SharePtr &share) const
|
|
{
|
|
if (share->getShareWith() && share->getShareWith()->type() == Sharee::User && _accountState && _accountState->account()) {
|
|
const auto provider = QStringLiteral("image://tray-image-provider/");
|
|
const auto userId = share->getShareWith()->shareWith();
|
|
const auto avatarUrl = Utility::concatUrlPath(_accountState->account()->url(),
|
|
QString("remote.php/dav/avatars/%1/%2.png").arg(userId, QString::number(64))).toString();
|
|
return QString(provider + avatarUrl);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
long long ShareModel::enforcedMaxExpireDateForShare(const SharePtr &share) const
|
|
{
|
|
if (!_accountState || !_accountState->account() || !_accountState->account()->capabilities().isValid()) {
|
|
return {};
|
|
}
|
|
|
|
auto expireDays = -1;
|
|
|
|
// Both public links and emails count as "public" shares
|
|
if ((share->getShareType() == Share::TypeLink || share->getShareType() == Share::TypeEmail)
|
|
&& _accountState->account()->capabilities().sharePublicLinkEnforceExpireDate()) {
|
|
expireDays = _accountState->account()->capabilities().sharePublicLinkExpireDateDays();
|
|
|
|
} else if (share->getShareType() == Share::TypeRemote && _accountState->account()->capabilities().shareRemoteEnforceExpireDate()) {
|
|
expireDays = _accountState->account()->capabilities().shareRemoteExpireDateDays();
|
|
|
|
} else if ((share->getShareType() == Share::TypeUser ||
|
|
share->getShareType() == Share::TypeGroup ||
|
|
share->getShareType() == Share::TypeCircle ||
|
|
share->getShareType() == Share::TypeRoom) &&
|
|
_accountState->account()->capabilities().shareInternalEnforceExpireDate()) {
|
|
expireDays = _accountState->account()->capabilities().shareInternalExpireDateDays();
|
|
|
|
} else {
|
|
return {};
|
|
}
|
|
|
|
const auto expireDateTime = QDate::currentDate().addDays(expireDays).startOfDay(QTimeZone::utc());
|
|
return expireDateTime.toMSecsSinceEpoch();
|
|
}
|
|
|
|
bool ShareModel::expireDateEnforcedForShare(const SharePtr &share) const
|
|
{
|
|
if(!_accountState || !_accountState->account() || !_accountState->account()->capabilities().isValid()) {
|
|
return false;
|
|
}
|
|
|
|
// Both public links and emails count as "public" shares
|
|
if (share->getShareType() == Share::TypeLink ||
|
|
share->getShareType() == Share::TypeEmail) {
|
|
return _accountState->account()->capabilities().sharePublicLinkEnforceExpireDate();
|
|
|
|
} else if (share->getShareType() == Share::TypeRemote) {
|
|
return _accountState->account()->capabilities().shareRemoteEnforceExpireDate();
|
|
|
|
} else if (share->getShareType() == Share::TypeUser ||
|
|
share->getShareType() == Share::TypeGroup ||
|
|
share->getShareType() == Share::TypeCircle ||
|
|
share->getShareType() == Share::TypeRoom) {
|
|
return _accountState->account()->capabilities().shareInternalEnforceExpireDate();
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------- Shares modified signal handling slots ----------------- //
|
|
|
|
void ShareModel::slotSharePermissionsSet(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, { EditingAllowedRole });
|
|
}
|
|
|
|
void ShareModel::slotSharePasswordSet(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, { PasswordProtectEnabledRole, PasswordRole });
|
|
}
|
|
|
|
void ShareModel::slotShareNoteSet(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, { NoteEnabledRole, NoteRole });
|
|
}
|
|
|
|
void ShareModel::slotShareNameSet(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, { LinkShareNameRole });
|
|
}
|
|
|
|
void ShareModel::slotShareLabelSet(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, { Qt::DisplayRole, LinkShareLabelRole });
|
|
}
|
|
|
|
void ShareModel::slotShareExpireDateSet(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, { ExpireDateEnabledRole, ExpireDateRole });
|
|
}
|
|
|
|
// ----------------------- Shares modification slots ----------------------- //
|
|
|
|
void ShareModel::toggleShareAllowEditing(const SharePtr &share, const bool enable) const
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
auto permissions = share->getPermissions();
|
|
enable ? permissions |= SharePermissionUpdate : permissions &= ~SharePermissionUpdate;
|
|
|
|
share->setPermissions(permissions);
|
|
}
|
|
|
|
void ShareModel::toggleShareAllowEditingFromQml(const QVariant &share, const bool enable) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
toggleShareAllowEditing(ptr, enable);
|
|
}
|
|
|
|
void ShareModel::toggleShareAllowResharing(const SharePtr &share, const bool enable) const
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
auto permissions = share->getPermissions();
|
|
enable ? permissions |= SharePermissionShare : permissions &= ~SharePermissionShare;
|
|
|
|
share->setPermissions(permissions);
|
|
}
|
|
|
|
void ShareModel::toggleShareAllowResharingFromQml(const QVariant &share, const bool enable) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
toggleShareAllowResharing(ptr, enable);
|
|
}
|
|
|
|
void ShareModel::toggleSharePasswordProtect(const SharePtr &share, const bool enable)
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
if(!enable) {
|
|
share->setPassword({});
|
|
return;
|
|
}
|
|
|
|
const auto randomPassword = createRandomPassword();
|
|
_shareIdRecentlySetPasswords.insert(share->getId(), randomPassword);
|
|
share->setPassword(randomPassword);
|
|
}
|
|
|
|
void ShareModel::toggleSharePasswordProtectFromQml(const QVariant &share, const bool enable)
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
toggleSharePasswordProtect(ptr, enable);
|
|
}
|
|
|
|
void ShareModel::toggleShareExpirationDate(const SharePtr &share, const bool enable) const
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
const auto expireDate = enable ? QDate::currentDate().addDays(1) : QDate();
|
|
|
|
if (const auto linkShare = share.objectCast<LinkShare>()) {
|
|
linkShare->setExpireDate(expireDate);
|
|
} else if (const auto userGroupShare = share.objectCast<UserGroupShare>()) {
|
|
userGroupShare->setExpireDate(expireDate);
|
|
}
|
|
}
|
|
|
|
void ShareModel::toggleShareExpirationDateFromQml(const QVariant &share, const bool enable) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
toggleShareExpirationDate(ptr, enable);
|
|
}
|
|
|
|
void ShareModel::toggleShareNoteToRecipient(const SharePtr &share, const bool enable) const
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
const QString note = enable ? tr("Enter a note for the recipient") : QString();
|
|
if (const auto linkShare = share.objectCast<LinkShare>()) {
|
|
linkShare->setNote(note);
|
|
} else if (const auto userGroupShare = share.objectCast<UserGroupShare>()) {
|
|
userGroupShare->setNote(note);
|
|
}
|
|
}
|
|
|
|
void ShareModel::toggleShareNoteToRecipientFromQml(const QVariant &share, const bool enable) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
toggleShareNoteToRecipient(ptr, enable);
|
|
}
|
|
|
|
void ShareModel::setLinkShareLabel(const QSharedPointer<LinkShare> &linkShare, const QString &label) const
|
|
{
|
|
if (linkShare.isNull()) {
|
|
return;
|
|
}
|
|
|
|
linkShare->setLabel(label);
|
|
}
|
|
|
|
void ShareModel::setLinkShareLabelFromQml(const QVariant &linkShare, const QString &label) const
|
|
{
|
|
// All of our internal share pointers are SharePtr, so cast to LinkShare for this method
|
|
const auto ptr = linkShare.value<SharePtr>().objectCast<LinkShare>();
|
|
setLinkShareLabel(ptr, label);
|
|
}
|
|
|
|
void ShareModel::setShareExpireDate(const SharePtr &share, const qint64 milliseconds) const
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
const auto date = QDateTime::fromMSecsSinceEpoch(milliseconds, QTimeZone::utc()).date();
|
|
|
|
if (const auto linkShare = share.objectCast<LinkShare>()) {
|
|
linkShare->setExpireDate(date);
|
|
} else if (const auto userGroupShare = share.objectCast<UserGroupShare>()) {
|
|
userGroupShare->setExpireDate(date);
|
|
}
|
|
}
|
|
|
|
void ShareModel::setShareExpireDateFromQml(const QVariant &share, const QVariant milliseconds) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
const auto millisecondsLL = milliseconds.toLongLong();
|
|
setShareExpireDate(ptr, millisecondsLL);
|
|
}
|
|
|
|
void ShareModel::setSharePassword(const SharePtr &share, const QString &password)
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
_shareIdRecentlySetPasswords.insert(share->getId(), password);
|
|
share->setPassword(password);
|
|
}
|
|
|
|
void ShareModel::setSharePasswordFromQml(const QVariant &share, const QString &password)
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
setSharePassword(ptr, password);
|
|
}
|
|
|
|
void ShareModel::setShareNote(const SharePtr &share, const QString ¬e) const
|
|
{
|
|
if (share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
if (const auto linkShare = share.objectCast<LinkShare>()) {
|
|
linkShare->setNote(note);
|
|
} else if (const auto userGroupShare = share.objectCast<UserGroupShare>()) {
|
|
userGroupShare->setNote(note);
|
|
}
|
|
}
|
|
|
|
void ShareModel::setShareNoteFromQml(const QVariant &share, const QString ¬e) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
setShareNote(ptr, note);
|
|
}
|
|
|
|
// ------------------- Share creation and deletion slots ------------------- //
|
|
|
|
void ShareModel::createNewLinkShare() const
|
|
{
|
|
if (_manager) {
|
|
const auto askOptionalPassword = _accountState->account()->capabilities().sharePublicLinkAskOptionalPassword();
|
|
const auto password = askOptionalPassword ? createRandomPassword() : QString();
|
|
_manager->createLinkShare(_sharePath, QString(), password);
|
|
}
|
|
}
|
|
|
|
void ShareModel::createNewLinkShareWithPassword(const QString &password) const
|
|
{
|
|
if (_manager) {
|
|
_manager->createLinkShare(_sharePath, QString(), password);
|
|
}
|
|
}
|
|
|
|
void ShareModel::createNewUserGroupShare(const ShareePtr &sharee)
|
|
{
|
|
if (sharee.isNull()) {
|
|
return;
|
|
}
|
|
|
|
qCInfo(lcShareModel) << "Creating new user/group share for sharee: " << sharee->format();
|
|
|
|
if (sharee->type() == Sharee::Email &&
|
|
_accountState &&
|
|
!_accountState->account().isNull() &&
|
|
_accountState->account()->capabilities().isValid() &&
|
|
_accountState->account()->capabilities().shareEmailPasswordEnforced()) {
|
|
|
|
Q_EMIT requestPasswordForEmailSharee(sharee);
|
|
return;
|
|
}
|
|
|
|
_manager->createShare(_sharePath,
|
|
Share::ShareType(sharee->type()),
|
|
sharee->shareWith(),
|
|
_maxSharingPermissions,
|
|
{});
|
|
}
|
|
|
|
void ShareModel::createNewUserGroupShareWithPassword(const ShareePtr &sharee, const QString &password) const
|
|
{
|
|
if (sharee.isNull()) {
|
|
return;
|
|
}
|
|
|
|
_manager->createShare(_sharePath,
|
|
Share::ShareType(sharee->type()),
|
|
sharee->shareWith(),
|
|
_maxSharingPermissions,
|
|
password);
|
|
}
|
|
|
|
void ShareModel::createNewUserGroupShareFromQml(const QVariant &sharee)
|
|
{
|
|
const auto ptr = sharee.value<ShareePtr>();
|
|
createNewUserGroupShare(ptr);
|
|
}
|
|
|
|
void ShareModel::createNewUserGroupShareWithPasswordFromQml(const QVariant &sharee, const QString &password) const
|
|
{
|
|
const auto ptr = sharee.value<ShareePtr>();
|
|
createNewUserGroupShareWithPassword(ptr, password);
|
|
}
|
|
|
|
void ShareModel::deleteShare(const SharePtr &share) const
|
|
{
|
|
if(share.isNull()) {
|
|
return;
|
|
}
|
|
|
|
share->deleteShare();
|
|
}
|
|
|
|
void ShareModel::deleteShareFromQml(const QVariant &share) const
|
|
{
|
|
const auto ptr = share.value<SharePtr>();
|
|
deleteShare(ptr);
|
|
}
|
|
|
|
// --------------------------- QPROPERTY methods --------------------------- //
|
|
|
|
QString ShareModel::localPath() const
|
|
{
|
|
return _localPath;
|
|
}
|
|
|
|
void ShareModel::setLocalPath(const QString &localPath)
|
|
{
|
|
if (localPath == _localPath) {
|
|
return;
|
|
}
|
|
|
|
_localPath = localPath;
|
|
Q_EMIT localPathChanged();
|
|
updateData();
|
|
}
|
|
|
|
AccountState *ShareModel::accountState() const
|
|
{
|
|
return _accountState;
|
|
}
|
|
|
|
void ShareModel::setAccountState(AccountState *accountState)
|
|
{
|
|
if (accountState == _accountState) {
|
|
return;
|
|
}
|
|
|
|
_accountState = accountState;
|
|
|
|
// Change the server and account-related properties
|
|
connect(_accountState, &AccountState::stateChanged, this, &ShareModel::accountConnectedChanged);
|
|
connect(_accountState, &AccountState::stateChanged, this, &ShareModel::sharingEnabledChanged);
|
|
connect(_accountState, &AccountState::stateChanged, this, &ShareModel::publicLinkSharesEnabledChanged);
|
|
connect(_accountState, &AccountState::stateChanged, this, &ShareModel::userGroupSharingEnabledChanged);
|
|
|
|
Q_EMIT accountStateChanged();
|
|
Q_EMIT accountConnectedChanged();
|
|
Q_EMIT sharingEnabledChanged();
|
|
Q_EMIT publicLinkSharesEnabledChanged();
|
|
Q_EMIT userGroupSharingEnabledChanged();
|
|
updateData();
|
|
}
|
|
|
|
bool ShareModel::accountConnected() const
|
|
{
|
|
return _accountState && _accountState->isConnected();
|
|
}
|
|
|
|
bool ShareModel::sharingEnabled() const
|
|
{
|
|
return _accountState &&
|
|
_accountState->account() &&
|
|
_accountState->account()->capabilities().isValid() &&
|
|
_accountState->account()->capabilities().shareAPI();
|
|
}
|
|
|
|
bool ShareModel::publicLinkSharesEnabled() const
|
|
{
|
|
return Theme::instance()->linkSharing() &&
|
|
_accountState &&
|
|
_accountState->account() &&
|
|
_accountState->account()->capabilities().isValid() &&
|
|
_accountState->account()->capabilities().sharePublicLink();
|
|
}
|
|
|
|
bool ShareModel::userGroupSharingEnabled() const
|
|
{
|
|
return Theme::instance()->userGroupSharing();
|
|
}
|
|
|
|
bool ShareModel::fetchOngoing() const
|
|
{
|
|
return _fetchOngoing;
|
|
}
|
|
|
|
bool ShareModel::hasInitialShareFetchCompleted() const
|
|
{
|
|
return _hasInitialShareFetchCompleted;
|
|
}
|
|
|
|
bool ShareModel::canShare() const
|
|
{
|
|
return _maxSharingPermissions & SharePermissionShare;
|
|
}
|
|
|
|
QVariantList ShareModel::sharees() const
|
|
{
|
|
QVariantList returnSharees;
|
|
for (const auto &sharee : _sharees) {
|
|
returnSharees.append(QVariant::fromValue(sharee));
|
|
}
|
|
return returnSharees;
|
|
}
|
|
|
|
} // namespace OCC
|