From a54c5a8fdf5f35bd336c0f719b4999074b7a0192 Mon Sep 17 00:00:00 2001 From: Shuai Lin Date: Wed, 2 Dec 2015 21:16:36 +0800 Subject: [PATCH] support private share in cloud file browser --- .clang-format | 4 + CMakeLists.txt | 3 + qt-linux.css | 4 + qt.css | 16 + src/api/contact-share-info.h | 58 ++ src/api/requests.cpp | 509 +++++++++---- src/api/requests.h | 266 +++++-- src/filebrowser/file-browser-dialog.cpp | 12 + src/filebrowser/file-browser-dialog.h | 1 + src/filebrowser/file-table.cpp | 43 ++ src/filebrowser/file-table.h | 6 + src/ui/private-share-dialog.cpp | 922 ++++++++++++++++++++++++ src/ui/private-share-dialog.h | 220 ++++++ src/utils/json-utils.cpp | 22 + src/utils/json-utils.h | 3 +- ui/private-share-dialog.ui | 114 +++ 16 files changed, 2017 insertions(+), 186 deletions(-) create mode 100644 qt-linux.css create mode 100644 src/api/contact-share-info.h create mode 100644 src/ui/private-share-dialog.cpp create mode 100644 src/ui/private-share-dialog.h create mode 100644 ui/private-share-dialog.ui diff --git a/.clang-format b/.clang-format index 941fd29fb..a3b3244fd 100644 --- a/.clang-format +++ b/.clang-format @@ -34,6 +34,10 @@ AccessModifierOffset: -4 # AlignTrailingComments: false AllowShortFunctionsOnASingleLine: false +AllowShortBlocksOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false # Good: # diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e89d63ab..c8da9498d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,6 +213,7 @@ SET(moc_headers src/ui/init-vdrive-dialog.h src/ui/uninstall-helper-dialog.h src/ui/ssl-confirm-dialog.h + src/ui/private-share-dialog.h src/ui/account-view.h src/ui/seafile-tab-widget.h src/ui/tab-view.h @@ -266,6 +267,7 @@ SET(ui_files ui/init-vdrive-dialog.ui ui/uninstall-helper-dialog.ui ui/ssl-confirm-dialog.ui + ui/private-share-dialog.ui ui/account-view.ui ui/set-repo-password-dialog.ui ${platform_specific_ui_files} @@ -400,6 +402,7 @@ SET(seafile_client_sources src/ui/search-tab.cpp src/ui/search-tab-items.cpp src/ui/ssl-confirm-dialog.cpp + src/ui/private-share-dialog.cpp src/ui/proxy-style.cpp src/ui/account-view.cpp src/ui/seafile-tab-widget.cpp diff --git a/qt-linux.css b/qt-linux.css new file mode 100644 index 000000000..8a1c51d25 --- /dev/null +++ b/qt-linux.css @@ -0,0 +1,4 @@ + +PrivateShareDialog QFrame#mFrame { + border: 0; +} \ No newline at end of file diff --git a/qt.css b/qt.css index b074d34a6..e84e23bd1 100644 --- a/qt.css +++ b/qt.css @@ -597,3 +597,19 @@ QProgressBar#mStorageUsage { margin-right: 0; font-size: 11px; } + +PrivateShareDialog { + min-width: 550px; + min-height: 200px; +} + +SharedItemsTableView { + margin-top: 10px; + qproperty-focusPolicy: NoFocus; +} + +SharedItemsTableView QHeaderView::section { + /* outline: 1px; */ + font-size: 12px; + padding-top: 10px; +} diff --git a/src/api/contact-share-info.h b/src/api/contact-share-info.h new file mode 100644 index 000000000..2bd271a09 --- /dev/null +++ b/src/api/contact-share-info.h @@ -0,0 +1,58 @@ +#ifndef SEAFILE_CLIENT_CONTACT_SHARE_INFO_H +#define SEAFILE_CLIENT_CONTACT_SHARE_INFO_H + +#include +#include + +struct SeafileGroup { + int id; + QString name; + QString owner; +}; + +struct SeafileContact { + QString email; + QString nickname; +}; + +enum SharePermission { + READ_WRITE, + READ_ONLY, +}; + + +enum ShareType { + SHARE_TO_USER, + SHARE_TO_GROUP, +}; + + +inline SharePermission permissionfromString(const QString& s) +{ + return s == "r" ? READ_ONLY : READ_WRITE; +} + +inline ShareType shareTypeFromString(const QString& s) +{ + return s == "group" ? SHARE_TO_GROUP : SHARE_TO_USER; +} + +struct UserShareInfo { + SharePermission permission; + SeafileContact user; +}; + +struct GroupShareInfo { + SharePermission permission; + SeafileGroup group; +}; + +/** + * Register with QMetaType so we can wrap it with QVariant::fromValue + */ +Q_DECLARE_METATYPE(SeafileGroup) +Q_DECLARE_METATYPE(SeafileContact) +Q_DECLARE_METATYPE(UserShareInfo) +Q_DECLARE_METATYPE(GroupShareInfo) + +#endif diff --git a/src/api/requests.cpp b/src/api/requests.cpp index b9cd5b411..b3364bd5c 100644 --- a/src/api/requests.cpp +++ b/src/api/requests.cpp @@ -1,53 +1,56 @@ #include -#include #include +#include #include "account.h" -#include "seafile-applet.h" +#include "api-error.h" +#include "commit-details.h" +#include "event.h" #include "repo-service.h" #include "rpc/rpc-client.h" -#include "utils/utils.h" -#include "utils/api-utils.h" -#include "api-error.h" +#include "seafile-applet.h" #include "server-repo.h" #include "starred-file.h" -#include "event.h" -#include "commit-details.h" +#include "utils/api-utils.h" +#include "utils/json-utils.h" +#include "utils/utils.h" #include "requests.h" -namespace { - -const char *kApiPingUrl = "api2/ping/"; -const char *kApiLoginUrl = "api2/auth-token/"; -const char *kListReposUrl = "api2/repos/"; -const char *kCreateRepoUrl = "api2/repos/"; -const char *kGetRepoUrl = "api2/repos/%1/"; -const char *kCreateSubrepoUrl ="api2/repos/%1/dir/sub_repo/"; -const char *kUnseenMessagesUrl = "api2/unseen_messages/"; -const char *kDefaultRepoUrl = "api2/default-repo/"; -const char *kStarredFilesUrl = "api2/starredfiles/"; -const char *kGetEventsUrl = "api2/events/"; -const char *kCommitDetailsUrl = "api2/repo_history_changes/"; -const char *kAvatarUrl = "api2/avatars/user/"; -const char *kSetRepoPasswordUrl = "api2/repos/"; -const char *kServerInfoUrl ="api2/server-info/"; -const char *kLogoutDeviceUrl = "api2/logout-device/"; -const char *kGetRepoTokensUrl = "api2/repo-tokens/"; -const char *kGetLoginTokenUrl = "api2/client-login/"; -const char *kFileSearchUrl = "api2/search/"; -const char *kAccountInfoUrl = "api2/account/info/"; - -const char *kLatestVersionUrl = "http://seafile.com/api/client-versions/"; +namespace +{ +const char* kApiPingUrl = "api2/ping/"; +const char* kApiLoginUrl = "api2/auth-token/"; +const char* kListReposUrl = "api2/repos/"; +const char* kCreateRepoUrl = "api2/repos/"; +const char* kGetRepoUrl = "api2/repos/%1/"; +const char* kCreateSubrepoUrl = "api2/repos/%1/dir/sub_repo/"; +const char* kUnseenMessagesUrl = "api2/unseen_messages/"; +const char* kDefaultRepoUrl = "api2/default-repo/"; +const char* kStarredFilesUrl = "api2/starredfiles/"; +const char* kGetEventsUrl = "api2/events/"; +const char* kCommitDetailsUrl = "api2/repo_history_changes/"; +const char* kAvatarUrl = "api2/avatars/user/"; +const char* kSetRepoPasswordUrl = "api2/repos/"; +const char* kServerInfoUrl = "api2/server-info/"; +const char* kLogoutDeviceUrl = "api2/logout-device/"; +const char* kGetRepoTokensUrl = "api2/repo-tokens/"; +const char* kGetLoginTokenUrl = "api2/client-login/"; +const char* kFileSearchUrl = "api2/search/"; +const char* kAccountInfoUrl = "api2/account/info/"; +const char* kDirSharedItemsUrl = "api2/repos/%1/dir/shared_items/"; +const char* kFetchGroupsAndContactsUrl = "api2/groupandcontacts/"; + +const char* kLatestVersionUrl = "http://seafile.com/api/client-versions/"; #if defined(Q_OS_WIN32) -const char *kOsName = "windows"; +const char* kOsName = "windows"; #elif defined(Q_OS_LINUX) -const char *kOsName = "linux"; +const char* kOsName = "linux"; #else -const char *kOsName = "mac"; +const char* kOsName = "mac"; #endif } // namespace @@ -55,8 +58,8 @@ const char *kOsName = "mac"; PingServerRequest::PingServerRequest(const QUrl& serverAddr) - : SeafileApiRequest (::urlJoin(serverAddr, kApiPingUrl), - SeafileApiRequest::METHOD_GET) + : SeafileApiRequest(::urlJoin(serverAddr, kApiPingUrl), + SeafileApiRequest::METHOD_GET) { } @@ -73,8 +76,8 @@ LoginRequest::LoginRequest(const QUrl& serverAddr, const QString& password, const QString& computer_name) - : SeafileApiRequest (::urlJoin(serverAddr, kApiLoginUrl), - SeafileApiRequest::METHOD_POST) + : SeafileApiRequest(::urlJoin(serverAddr, kApiLoginUrl), + SeafileApiRequest::METHOD_POST) { setFormParam("username", username); setFormParam("password", password); @@ -88,7 +91,7 @@ LoginRequest::LoginRequest(const QUrl& serverAddr, void LoginRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -97,7 +100,8 @@ void LoginRequest::requestSuccess(QNetworkReply& reply) QScopedPointer json(root); - const char *token = json_string_value(json_object_get(json.data(), "token")); + const char* token = + json_string_value(json_object_get(json.data(), "token")); if (token == NULL) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -112,15 +116,16 @@ void LoginRequest::requestSuccess(QNetworkReply& reply) * ListReposRequest */ ListReposRequest::ListReposRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kListReposUrl), - SeafileApiRequest::METHOD_GET, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kListReposUrl), + SeafileApiRequest::METHOD_GET, + account.token) { } void ListReposRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("ListReposRequest:failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -129,7 +134,8 @@ void ListReposRequest::requestSuccess(QNetworkReply& reply) QScopedPointer json(root); - std::vector repos = ServerRepo::listFromJSON(json.data(), &error); + std::vector repos = + ServerRepo::listFromJSON(json.data(), &error); emit success(repos); } @@ -137,9 +143,13 @@ void ListReposRequest::requestSuccess(QNetworkReply& reply) /** * DownloadRepoRequest */ -DownloadRepoRequest::DownloadRepoRequest(const Account& account, const QString& repo_id, bool read_only) - : SeafileApiRequest(account.getAbsoluteUrl("api2/repos/" + repo_id + "/download-info/"), - SeafileApiRequest::METHOD_GET, account.token), +DownloadRepoRequest::DownloadRepoRequest(const Account& account, + const QString& repo_id, + bool read_only) + : SeafileApiRequest( + account.getAbsoluteUrl("api2/repos/" + repo_id + "/download-info/"), + SeafileApiRequest::METHOD_GET, + account.token), read_only_(read_only) { } @@ -179,7 +189,7 @@ RepoDownloadInfo RepoDownloadInfo::fromDict(QMap& dict, void DownloadRepoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -197,17 +207,19 @@ void DownloadRepoRequest::requestSuccess(QNetworkReply& reply) /** * GetRepoRequest */ -GetRepoRequest::GetRepoRequest(const Account& account, const QString &repoid) - : SeafileApiRequest (account.getAbsoluteUrl(QString(kGetRepoUrl).arg(repoid)), - SeafileApiRequest::METHOD_GET, account.token) - , repoid_(repoid) +GetRepoRequest::GetRepoRequest(const Account& account, const QString& repoid) + : SeafileApiRequest( + account.getAbsoluteUrl(QString(kGetRepoUrl).arg(repoid)), + SeafileApiRequest::METHOD_GET, + account.token), + repoid_(repoid) { } void GetRepoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -224,9 +236,13 @@ void GetRepoRequest::requestSuccess(QNetworkReply& reply) /** * CreateRepoRequest */ -CreateRepoRequest::CreateRepoRequest(const Account& account, const QString &name, const QString &desc, const QString &passwd) - : SeafileApiRequest (account.getAbsoluteUrl(kCreateRepoUrl), - SeafileApiRequest::METHOD_POST, account.token) +CreateRepoRequest::CreateRepoRequest(const Account& account, + const QString& name, + const QString& desc, + const QString& passwd) + : SeafileApiRequest(account.getAbsoluteUrl(kCreateRepoUrl), + SeafileApiRequest::METHOD_POST, + account.token) { setFormParam(QString("name"), name); setFormParam(QString("desc"), desc); @@ -236,10 +252,16 @@ CreateRepoRequest::CreateRepoRequest(const Account& account, const QString &name } } -CreateRepoRequest::CreateRepoRequest(const Account& account, const QString &name, const QString &desc, - int enc_version, const QString &repo_id, const QString& magic, const QString& random_key) - : SeafileApiRequest (account.getAbsoluteUrl(kCreateRepoUrl), - SeafileApiRequest::METHOD_POST, account.token) +CreateRepoRequest::CreateRepoRequest(const Account& account, + const QString& name, + const QString& desc, + int enc_version, + const QString& repo_id, + const QString& magic, + const QString& random_key) + : SeafileApiRequest(account.getAbsoluteUrl(kCreateRepoUrl), + SeafileApiRequest::METHOD_POST, + account.token) { setFormParam("name", name); setFormParam("desc", desc); @@ -252,7 +274,7 @@ CreateRepoRequest::CreateRepoRequest(const Account& account, const QString &name void CreateRepoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -269,9 +291,15 @@ void CreateRepoRequest::requestSuccess(QNetworkReply& reply) /** * CreateSubrepoRequest */ -CreateSubrepoRequest::CreateSubrepoRequest(const Account& account, const QString &name, const QString &repoid , const QString &path, const QString &passwd) - : SeafileApiRequest (account.getAbsoluteUrl(QString(kCreateSubrepoUrl).arg(repoid)), - SeafileApiRequest::METHOD_GET, account.token) +CreateSubrepoRequest::CreateSubrepoRequest(const Account& account, + const QString& name, + const QString& repoid, + const QString& path, + const QString& passwd) + : SeafileApiRequest( + account.getAbsoluteUrl(QString(kCreateSubrepoUrl).arg(repoid)), + SeafileApiRequest::METHOD_GET, + account.token) { setUrlParam(QString("p"), path); setUrlParam(QString("name"), name); @@ -283,7 +311,7 @@ CreateSubrepoRequest::CreateSubrepoRequest(const Account& account, const QString void CreateSubrepoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -299,18 +327,22 @@ void CreateSubrepoRequest::requestSuccess(QNetworkReply& reply) /** * GetUnseenSeahubNotificationsRequest */ -GetUnseenSeahubNotificationsRequest::GetUnseenSeahubNotificationsRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kUnseenMessagesUrl), - SeafileApiRequest::METHOD_GET, account.token) +GetUnseenSeahubNotificationsRequest::GetUnseenSeahubNotificationsRequest( + const Account& account) + : SeafileApiRequest(account.getAbsoluteUrl(kUnseenMessagesUrl), + SeafileApiRequest::METHOD_GET, + account.token) { } void GetUnseenSeahubNotificationsRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("GetUnseenSeahubNotificationsRequest: failed to parse json:%s\n", error.text); + qWarning( + "GetUnseenSeahubNotificationsRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } @@ -329,17 +361,19 @@ void GetUnseenSeahubNotificationsRequest::requestSuccess(QNetworkReply& reply) } GetDefaultRepoRequest::GetDefaultRepoRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kDefaultRepoUrl), - SeafileApiRequest::METHOD_GET, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kDefaultRepoUrl), + SeafileApiRequest::METHOD_GET, + account.token) { } void GetDefaultRepoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("CreateDefaultRepoRequest: failed to parse json:%s\n", error.text); + qWarning("CreateDefaultRepoRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } @@ -371,17 +405,19 @@ void GetDefaultRepoRequest::requestSuccess(QNetworkReply& reply) CreateDefaultRepoRequest::CreateDefaultRepoRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kDefaultRepoUrl), - SeafileApiRequest::METHOD_POST, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kDefaultRepoUrl), + SeafileApiRequest::METHOD_POST, + account.token) { } void CreateDefaultRepoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("CreateDefaultRepoRequest: failed to parse json:%s\n", error.text); + qWarning("CreateDefaultRepoRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } @@ -409,9 +445,10 @@ GetLatestVersionRequest::GetLatestVersionRequest(const QString& client_id, void GetLatestVersionRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("GetLatestVersionRequest: failed to parse json:%s\n", error.text); + qWarning("GetLatestVersionRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } @@ -431,30 +468,34 @@ void GetLatestVersionRequest::requestSuccess(QNetworkReply& reply) } GetStarredFilesRequest::GetStarredFilesRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kStarredFilesUrl), - SeafileApiRequest::METHOD_GET, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kStarredFilesUrl), + SeafileApiRequest::METHOD_GET, + account.token) { } void GetStarredFilesRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("GetStarredFilesRequest: failed to parse json:%s\n", error.text); + qWarning("GetStarredFilesRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } QScopedPointer json(root); - std::vector files = StarredFile::listFromJSON(json.data(), &error); + std::vector files = + StarredFile::listFromJSON(json.data(), &error); emit success(files); } GetEventsRequest::GetEventsRequest(const Account& account, int start) - : SeafileApiRequest (account.getAbsoluteUrl(kGetEventsUrl), - SeafileApiRequest::METHOD_GET, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kGetEventsUrl), + SeafileApiRequest::METHOD_GET, + account.token) { if (start > 0) { setUrlParam("start", QString::number(start)); @@ -464,7 +505,7 @@ GetEventsRequest::GetEventsRequest(const Account& account, int start) void GetEventsRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("GetEventsRequest: failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -481,17 +522,20 @@ void GetEventsRequest::requestSuccess(QNetworkReply& reply) more = json_is_true(json_object_get(json.data(), "more")); if (more) { - more_offset = json_integer_value(json_object_get(json.data(), "more_offset")); + more_offset = + json_integer_value(json_object_get(json.data(), "more_offset")); } emit success(events, more_offset); } GetCommitDetailsRequest::GetCommitDetailsRequest(const Account& account, - const QString& repo_id, - const QString& commit_id) - : SeafileApiRequest (account.getAbsoluteUrl(kCommitDetailsUrl + repo_id + "/"), - SeafileApiRequest::METHOD_GET, account.token) + const QString& repo_id, + const QString& commit_id) + : SeafileApiRequest( + account.getAbsoluteUrl(kCommitDetailsUrl + repo_id + "/"), + SeafileApiRequest::METHOD_GET, + account.token) { setUrlParam("commit_id", commit_id); } @@ -499,9 +543,10 @@ GetCommitDetailsRequest::GetCommitDetailsRequest(const Account& account, void GetCommitDetailsRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("GetCommitDetailsRequest: failed to parse json:%s\n", error.text); + qWarning("GetCommitDetailsRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } @@ -518,11 +563,11 @@ GetAvatarRequest::GetAvatarRequest(const Account& account, const QString& email, qint64 mtime, int size) - : SeafileApiRequest (account.getAbsoluteUrl( - kAvatarUrl - + email + "/resized/" - + QString::number(size) + "/"), - SeafileApiRequest::METHOD_GET, account.token), + : SeafileApiRequest( + account.getAbsoluteUrl(kAvatarUrl + email + "/resized/" + + QString::number(size) + "/"), + SeafileApiRequest::METHOD_GET, + account.token), fetch_img_req_(NULL), mtime_(mtime) { @@ -540,7 +585,7 @@ GetAvatarRequest::~GetAvatarRequest() void GetAvatarRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("GetAvatarRequest: failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -549,13 +594,15 @@ void GetAvatarRequest::requestSuccess(QNetworkReply& reply) QScopedPointer json(root); - const char *avatar_url = json_string_value(json_object_get(json.data(), "url")); + const char* avatar_url = + json_string_value(json_object_get(json.data(), "url")); // we don't need to fetch all images if we have latest one - json_t *mtime = json_object_get(json.data(), "mtime"); + json_t* mtime = json_object_get(json.data(), "mtime"); if (!mtime) { qWarning("GetAvatarRequest: no 'mtime' value in response\n"); - } else { + } + else { qint64 new_mtime = json_integer_value(mtime); if (new_mtime == mtime_) { emit success(QImage()); @@ -574,10 +621,10 @@ void GetAvatarRequest::requestSuccess(QNetworkReply& reply) fetch_img_req_ = new FetchImageRequest(url); - connect(fetch_img_req_, SIGNAL(failed(const ApiError&)), - this, SIGNAL(failed(const ApiError&))); - connect(fetch_img_req_, SIGNAL(success(const QImage&)), - this, SIGNAL(success(const QImage&))); + connect(fetch_img_req_, SIGNAL(failed(const ApiError&)), this, + SIGNAL(failed(const ApiError&))); + connect(fetch_img_req_, SIGNAL(success(const QImage&)), this, + SIGNAL(success(const QImage&))); fetch_img_req_->send(); } @@ -594,7 +641,8 @@ void FetchImageRequest::requestSuccess(QNetworkReply& reply) if (img.isNull()) { qWarning("FetchImageRequest: invalid image data\n"); emit failed(ApiError::fromHttpError(400)); - } else { + } + else { emit success(img); } } @@ -602,8 +650,10 @@ void FetchImageRequest::requestSuccess(QNetworkReply& reply) SetRepoPasswordRequest::SetRepoPasswordRequest(const Account& account, const QString& repo_id, const QString& password) - : SeafileApiRequest (account.getAbsoluteUrl(kSetRepoPasswordUrl + repo_id + "/"), - SeafileApiRequest::METHOD_POST, account.token) + : SeafileApiRequest( + account.getAbsoluteUrl(kSetRepoPasswordUrl + repo_id + "/"), + SeafileApiRequest::METHOD_POST, + account.token) { setFormParam("password", password); } @@ -614,16 +664,17 @@ void SetRepoPasswordRequest::requestSuccess(QNetworkReply& reply) } ServerInfoRequest::ServerInfoRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kServerInfoUrl), - SeafileApiRequest::METHOD_GET, account.token), - account_(account) + : SeafileApiRequest(account.getAbsoluteUrl(kServerInfoUrl), + SeafileApiRequest::METHOD_GET, + account.token), + account_(account) { } void ServerInfoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -655,8 +706,9 @@ void ServerInfoRequest::requestSuccess(QNetworkReply& reply) } LogoutDeviceRequest::LogoutDeviceRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kLogoutDeviceUrl), - SeafileApiRequest::METHOD_POST, account.token), + : SeafileApiRequest(account.getAbsoluteUrl(kLogoutDeviceUrl), + SeafileApiRequest::METHOD_POST, + account.token), account_(account) { } @@ -668,8 +720,9 @@ void LogoutDeviceRequest::requestSuccess(QNetworkReply& reply) GetRepoTokensRequest::GetRepoTokensRequest(const Account& account, const QStringList& repo_ids) - : SeafileApiRequest (account.getAbsoluteUrl(kGetRepoTokensUrl), - SeafileApiRequest::METHOD_GET, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kGetRepoTokensUrl), + SeafileApiRequest::METHOD_GET, + account.token) { setUrlParam("repos", repo_ids.join(",")); } @@ -677,7 +730,7 @@ GetRepoTokensRequest::GetRepoTokensRequest(const Account& account, void GetRepoTokensRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("GetRepoTokensRequest: failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -687,16 +740,18 @@ void GetRepoTokensRequest::requestSuccess(QNetworkReply& reply) QScopedPointer json(root); QMap dict = mapFromJSON(json.data(), &error); - foreach (const QString &repo_id, dict.keys()) { + foreach (const QString& repo_id, dict.keys()) { repo_tokens_[repo_id] = dict[repo_id].toString(); } emit success(); } -GetLoginTokenRequest::GetLoginTokenRequest(const Account& account, const QString& next_url) - : SeafileApiRequest (account.getAbsoluteUrl(kGetLoginTokenUrl), - SeafileApiRequest::METHOD_POST, account.token), +GetLoginTokenRequest::GetLoginTokenRequest(const Account& account, + const QString& next_url) + : SeafileApiRequest(account.getAbsoluteUrl(kGetLoginTokenUrl), + SeafileApiRequest::METHOD_POST, + account.token), account_(account), next_url_(next_url) { @@ -705,7 +760,7 @@ GetLoginTokenRequest::GetLoginTokenRequest(const Account& account, const QString void GetLoginTokenRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("GetLoginTokenRequest: failed to parse json:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -722,9 +777,12 @@ void GetLoginTokenRequest::requestSuccess(QNetworkReply& reply) emit success(dict["token"].toString()); } -FileSearchRequest::FileSearchRequest(const Account& account, const QString &keyword, int per_page) - : SeafileApiRequest (account.getAbsoluteUrl(kFileSearchUrl), - SeafileApiRequest::METHOD_GET, account.token), +FileSearchRequest::FileSearchRequest(const Account& account, + const QString& keyword, + int per_page) + : SeafileApiRequest(account.getAbsoluteUrl(kFileSearchUrl), + SeafileApiRequest::METHOD_GET, + account.token), keyword_(keyword) { setUrlParam("q", keyword_); @@ -733,7 +791,7 @@ FileSearchRequest::FileSearchRequest(const Account& account, const QString &keyw void FileSearchRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { qWarning("FileSearchResult: failed to parse jsn:%s\n", error.text); emit failed(ApiError::fromJsonError()); @@ -779,13 +837,16 @@ void FetchCustomLogoRequest::requestSuccess(QNetworkReply& reply) if (logo.isNull()) { qWarning("FetchCustomLogoRequest: invalid image data\n"); emit failed(ApiError::fromHttpError(400)); - } else { + } + else { emit success(url()); } } FetchAccountInfoRequest::FetchAccountInfoRequest(const Account& account) - : SeafileApiRequest (account.getAbsoluteUrl(kAccountInfoUrl), SeafileApiRequest::METHOD_GET, account.token) + : SeafileApiRequest(account.getAbsoluteUrl(kAccountInfoUrl), + SeafileApiRequest::METHOD_GET, + account.token) { account_ = account; } @@ -793,9 +854,10 @@ FetchAccountInfoRequest::FetchAccountInfoRequest(const Account& account) void FetchAccountInfoRequest::requestSuccess(QNetworkReply& reply) { json_error_t error; - json_t *root = parseJSON(reply, &error); + json_t* root = parseJSON(reply, &error); if (!root) { - qWarning("FetchAccountInfoRequest: failed to parse json:%s\n", error.text); + qWarning("FetchAccountInfoRequest: failed to parse json:%s\n", + error.text); emit failed(ApiError::fromJsonError()); return; } @@ -811,3 +873,186 @@ void FetchAccountInfoRequest::requestSuccess(QNetworkReply& reply) info.usedStorage = dict["usage"].toLongLong(); emit success(info); } + +PrivateShareRequest::PrivateShareRequest(const Account& account, + const QString& repo_id, + const QString& path, + const QString& username, + int group_id, + SharePermission permission, + ShareType share_type, + ShareOperation op) + : SeafileApiRequest( + account.getAbsoluteUrl(QString(kDirSharedItemsUrl).arg(repo_id)), + op == UPDATE_SHARE ? METHOD_POST : (op == REMOVE_SHARE ? METHOD_DELETE + : METHOD_PUT), + account.token), + group_id_(share_type == SHARE_TO_GROUP ? group_id : -1), + username_(share_type == SHARE_TO_USER ? username : QString()), + permission_(permission), + share_type_(share_type), + share_operation_(op) +{ + setUrlParam("p", path); + setFormParam("permission", permission == READ_ONLY ? "r" : "rw"); + bool is_add = op == ADD_SHARE; + if (is_add) { + setFormParam("share_type", + share_type == SHARE_TO_USER ? "user" : "group"); + } + else { + setUrlParam("share_type", + share_type == SHARE_TO_USER ? "user" : "group"); + } + + if (share_type == SHARE_TO_USER) { + if (is_add) { + setFormParam("username", username); + } + else { + setUrlParam("username", username); + } + } + else { + if (is_add) { + setFormParam("group_id", QString::number(group_id)); + } + else { + setUrlParam("group_id", QString::number(group_id)); + } + } +} + +void PrivateShareRequest::requestSuccess(QNetworkReply& reply) +{ + json_error_t error; + json_t* root = parseJSON(reply, &error); + if (!root) { + qWarning("PrivateShareRequest: failed to parse json:%s\n", error.text); + emit failed(ApiError::fromJsonError()); + return; + } + + QScopedPointer json(root); + + emit success(); +} + + +FetchGroupsAndContactsRequest::FetchGroupsAndContactsRequest( + const Account& account) + : SeafileApiRequest(account.getAbsoluteUrl(kFetchGroupsAndContactsUrl), + SeafileApiRequest::METHOD_GET, + account.token) +{ +} + +void FetchGroupsAndContactsRequest::requestSuccess(QNetworkReply& reply) +{ + json_error_t error; + json_t* root = parseJSON(reply, &error); + if (!root) { + qWarning("FetchGroupsAndContactsRequest: failed to parse json:%s\n", + error.text); + emit failed(ApiError::fromJsonError()); + return; + } + + QScopedPointer json(root); + + QList groups; + QList contacts; + + json_t* groups_array = json_object_get(json.data(), "groups"); + if (groups_array) { + int i, n = json_array_size(groups_array); + for (i = 0; i < n; i++) { + json_t* group_object = json_array_get(groups_array, i); + const char* name = + json_string_value(json_object_get(group_object, "name")); + int group_id = + json_integer_value(json_object_get(group_object, "id")); + if (name && group_id) { + SeafileGroup group; + group.id = group_id; + group.name = QString::fromUtf8(name); + groups.push_back(group); + } + } + } + + json_t* contacts_array = json_object_get(json.data(), "contacts"); + if (contacts_array) { + int i, n = json_array_size(contacts_array); + + for (i = 0; i < n; i++) { + json_t* contact_object = json_array_get(contacts_array, i); + const char* email = + json_string_value(json_object_get(contact_object, "email")); + if (email) { + SeafileContact contact; + contact.email = QString::fromUtf8(email); + contact.nickname = QString::fromUtf8( + json_string_value(json_object_get(contact_object, "name"))); + contacts.push_back(contact); + } + } + } + + emit success(groups, contacts); +} + +GetPrivateShareItemsRequest::GetPrivateShareItemsRequest(const Account& account, + const QString& repo_id, + const QString& path) + : SeafileApiRequest( + account.getAbsoluteUrl(QString(kDirSharedItemsUrl).arg(repo_id)), + SeafileApiRequest::METHOD_GET, + account.token) +{ + setUrlParam("p", path); +} + +void GetPrivateShareItemsRequest::requestSuccess(QNetworkReply& reply) +{ + json_error_t error; + json_t* root = parseJSON(reply, &error); + if (!root) { + qWarning("GetPrivateShareItemsRequest: failed to parse json:%s\n", + error.text); + emit failed(ApiError::fromJsonError()); + return; + } + + QScopedPointer json(root); + + QList group_shares; + QList user_shares; + + int i, n = json_array_size(json.data()); + for (i = 0; i < n; i++) { + json_t* share_info_object = json_array_get(json.data(), i); + Json share_info(share_info_object); + QString share_type = share_info.getString("share_type"); + QString permission = share_info.getString("permission"); + if (share_type == "group") { + // group share + Json group = share_info.getObject("group_info"); + GroupShareInfo group_share; + group_share.group.id = group.getLong("id"); + group_share.group.name = group.getString("name"); + group_share.permission = ::permissionfromString(permission); + group_shares.push_back(group_share); + } + else if (share_type == "user") { + Json user = share_info.getObject("user_info"); + UserShareInfo user_share; + user_share.user.email = user.getString("name"); + user_share.user.nickname = user.getString("nickname"); + user_share.permission = ::permissionfromString(permission); + user_shares.push_back(user_share); + } + } + + emit success(group_shares, user_shares); +} diff --git a/src/api/requests.h b/src/api/requests.h index 863eba587..d51f59b74 100644 --- a/src/api/requests.h +++ b/src/api/requests.h @@ -1,12 +1,13 @@ #ifndef SEAFILE_CLIENT_API_REQUESTS_H #define SEAFILE_CLIENT_API_REQUESTS_H -#include #include +#include +#include "account.h" #include "api-request.h" +#include "contact-share-info.h" #include "server-repo.h" -#include "account.h" #include "server-repo.h" class QNetworkReply; @@ -19,7 +20,8 @@ class StarredFile; class SeafEvent; class CommitDetails; -class PingServerRequest : public SeafileApiRequest { +class PingServerRequest : public SeafileApiRequest +{ Q_OBJECT public: PingServerRequest(const QUrl& serverAddr); @@ -34,7 +36,8 @@ protected slots: Q_DISABLE_COPY(PingServerRequest) }; -class LoginRequest : public SeafileApiRequest { +class LoginRequest : public SeafileApiRequest +{ Q_OBJECT public: @@ -54,7 +57,8 @@ protected slots: }; -class ListReposRequest : public SeafileApiRequest { +class ListReposRequest : public SeafileApiRequest +{ Q_OBJECT public: @@ -71,7 +75,8 @@ protected slots: }; -class RepoDownloadInfo { +class RepoDownloadInfo +{ public: int repo_version; QString relay_id; @@ -93,7 +98,8 @@ class RepoDownloadInfo { bool read_only); }; -class DownloadRepoRequest : public SeafileApiRequest { +class DownloadRepoRequest : public SeafileApiRequest +{ Q_OBJECT public: @@ -113,12 +119,16 @@ protected slots: bool read_only_; }; -class GetRepoRequest : public SeafileApiRequest { +class GetRepoRequest : public SeafileApiRequest +{ Q_OBJECT public: - explicit GetRepoRequest(const Account& account, const QString &repoid); - const QString &repoid() { return repoid_; } + explicit GetRepoRequest(const Account& account, const QString& repoid); + const QString& repoid() + { + return repoid_; + } protected slots: void requestSuccess(QNetworkReply& reply); @@ -131,13 +141,22 @@ protected slots: const QString repoid_; }; -class CreateRepoRequest : public SeafileApiRequest { +class CreateRepoRequest : public SeafileApiRequest +{ Q_OBJECT public: - CreateRepoRequest(const Account& account, const QString &name, const QString &desc, const QString &passwd); - CreateRepoRequest(const Account& account, const QString &name, const QString &desc, - int enc_version, const QString &repo_id, const QString& magic, const QString& random_key); + CreateRepoRequest(const Account& account, + const QString& name, + const QString& desc, + const QString& passwd); + CreateRepoRequest(const Account& account, + const QString& name, + const QString& desc, + int enc_version, + const QString& repo_id, + const QString& magic, + const QString& random_key); protected slots: void requestSuccess(QNetworkReply& reply); @@ -147,14 +166,18 @@ protected slots: private: Q_DISABLE_COPY(CreateRepoRequest) - }; -class CreateSubrepoRequest : public SeafileApiRequest { +class CreateSubrepoRequest : public SeafileApiRequest +{ Q_OBJECT public: - explicit CreateSubrepoRequest(const Account& account, const QString &name, const QString &repoid , const QString &path, const QString &passwd); + explicit CreateSubrepoRequest(const Account& account, + const QString& name, + const QString& repoid, + const QString& path, + const QString& passwd); protected slots: void requestSuccess(QNetworkReply& reply); @@ -164,10 +187,10 @@ protected slots: private: Q_DISABLE_COPY(CreateSubrepoRequest) - }; -class GetUnseenSeahubNotificationsRequest : public SeafileApiRequest { +class GetUnseenSeahubNotificationsRequest : public SeafileApiRequest +{ Q_OBJECT public: @@ -181,10 +204,10 @@ protected slots: private: Q_DISABLE_COPY(GetUnseenSeahubNotificationsRequest) - }; -class GetDefaultRepoRequest : public SeafileApiRequest { +class GetDefaultRepoRequest : public SeafileApiRequest +{ Q_OBJECT public: GetDefaultRepoRequest(const Account& account); @@ -199,7 +222,8 @@ protected slots: Q_DISABLE_COPY(GetDefaultRepoRequest); }; -class CreateDefaultRepoRequest : public SeafileApiRequest { +class CreateDefaultRepoRequest : public SeafileApiRequest +{ Q_OBJECT public: CreateDefaultRepoRequest(const Account& account); @@ -214,10 +238,12 @@ protected slots: Q_DISABLE_COPY(CreateDefaultRepoRequest); }; -class GetLatestVersionRequest : public SeafileApiRequest { +class GetLatestVersionRequest : public SeafileApiRequest +{ Q_OBJECT public: - GetLatestVersionRequest(const QString& client_id, const QString& client_version); + GetLatestVersionRequest(const QString& client_id, + const QString& client_version); signals: void success(const QString& latest_version); @@ -229,7 +255,8 @@ protected slots: Q_DISABLE_COPY(GetLatestVersionRequest); }; -class GetStarredFilesRequest : public SeafileApiRequest { +class GetStarredFilesRequest : public SeafileApiRequest +{ Q_OBJECT public: GetStarredFilesRequest(const Account& account); @@ -244,10 +271,11 @@ protected slots: Q_DISABLE_COPY(GetStarredFilesRequest); }; -class GetEventsRequest : public SeafileApiRequest { +class GetEventsRequest : public SeafileApiRequest +{ Q_OBJECT public: - GetEventsRequest(const Account& account, int start=0); + GetEventsRequest(const Account& account, int start = 0); signals: void success(const std::vector& events, int more_offset); @@ -259,7 +287,8 @@ protected slots: Q_DISABLE_COPY(GetEventsRequest); }; -class GetCommitDetailsRequest : public SeafileApiRequest { +class GetCommitDetailsRequest : public SeafileApiRequest +{ Q_OBJECT public: GetCommitDetailsRequest(const Account& account, @@ -276,7 +305,8 @@ protected slots: Q_DISABLE_COPY(GetCommitDetailsRequest); }; -class FetchImageRequest : public SeafileApiRequest { +class FetchImageRequest : public SeafileApiRequest +{ Q_OBJECT public: FetchImageRequest(const QString& img_url); @@ -291,7 +321,8 @@ protected slots: Q_DISABLE_COPY(FetchImageRequest); }; -class GetAvatarRequest : public SeafileApiRequest { +class GetAvatarRequest : public SeafileApiRequest +{ Q_OBJECT public: GetAvatarRequest(const Account& account, @@ -301,9 +332,18 @@ class GetAvatarRequest : public SeafileApiRequest { ~GetAvatarRequest(); - const QString& email() const { return email_; } - const Account& account() const { return account_; } - qint64 mtime() const { return mtime_; } + const QString& email() const + { + return email_; + } + const Account& account() const + { + return account_; + } + qint64 mtime() const + { + return mtime_; + } signals: void success(const QImage& avatar); @@ -314,7 +354,7 @@ protected slots: private: Q_DISABLE_COPY(GetAvatarRequest); - FetchImageRequest *fetch_img_req_; + FetchImageRequest* fetch_img_req_; QString email_; @@ -323,7 +363,8 @@ protected slots: qint64 mtime_; }; -class SetRepoPasswordRequest : public SeafileApiRequest { +class SetRepoPasswordRequest : public SeafileApiRequest +{ Q_OBJECT public: SetRepoPasswordRequest(const Account& account, @@ -340,13 +381,14 @@ protected slots: Q_DISABLE_COPY(SetRepoPasswordRequest); }; -class ServerInfoRequest : public SeafileApiRequest { +class ServerInfoRequest : public SeafileApiRequest +{ Q_OBJECT public: ServerInfoRequest(const Account& account); signals: - void success(const Account &account, const ServerInfo &info); + void success(const Account& account, const ServerInfo& info); protected slots: void requestSuccess(QNetworkReply& reply); @@ -356,12 +398,16 @@ protected slots: const Account& account_; }; -class LogoutDeviceRequest : public SeafileApiRequest { +class LogoutDeviceRequest : public SeafileApiRequest +{ Q_OBJECT public: LogoutDeviceRequest(const Account& account); - const Account& account() { return account_; } + const Account& account() + { + return account_; + } signals: void success(); @@ -375,13 +421,16 @@ protected slots: Account account_; }; -class GetRepoTokensRequest : public SeafileApiRequest { +class GetRepoTokensRequest : public SeafileApiRequest +{ Q_OBJECT public: - GetRepoTokensRequest(const Account& account, - const QStringList& repo_ids); + GetRepoTokensRequest(const Account& account, const QStringList& repo_ids); - const QMap& repoTokens() { return repo_tokens_; } + const QMap& repoTokens() + { + return repo_tokens_; + } signals: void success(); @@ -395,13 +444,20 @@ protected slots: QMap repo_tokens_; }; -class GetLoginTokenRequest : public SeafileApiRequest { +class GetLoginTokenRequest : public SeafileApiRequest +{ Q_OBJECT public: GetLoginTokenRequest(const Account& account, const QString& next_url); - const Account& account() { return account_; } - const QString& nextUrl() { return next_url_; } + const Account& account() + { + return account_; + } + const QString& nextUrl() + { + return next_url_; + } signals: void success(const QString& token); @@ -428,11 +484,17 @@ struct FileSearchResult { Q_DECLARE_METATYPE(FileSearchResult) -class FileSearchRequest : public SeafileApiRequest { +class FileSearchRequest : public SeafileApiRequest +{ Q_OBJECT public: - FileSearchRequest(const Account& account, const QString &keyword, int per_page = 10); - const QString &keyword() const { return keyword_; } + FileSearchRequest(const Account& account, + const QString& keyword, + int per_page = 10); + const QString& keyword() const + { + return keyword_; + } signals: void success(const std::vector& result); @@ -446,10 +508,11 @@ protected slots: const QString keyword_; }; -class FetchCustomLogoRequest : public SeafileApiRequest { +class FetchCustomLogoRequest : public SeafileApiRequest +{ Q_OBJECT public: - FetchCustomLogoRequest(const QUrl &url); + FetchCustomLogoRequest(const QUrl& url); signals: void success(const QUrl& url); @@ -461,12 +524,16 @@ protected slots: Q_DISABLE_COPY(FetchCustomLogoRequest); }; -class FetchAccountInfoRequest : public SeafileApiRequest { +class FetchAccountInfoRequest : public SeafileApiRequest +{ Q_OBJECT public: FetchAccountInfoRequest(const Account& account); - const Account& account() const { return account_; } + const Account& account() const + { + return account_; + } signals: void success(const AccountInfo& info); @@ -480,4 +547,97 @@ protected slots: Account account_; }; +class PrivateShareRequest : public SeafileApiRequest +{ + Q_OBJECT +public: + enum ShareOperation { + ADD_SHARE, + UPDATE_SHARE, + REMOVE_SHARE, + }; + PrivateShareRequest(const Account& account, + const QString& repo_id, + const QString& path, + const QString& username, + int group_id, + SharePermission permission, + ShareType share_type, + ShareOperation op); + + ShareOperation shareOperation() const + { + return share_operation_; + } + + int groupId() const + { + return share_type_ == SHARE_TO_GROUP ? group_id_ : -1; + }; + + QString userName() const + { + return share_type_ == SHARE_TO_USER ? username_ : QString(); + }; + + SharePermission permission() const + { + return permission_; + } + + ShareType shareType() const + { + return share_type_; + } + +signals: + void success(); + +protected slots: + void requestSuccess(QNetworkReply& reply); + +private: + Q_DISABLE_COPY(PrivateShareRequest); + + int group_id_; + QString username_; + SharePermission permission_; + ShareType share_type_; + ShareOperation share_operation_; +}; + +class GetPrivateShareItemsRequest : public SeafileApiRequest +{ + Q_OBJECT +public: + GetPrivateShareItemsRequest(const Account& account, + const QString& repo_id, + const QString& path); + +signals: + void success(const QList&, const QList&); + +protected slots: + void requestSuccess(QNetworkReply& reply); + +private: + Q_DISABLE_COPY(GetPrivateShareItemsRequest); +}; + +class FetchGroupsAndContactsRequest : public SeafileApiRequest +{ + Q_OBJECT +public: + FetchGroupsAndContactsRequest(const Account& account); + +signals: + void success(const QList&, const QList&); + +protected slots: + void requestSuccess(QNetworkReply& reply); + +private: + Q_DISABLE_COPY(FetchGroupsAndContactsRequest); +}; + #endif // SEAFILE_CLIENT_API_REQUESTS_H diff --git a/src/filebrowser/file-browser-dialog.cpp b/src/filebrowser/file-browser-dialog.cpp index 4e56bd2c9..519ce882a 100644 --- a/src/filebrowser/file-browser-dialog.cpp +++ b/src/filebrowser/file-browser-dialog.cpp @@ -27,6 +27,7 @@ #include "repo-service.h" #include "rpc/local-repo.h" #include "rpc/rpc-client.h" +#include "ui/private-share-dialog.h" #include "file-browser-manager.h" #include "file-browser-dialog.h" @@ -152,6 +153,8 @@ FileBrowserDialog::FileBrowserDialog(const Account &account, const ServerRepo& r this, SLOT(onGetDirentRemove(const QList &))); connect(table_view_, SIGNAL(direntShare(const SeafDirent&)), this, SLOT(onGetDirentShare(const SeafDirent&))); + connect(table_view_, SIGNAL(direntShareToUserOrGroup(const SeafDirent&, bool)), + this, SLOT(onGetDirentShareToUserOrGroup(const SeafDirent&, bool))); connect(table_view_, SIGNAL(direntShareSeafile(const SeafDirent&)), this, SLOT(onGetDirentShareSeafile(const SeafDirent&))); connect(table_view_, SIGNAL(direntUpdate(const SeafDirent&)), @@ -1014,6 +1017,15 @@ void FileBrowserDialog::onGetDirentShare(const SeafDirent& dirent) dirent.isFile()); } +void FileBrowserDialog::onGetDirentShareToUserOrGroup(const SeafDirent& dirent, + bool to_group) +{ + PrivateShareDialog dialog(account_, repo_.id, repo_.name, + ::pathJoin(current_path_, dirent.name), to_group, + this); + dialog.exec(); +} + void FileBrowserDialog::onGetDirentShareSeafile(const SeafDirent& dirent) { QString repo_id = repo_.id; diff --git a/src/filebrowser/file-browser-dialog.h b/src/filebrowser/file-browser-dialog.h index 665688529..820301e45 100644 --- a/src/filebrowser/file-browser-dialog.h +++ b/src/filebrowser/file-browser-dialog.h @@ -88,6 +88,7 @@ private slots: void onGetDirentRemove(const SeafDirent& dirent); void onGetDirentRemove(const QList &dirents); void onGetDirentShare(const SeafDirent& dirent); + void onGetDirentShareToUserOrGroup(const SeafDirent& dirent, bool to_group); void onGetDirentShareSeafile(const SeafDirent& dirent); void onGetDirentUpdate(const SeafDirent& dirent); void onGetDirentsPaste(); diff --git a/src/filebrowser/file-table.cpp b/src/filebrowser/file-table.cpp index e630f7f0f..f0a654c8f 100644 --- a/src/filebrowser/file-table.cpp +++ b/src/filebrowser/file-table.cpp @@ -317,6 +317,14 @@ void FileTableView::setupContextMenu() this, SLOT(onShare())); share_action_->setShortcut(Qt::ALT + Qt::Key_G); + share_to_user_action_ = new QAction(tr("Share to a User"), this); + connect(share_to_user_action_, SIGNAL(triggered()), + this, SLOT(onShareToUser())); + + share_to_group_action_ = new QAction(tr("Share to a Group"), this); + connect(share_to_group_action_, SIGNAL(triggered()), + this, SLOT(onShareToGroup())); + share_seafile_action_ = new QAction(tr("G&enerate Seafile Internal Link"), this); connect(share_seafile_action_, SIGNAL(triggered()), this, SLOT(onShareSeafile())); @@ -366,6 +374,8 @@ void FileTableView::setupContextMenu() context_menu_->addAction(saveas_action_); context_menu_->addAction(share_action_); context_menu_->addAction(share_seafile_action_); + context_menu_->addAction(share_to_user_action_); + context_menu_->addAction(share_to_group_action_); context_menu_->addSeparator(); context_menu_->addAction(move_action_); context_menu_->addAction(copy_action_); @@ -469,6 +479,8 @@ void FileTableView::contextMenuEvent(QContextMenuEvent *event) rename_action_->setVisible(false); share_action_->setVisible(false); share_seafile_action_->setVisible(false); + share_to_user_action_->setVisible(false); + share_to_group_action_->setVisible(false); update_action_->setVisible(false); cancel_download_action_->setVisible(false); sync_subdirectory_action_->setVisible(false); @@ -513,6 +525,8 @@ void FileTableView::contextMenuEvent(QContextMenuEvent *event) download_action_->setText(tr("&Open")); saveas_action_->setEnabled(false); sync_subdirectory_action_->setVisible(true); + share_to_user_action_->setVisible(true); + share_to_group_action_->setVisible(true); } else { if (item_->locked_by_me) { lock_action_->setText(tr("Un&lock")); @@ -528,6 +542,8 @@ void FileTableView::contextMenuEvent(QContextMenuEvent *event) download_action_->setText(tr("D&ownload")); saveas_action_->setEnabled(true); sync_subdirectory_action_->setVisible(false); + share_to_user_action_->setVisible(false); + share_to_group_action_->setVisible(false); if (TransferManager::instance()->getDownloadTask(parent_->repo_.id, ::pathJoin(parent_->current_path_, dirent->name))) { @@ -537,6 +553,11 @@ void FileTableView::contextMenuEvent(QContextMenuEvent *event) } } + if (!parent_->account_.isPro()) { + share_to_user_action_->setVisible(false); + share_to_group_action_->setVisible(false); + } + context_menu_->exec(position); // synchronously // @@ -657,6 +678,28 @@ void FileTableView::onShare() emit direntShare(*item_); } +void FileTableView::onShareToUser() +{ + onShareToUserOrGroup(false); +} + +void FileTableView::onShareToGroup() +{ + onShareToUserOrGroup(true); +} + +void FileTableView::onShareToUserOrGroup(bool to_group) +{ + if (item_ == NULL) { + const SeafDirent *selected_item = getSelectedItemFromSource(); + if (selected_item && selected_item->isDir()) + emit direntShareToUserOrGroup(*selected_item, to_group); + return; + } + emit direntShareToUserOrGroup(*item_, to_group); +} + + void FileTableView::onShareSeafile() { if (item_ == NULL) { diff --git a/src/filebrowser/file-table.h b/src/filebrowser/file-table.h index 08d0f4003..3037a298d 100644 --- a/src/filebrowser/file-table.h +++ b/src/filebrowser/file-table.h @@ -40,6 +40,7 @@ class FileTableView : public QTableView void direntRemove(const QList &dirents); void direntUpdate(const SeafDirent& dirent); void direntShare(const SeafDirent& dirent); + void direntShareToUserOrGroup(const SeafDirent& dirent, bool to_group); void direntShareSeafile(const SeafDirent& dirent); void direntPaste(); @@ -55,6 +56,8 @@ private slots: void onRename(); void onRemove(); void onShare(); + void onShareToUser(); + void onShareToGroup(); void onShareSeafile(); void onUpdate(); void onCopy(); @@ -80,6 +83,7 @@ private slots: void dragMoveEvent(QDragMoveEvent *event); void dragEnterEvent(QDragEnterEvent *event); void resizeEvent(QResizeEvent *event); + void onShareToUserOrGroup(bool to_group); Q_DISABLE_COPY(FileTableView) @@ -93,6 +97,8 @@ private slots: QAction *rename_action_; QAction *remove_action_; QAction *share_action_; + QAction *share_to_user_action_; + QAction *share_to_group_action_; QAction *share_seafile_action_; QAction *update_action_; QAction *copy_action_; diff --git a/src/ui/private-share-dialog.cpp b/src/ui/private-share-dialog.cpp new file mode 100644 index 000000000..06edbe91a --- /dev/null +++ b/src/ui/private-share-dialog.cpp @@ -0,0 +1,922 @@ +#include +#include +#include +#include +#include +#include "api/api-error.h" +#include "api/requests.h" +#include "private-share-dialog.h" +#include "seafile-applet.h" +#include "utils/file-utils.h" +#include "utils/utils.h" + +namespace +{ +enum { + COLUMN_NAME = 0, + COLUMN_PERMISSION, + MAX_COLUMN, +}; + +const int kPermissionColumnWidth = 150; +const int kNameColumnWidth = 300; +const int kDefaultColumnHeight = 40; +const int kIndicatorIconWidth = 10; +const int kIndicatorIconHeight = 8; + +const int kMarginLeft = 2; +const int kMarginTop = 2; +const int kPadding = 2; +const int kMarginBetweenPermissionAndIndicator = 10; + +const QColor kSelectedItemBackgroundcColor("#F9E0C7"); +const QColor kItemBackgroundColor("white"); +const QColor kItemBottomBorderColor("#f3f3f3"); +const QColor kItemColor("black"); + +} // namespace + +PrivateShareDialog::PrivateShareDialog(const Account& account, + const QString& repo_id, + const QString& repo_name, + const QString& path, + bool to_group, + QWidget* parent) + : QDialog(parent), + account_(account), + repo_id_(repo_id), + repo_name_(repo_name), + path_(path), + to_group_(to_group), + request_in_progress_(false) +{ + setupUi(this); + + setWindowTitle( + tr("Share %1") + .arg(path.length() <= 1 ? repo_name : ::getBaseName(path))); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + mUsername->setPlaceholderText(to_group_ ? tr("Enter the group name") + : tr("Enter the email address")); + mOkBtn->setEnabled(false); + mPermission->setCurrentIndex(0); + createTable(); + + fetchContacsForCompletion(); + + connect(mOkBtn, SIGNAL(clicked()), this, SLOT(onOkBtnClicked())); + connect(mCancelBtn, SIGNAL(clicked()), this, SLOT(reject())); + + connect(table_, SIGNAL(clicked(const QModelIndex&)), mStatusText, + SLOT(clear())); + connect(mUsername, SIGNAL(textEdited(const QString&)), mStatusText, + SLOT(clear())); + connect(mUsername, SIGNAL(textEdited(const QString&)), this, + SLOT(onNameInputEdited())); + connect(model_, SIGNAL(modelReset()), this, SLOT(selectFirstRow())); + + adjustSize(); + disableInputs(); +} + +void PrivateShareDialog::selectFirstRow() +{ + // for (int i = 0; i < model_->rowCount(); i++) { + // table_->openPersistentEditor(model_->index(i, COLUMN_PERMISSION)); + // } + + // Select the first row of the table, so that the indicator would be + // painted, to tell the user the permission is editable. + if (!table_->currentIndex().isValid()) { + table_->setCurrentIndex(model_->index(0, 0)); + } +} + +void PrivateShareDialog::createTable() +{ + table_ = new SharedItemsTableView(this); + // table_->setEditTriggers(QAbstractItemView::AllEditTriggers); + model_ = new SharedItemsTableModel( + to_group_ ? SHARE_TO_GROUP : SHARE_TO_USER, this); + table_->setModel(model_); + QVBoxLayout* vlayout = (QVBoxLayout*)mFrame->layout(); + vlayout->insertWidget(1, table_); + + table_->setItemDelegate(new SharedItemDelegate(this)); + + connect(model_, SIGNAL(updateShareItem(int, SharePermission)), this, + SLOT(onUpdateShareItem(int, SharePermission))); + connect(model_, SIGNAL(updateShareItem(const QString&, SharePermission)), + this, SLOT(onUpdateShareItem(const QString&, SharePermission))); + connect(model_, SIGNAL(removeShareItem(int, SharePermission)), this, + SLOT(onRemoveShareItem(int, SharePermission))); + connect(model_, SIGNAL(removeShareItem(const QString&, SharePermission)), + this, SLOT(onRemoveShareItem(const QString&, SharePermission))); +} + +void PrivateShareDialog::onNameInputEdited() +{ + mOkBtn->setEnabled(!mUsername->text().trimmed().isEmpty()); +} + +void PrivateShareDialog::fetchContacsForCompletion() +{ + contacts_request_.reset(new FetchGroupsAndContactsRequest(account_)); + contacts_request_->send(); + connect(contacts_request_.data(), + SIGNAL(success(const QList&, + const QList&)), + this, SLOT(onFetchContactsSuccess(const QList&, + const QList&))); + connect(contacts_request_.data(), SIGNAL(failed(const ApiError&)), this, + SLOT(onFetchContactsFailed(const ApiError&))); +} + +void PrivateShareDialog::onUpdateShareItem(int group_id, + SharePermission permission) +{ + request_.reset(new PrivateShareRequest(account_, repo_id_, path_, QString(), + group_id, permission, SHARE_TO_GROUP, + PrivateShareRequest::UPDATE_SHARE)); + + connect(request_.data(), SIGNAL(success()), this, + SLOT(onUpdateShareSuccess())); + connect(request_.data(), SIGNAL(failed(const ApiError&)), this, + SLOT(onUpdateShareFailed(const ApiError&))); + + // disableInputs(); + request_in_progress_ = true; + request_->send(); +} + +void PrivateShareDialog::onUpdateShareItem(const QString& email, + SharePermission permission) +{ + request_.reset(new PrivateShareRequest(account_, repo_id_, path_, email, 0, + permission, SHARE_TO_USER, + PrivateShareRequest::UPDATE_SHARE)); + + connect(request_.data(), SIGNAL(success()), this, + SLOT(onUpdateShareSuccess())); + connect(request_.data(), SIGNAL(failed(const ApiError&)), this, + SLOT(onUpdateShareFailed(const ApiError&))); + + // disableInputs(); + request_in_progress_ = true; + request_->send(); +} + +void PrivateShareDialog::onUpdateShareSuccess() +{ + request_in_progress_ = false; + // seafApplet->messageBox(tr("Shared successfully"), this); + if (to_group_) { + GroupShareInfo info; + info.group.id = request_.data()->groupId(); + info.group.name = group_id_map_[info.group.id]; + info.permission = request_.data()->permission(); + model_->addNewShareInfo(info); + } + else { + UserShareInfo info; + info.user.email = request_.data()->userName(); + info.permission = request_.data()->permission(); + model_->addNewShareInfo(info); + } + model_->shareOperationSuccess(); + // enableInputs(); + mStatusText->setText(tr("Updated successfully")); +} + +void PrivateShareDialog::onUpdateShareFailed(const ApiError& error) +{ + // enableInputs(); + request_in_progress_ = false; + showWarning(tr("Share Operation Failed: %1").arg(error.toString())); + model_->shareOperationFailed(request_->shareOperation()); +} + +void PrivateShareDialog::onRemoveShareItem(int group_id, + SharePermission permission) +{ + request_.reset(new PrivateShareRequest(account_, repo_id_, path_, QString(), + group_id, permission, SHARE_TO_GROUP, + PrivateShareRequest::REMOVE_SHARE)); + + connect(request_.data(), SIGNAL(success()), this, + SLOT(onRemoveShareSuccess())); + connect(request_.data(), SIGNAL(failed(const ApiError&)), this, + SLOT(onRemoveShareFailed(const ApiError&))); + + // disableInputs(); + request_in_progress_ = true; + request_->send(); +} + +void PrivateShareDialog::onRemoveShareSuccess() +{ + // enableInputs(); + request_in_progress_ = false; + model_->shareOperationSuccess(); + mStatusText->setText(tr("Removed successfully")); +} + +void PrivateShareDialog::onRemoveShareFailed(const ApiError& error) +{ + request_in_progress_ = false; + showWarning(tr("Share Operation Failed: %1").arg(error.toString())); + model_->shareOperationFailed(request_->shareOperation()); + // enableInputs(); +} + +void PrivateShareDialog::onRemoveShareItem(const QString& email, + SharePermission permission) +{ + request_.reset(new PrivateShareRequest(account_, repo_id_, path_, email, 0, + permission, SHARE_TO_USER, + PrivateShareRequest::REMOVE_SHARE)); + + connect(request_.data(), SIGNAL(success()), this, + SLOT(onRemoveShareSuccess())); + connect(request_.data(), SIGNAL(failed(const ApiError&)), this, + SLOT(onRemoveShareFailed(const ApiError&))); + + // disableInputs(); + request_in_progress_ = true; + request_->send(); +} + + +void PrivateShareDialog::onFetchContactsSuccess( + const QList& groups, const QList& contacts) +{ + QStringList candidates; + if (to_group_) { + foreach (const SeafileGroup& group, groups) { + candidates << group.name; + group_id_map_[group.id] = group.name; + } + } + else { + foreach (const SeafileContact& contact, contacts) { + candidates << contact.email; + } + } + + if (!candidates.isEmpty()) { + completer_.reset(new QCompleter(candidates)); + mUsername->setCompleter(completer_.data()); + } + + get_shared_items_request_.reset( + new GetPrivateShareItemsRequest(account_, repo_id_, path_)); + + connect(get_shared_items_request_.data(), + SIGNAL(success(const QList&, + const QList&)), + this, SLOT(onGetSharedItemsSuccess(const QList&, + const QList&))); + connect(get_shared_items_request_.data(), SIGNAL(failed(const ApiError&)), + this, SLOT(onGetSharedItemsFailed(const ApiError&))); + + get_shared_items_request_->send(); +} + +void PrivateShareDialog::onGetSharedItemsSuccess( + const QList& group_shares, + const QList& user_shares) +{ + model_->setShareInfo(group_shares, user_shares); + selectFirstRow(); + enableInputs(); +} + +void PrivateShareDialog::onGetSharedItemsFailed(const ApiError& error) +{ + showWarning(tr("Failed to get share information of the folder")); + reject(); +} + +void PrivateShareDialog::onFetchContactsFailed(const ApiError& error) +{ + showWarning(tr("Failed to get your groups and contacts information")); + reject(); +} + +SharePermission PrivateShareDialog::currentPermission() +{ + return mPermission->currentIndex() == 0 ? READ_WRITE : READ_ONLY; +} + +bool PrivateShareDialog::validateInputs() +{ + QString name = mUsername->text().trimmed(); + if (name.isEmpty()) { + showWarning(to_group_ ? tr("Please enter the username") + : tr("Please enter the group name")); + return false; + } + + SharePermission permission = currentPermission(); + + if (to_group_) { + QList ids = group_id_map_.keys(name); + if (ids.isEmpty()) { + showWarning(tr("No such group \"%1\"").arg(name)); + return false; + } + int group_id = ids[0]; + if (model_->shareExists(group_id)) { + GroupShareInfo info = model_->shareInfo(group_id); + if (info.permission == permission) { + showWarning(tr("Already shared to group %1").arg(name)); + } + return false; + } + } + else { + if (model_->shareExists(name)) { + UserShareInfo info = model_->shareInfo(name); + if (info.permission == permission) { + showWarning(tr("Already shared to user %1").arg(name)); + return false; + } + } + } + + return true; +} + +void PrivateShareDialog::onOkBtnClicked() +{ + if (!validateInputs()) { + return; + } + if (request_in_progress_) { + showWarning(tr("The previous operation is still in progres")); + return; + } + + // disableInputs(); + QString username; + int group_id = 0; + QString name = mUsername->text().trimmed(); + if (to_group_) { + group_id = group_id_map_.keys(name)[0]; + } + else { + username = name; + } + request_.reset(new PrivateShareRequest( + account_, repo_id_, path_, username, group_id, currentPermission(), + to_group_ ? SHARE_TO_GROUP : SHARE_TO_USER, + PrivateShareRequest::ADD_SHARE)); + + connect(request_.data(), SIGNAL(success()), this, SLOT(onShareSuccess())); + + connect(request_.data(), SIGNAL(failed(const ApiError&)), this, + SLOT(onShareFailed(const ApiError&))); + + request_in_progress_ = true; + request_->send(); + + if (to_group_) { + GroupShareInfo info; + info.group.id = group_id; + info.group.name = name; + info.permission = currentPermission(); + model_->addNewShareInfo(info); + } + else { + UserShareInfo info; + info.user.email = name; + info.permission = currentPermission(); + model_->addNewShareInfo(info); + } +} + +void PrivateShareDialog::disableInputs() +{ + toggleInputs(false); +} + +void PrivateShareDialog::enableInputs() +{ + toggleInputs(true); +} + +void PrivateShareDialog::toggleInputs(bool enabled) +{ + mUsername->setEnabled(enabled); + mOkBtn->setEnabled(enabled); + mCancelBtn->setEnabled(enabled); + mPermission->setEnabled(enabled); +} + +void PrivateShareDialog::onShareSuccess() +{ + // seafApplet->messageBox(tr("Shared successfully"), this); + request_in_progress_ = false; + model_->shareOperationSuccess(); + // enableInputs(); + mStatusText->setText(tr("Shared successfully")); +} + +void PrivateShareDialog::onShareFailed(const ApiError& error) +{ + request_in_progress_ = false; + // enableInputs(); + model_->shareOperationFailed(PrivateShareRequest::ADD_SHARE); + showWarning(tr("Share Operation Failed: %1").arg(error.toString())); +} + +void PrivateShareDialog::showWarning(const QString& msg) +{ + seafApplet->warningBox(msg, this); +} + +SharedItemsHeadView::SharedItemsHeadView(QWidget* parent) + : QHeaderView(Qt::Horizontal, parent) +{ + setDefaultAlignment(Qt::AlignLeft); + setStretchLastSection(false); + setCascadingSectionResizes(true); + setHighlightSections(false); + setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + setSectionResizeMode(QHeaderView::ResizeToContents); +#else + setResizeMode(QHeaderView::ResizeToContents); +#endif +} + +QSize SharedItemsHeadView::sectionSizeFromContents(int index) const +{ + QSize size = QHeaderView::sectionSizeFromContents(index); + SharedItemsTableView* table = (SharedItemsTableView*)parent(); + SharedItemsTableModel* model = + (SharedItemsTableModel*)(table->sourceModel()); + if (model) { + size.setWidth(index == COLUMN_NAME ? model->nameColumnWidth() + : kPermissionColumnWidth); + } + return size; +} + +SharedItemsTableView::SharedItemsTableView(QWidget* parent) + : QTableView(parent), source_model_(0) +{ + setHorizontalHeader(new SharedItemsHeadView(this)); + verticalHeader()->hide(); + + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + + setMouseTracking(true); + setShowGrid(false); + setContentsMargins(0, 5, 0, 5); + // setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +void SharedItemsTableView::setModel(QAbstractItemModel* model) +{ + QTableView::setModel(model); + source_model_ = qobject_cast(model); +} + +void SharedItemsTableView::resizeEvent(QResizeEvent* event) +{ + QTableView::resizeEvent(event); + if (source_model_) + source_model_->onResize(event->size()); +} + + +SharedItemsTableModel::SharedItemsTableModel(ShareType share_type, + QObject* parent) + : QAbstractTableModel(parent), + share_type_(share_type), + name_column_width_(kNameColumnWidth) +{ +} + + +int SharedItemsTableModel::columnCount(const QModelIndex& parent) const +{ + return MAX_COLUMN; +} + +int SharedItemsTableModel::rowCount(const QModelIndex& parent) const +{ + return share_type_ == SHARE_TO_USER ? user_shares_.size() + : group_shares_.size(); +} + +QVariant SharedItemsTableModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (role != Qt::DisplayRole && role != Qt::EditRole && + role != Qt::SizeHintRole) { + return QVariant(); + } + + int column = index.column(); + + if (role == Qt::EditRole && column != COLUMN_PERMISSION) { + return QVariant(); + } + + if (role == Qt::SizeHintRole) { + QSize qsize(0, kDefaultColumnHeight); + if (column == COLUMN_NAME) { + qsize.setWidth(name_column_width_); + } + else { + qsize.setWidth(kPermissionColumnWidth); + } + return qsize; + } + + if (isGroupShare()) { + if (index.row() >= group_shares_.size()) { + return QVariant(); + } + const GroupShareInfo& info = group_shares_[index.row()]; + + if (column == COLUMN_NAME) { + return info.group.name; + } + else if (column == COLUMN_PERMISSION) { + if (role == Qt::DisplayRole) { + return info.permission == READ_WRITE ? tr("Read Write") + : tr("Read Only"); + } + else { + return info.permission == READ_WRITE ? 0 : 1; + } + } + } + else { + if (index.row() >= user_shares_.size()) { + return QVariant(); + } + const UserShareInfo& info = user_shares_[index.row()]; + + if (column == COLUMN_NAME) { + return info.user.email; + } + else if (column == COLUMN_PERMISSION) { + if (role == Qt::DisplayRole) { + return info.permission == READ_WRITE ? tr("Read Write") + : tr("Read Only"); + } + else { + return info.permission == READ_WRITE ? 0 : 1; + } + } + } + + return QVariant(); +} + +void SharedItemsTableModel::onResize(const QSize& size) +{ + name_column_width_ = size.width() - kPermissionColumnWidth; + if (rowCount() == 0) { + emit dataChanged(index(0, COLUMN_NAME), + index(rowCount() - 1, COLUMN_NAME)); + } +} + +bool SharedItemsTableModel::isGroupShare() const +{ + return share_type_ == SHARE_TO_GROUP; +} + +QVariant SharedItemsTableModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Vertical) { + return QVariant(); + } + + if (section == COLUMN_NAME) { + if (role == Qt::DisplayRole) { + return isGroupShare() ? tr("Group") : tr("User"); + } + } + else if (section == COLUMN_PERMISSION) { + if (role == Qt::DisplayRole) { + return tr("Permission"); + } + } + + + return QVariant(); +} + + +void SharedItemsTableModel::setShareInfo( + const QList& group_shares, + const QList& user_shares) +{ + beginResetModel(); + group_shares_ = group_shares; + user_shares_ = user_shares; + endResetModel(); +} + +void SharedItemsTableModel::addNewShareInfo(UserShareInfo newinfo) +{ + previous_user_shares_ = user_shares_; + beginResetModel(); + bool exists; + for (int i = 0; i < user_shares_.size(); i++) { + UserShareInfo& info = user_shares_[i]; + if (info.user.email == newinfo.user.email) { + exists = true; + info.permission = newinfo.permission; + } + } + if (!exists) { + user_shares_.prepend(newinfo); + } + endResetModel(); +} + +void SharedItemsTableModel::addNewShareInfo(GroupShareInfo newinfo) +{ + previous_group_shares_ = group_shares_; + beginResetModel(); + bool exists; + for (int i = 0; i < group_shares_.size(); i++) { + GroupShareInfo& info = group_shares_[i]; + if (info.group.id == newinfo.group.id) { + exists = true; + info.permission = newinfo.permission; + } + } + if (!exists) { + group_shares_.prepend(newinfo); + } + endResetModel(); +} + +bool SharedItemsTableModel::shareExists(int group_id) +{ + foreach (const GroupShareInfo& info, group_shares_) { + if (info.group.id == group_id) { + return true; + } + } + return false; +} + +bool SharedItemsTableModel::shareExists(const QString& email) +{ + foreach (const UserShareInfo& info, user_shares_) { + if (info.user.email == email) { + return true; + } + } + return false; +} + +GroupShareInfo SharedItemsTableModel::shareInfo(int group_id) +{ + foreach (const GroupShareInfo& info, group_shares_) { + if (info.group.id == group_id) { + return info; + } + } + return GroupShareInfo(); +} + +UserShareInfo SharedItemsTableModel::shareInfo(const QString& email) +{ + foreach (const UserShareInfo& info, user_shares_) { + if (info.user.email == email) { + return info; + } + } + return UserShareInfo(); +} + +Qt::ItemFlags SharedItemsTableModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + if (index.column() == COLUMN_PERMISSION) { + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; + } + else { + return QAbstractItemModel::flags(index); + } +} + +bool SharedItemsTableModel::removeRows(int row, + int count, + const QModelIndex& parent) +{ + beginRemoveRows(parent, row, row); + if (isGroupShare()) { + group_shares_.removeAt(row); + } + else { + user_shares_.removeAt(row); + } + endRemoveRows(); + return true; +} + +bool SharedItemsTableModel::setData(const QModelIndex& index, + const QVariant& value, + int role) +{ + PrivateShareDialog* dialog = (PrivateShareDialog*)parent(); + if (dialog->requestInProgress()) { + dialog->showWarning(tr("The previous operation is still in progres")); + return false; + } + if (!index.isValid() || role != Qt::EditRole) { + return false; + } + int permission = value.toInt(); + int row = index.row(); + if (isGroupShare()) { + previous_group_shares_ = group_shares_; + GroupShareInfo& info = group_shares_[row]; + if (permission == 3) { + emit removeShareItem(info.group.id, info.permission); + removed_group_share_ = info; + removeRows(row, 1); + } + else if (permission == info.permission) { + } + else { + emit updateShareItem(info.group.id, (SharePermission)permission); + info.permission = + info.permission == READ_ONLY ? READ_WRITE : READ_ONLY; + emit dataChanged(index, index); + } + } + else { + previous_user_shares_ = user_shares_; + UserShareInfo& info = user_shares_[row]; + if (permission == 3) { + emit removeShareItem(info.user.email, info.permission); + removed_user_share_ = info; + removeRows(row, 1); + } + else if (permission == info.permission) { + } + else { + emit updateShareItem(info.user.email, (SharePermission)permission); + info.permission = + info.permission == READ_ONLY ? READ_WRITE : READ_ONLY; + emit dataChanged(index, index); + } + } + + return true; +} + +void SharedItemsTableModel::shareOperationSuccess() +{ +} + +void SharedItemsTableModel::shareOperationFailed( + PrivateShareRequest::ShareOperation op) +{ + beginResetModel(); + if (isGroupShare()) { + group_shares_ = previous_group_shares_; + } + else { + user_shares_ = previous_user_shares_; + } + endResetModel(); +} + +SharedItemDelegate::SharedItemDelegate(QObject* parent) + : QStyledItemDelegate(parent) +{ +} + +QWidget* SharedItemDelegate::createEditor(QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QComboBox* combobox = new QComboBox(parent); + combobox->addItem(tr("Read Write")); + combobox->addItem(tr("Read Only")); + combobox->insertSeparator(2); + combobox->addItem(tr("Remove Share")); + return combobox; +} + +void SharedItemDelegate::setEditorData(QWidget* editor, + const QModelIndex& index) const +{ + int value = index.model()->data(index, Qt::EditRole).toInt(); + + QComboBox* combobox = static_cast(editor); + combobox->setCurrentIndex(value); +} + +void SharedItemDelegate::setModelData(QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index) const +{ + QComboBox* combobox = static_cast(editor); + model->setData(index, combobox->currentIndex(), Qt::EditRole); +} + +void SharedItemDelegate::updateEditorGeometry( + QWidget* editor, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QComboBox* combobox = static_cast(editor); + combobox->setGeometry(option.rect); + // combobox->showPopup(); +} + +void SharedItemDelegate::paint(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + const SharedItemsTableModel* model = + static_cast(index.model()); + + QRect option_rect = option.rect; + bool selected = false; + // draw item's background + painter->save(); + if (option.state & QStyle::State_Selected) { + painter->fillRect(option_rect, kSelectedItemBackgroundcColor); + selected = true; + } + else + painter->fillRect(option_rect, kItemBackgroundColor); + painter->restore(); + + // draw item's border for the first row only + static const QPen borderPen(kItemBottomBorderColor, 1); + // if (index.row() == 0) { + // painter->save(); + // painter->setPen(borderPen); + // painter->drawLine(option_rect.topLeft(), option_rect.topRight()); + // painter->restore(); + // } + // draw item's border under the bottom + painter->save(); + painter->setPen(borderPen); + painter->drawLine(option_rect.bottomLeft(), option_rect.bottomRight()); + painter->restore(); + + QPoint text_pos(kMarginLeft + kPadding, kMarginTop + kPadding); + text_pos += option.rect.topLeft(); + + QSize size = model->data(index, Qt::SizeHintRole).value(); + QString text = model->data(index, Qt::DisplayRole).value(); + QFont font = model->data(index, Qt::FontRole).value(); + QRect text_rect(text_pos, size); + painter->save(); + painter->setPen(kItemColor); + painter->setFont(font); + painter->drawText(text_rect, + Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, text, + &text_rect); + painter->restore(); + + if (selected && index.column() == COLUMN_PERMISSION) { + int h = option.rect.height(); + QPoint indicator_pos = option.rect.bottomRight() - + QPoint(40, h - (h - kIndicatorIconHeight) / 2); + indicator_pos.setX(text_rect.topRight().x() + + kMarginBetweenPermissionAndIndicator); + QRect indicator_rect(indicator_pos, + QSize(kIndicatorIconWidth, kIndicatorIconHeight)); + + QPainterPath path; + path.moveTo(indicator_rect.topLeft()); + path.lineTo(indicator_rect.topRight()); + path.lineTo(indicator_rect.bottomRight() - + QPoint((indicator_rect.width() / 2), 0)); + path.lineTo(indicator_rect.topLeft()); + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setRenderHint(QPainter::HighQualityAntialiasing); + painter->fillPath(path, QBrush(kItemColor)); + painter->restore(); + } +} diff --git a/src/ui/private-share-dialog.h b/src/ui/private-share-dialog.h new file mode 100644 index 000000000..57f6e7119 --- /dev/null +++ b/src/ui/private-share-dialog.h @@ -0,0 +1,220 @@ +#ifndef SEAFILE_CLIENT_UI_PRIVATE_SHARE_DIALOG_H +#define SEAFILE_CLIENT_UI_PRIVATE_SHARE_DIALOG_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_private-share-dialog.h" + +#include "account.h" +#include "api/contact-share-info.h" +#include "api/requests.h" + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) +// only available in qt 5.0+ +#define Q_DECL_OVERRIDE +#endif + + +class QResizeEvent; + +class SharedItemsTableView; +class SharedItemsTableModel; + +class PrivateShareDialog : public QDialog, public Ui::PrivateShareDialog +{ + Q_OBJECT +public: + PrivateShareDialog(const Account& account, + const QString& repo_id, + const QString& repo_name, + const QString& path, + bool to_group, + QWidget* parent); + + bool requestInProgress() const + { + return request_in_progress_; + } + void showWarning(const QString& msg); + +public slots: + void onUpdateShareItem(int group_id, SharePermission permission); + void onUpdateShareItem(const QString& email, SharePermission permission); + void onRemoveShareItem(int group_id, SharePermission permission); + void onRemoveShareItem(const QString& email, SharePermission permission); + +private slots: + void selectFirstRow(); + void onNameInputEdited(); + void onShareSuccess(); + void onShareFailed(const ApiError& error); + void onUpdateShareSuccess(); + void onUpdateShareFailed(const ApiError& error); + void onRemoveShareSuccess(); + void onRemoveShareFailed(const ApiError& error); + void onFetchContactsSuccess(const QList& groups, + const QList& contacts); + void onFetchContactsFailed(const ApiError& error); + void onOkBtnClicked(); + void onGetSharedItemsSuccess(const QList& group_shares, + const QList& user_shares); + void onGetSharedItemsFailed(const ApiError& error); + +private: + void createTable(); + void fetchContacsForCompletion(); + bool validateInputs(); + void toggleInputs(bool enabled); + void enableInputs(); + void disableInputs(); + SharePermission currentPermission(); + + Account account_; + QString repo_id_; + QString repo_name_; + QString path_; + bool to_group_; + + QHash group_id_map_; + + SharedItemsTableView* table_; + SharedItemsTableModel* model_; + + QScopedPointer request_; + QScopedPointer contacts_request_; + QScopedPointer get_shared_items_request_; + QScopedPointer completer_; + + bool request_in_progress_; +}; + +class SharedItemsHeadView : public QHeaderView +{ + Q_OBJECT +public: + SharedItemsHeadView(QWidget* parent = 0); + + QSize sectionSizeFromContents(int index) const Q_DECL_OVERRIDE; +}; + +class SharedItemsTableView : public QTableView +{ + Q_OBJECT +public: + SharedItemsTableView(QWidget* parent = 0); + + void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE; + + void setModel(QAbstractItemModel* model) Q_DECL_OVERRIDE; + + SharedItemsTableModel* sourceModel() + { + return source_model_; + } + +private: + SharedItemsTableModel* source_model_; +}; + +class SharedItemsTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + SharedItemsTableModel(ShareType share_type, QObject* parent = 0); + + void setShareInfo(const QList& group_shares, + const QList& user_shares); + + void addNewShareInfo(UserShareInfo info); + void addNewShareInfo(GroupShareInfo info); + + bool shareExists(int group_id); + bool shareExists(const QString& email); + + GroupShareInfo shareInfo(int group_id); + UserShareInfo shareInfo(const QString& email); + + void shareOperationSuccess(); + void shareOperationFailed(PrivateShareRequest::ShareOperation op); + + int rowCount(const QModelIndex& parent = QModelIndex()) const + Q_DECL_OVERRIDE; + int columnCount(const QModelIndex& parent = QModelIndex()) const + Q_DECL_OVERRIDE; + QVariant data(const QModelIndex& index, + int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE; + bool setData(const QModelIndex& index, + const QVariant& value, + int role) Q_DECL_OVERRIDE; + bool removeRows(int row, + int count, + const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE; + + QVariant headerData(int section, + Qt::Orientation orientation, + int role) const Q_DECL_OVERRIDE; + + int nameColumnWidth() const + { + return name_column_width_; + } +signals: + void updateShareItem(int group_id, SharePermission permission); + void updateShareItem(const QString& email, SharePermission permission); + void removeShareItem(int group_id, SharePermission permission); + void removeShareItem(const QString& email, SharePermission permission); + +public slots: + void onResize(const QSize& size); + +private: + bool isGroupShare() const; + + QList user_shares_, previous_user_shares_; + QList group_shares_, previous_group_shares_; + + ShareType share_type_; + int name_column_width_; + + GroupShareInfo removed_group_share_; + UserShareInfo removed_user_share_; +}; + + +class SharedItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + SharedItemDelegate(QObject* parent = 0); + + QWidget* createEditor(QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const Q_DECL_OVERRIDE; + + void setEditorData(QWidget* editor, + const QModelIndex& index) const Q_DECL_OVERRIDE; + void setModelData(QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index) const Q_DECL_OVERRIDE; + + void updateEditorGeometry(QWidget* editor, + const QStyleOptionViewItem& option, + const QModelIndex& index) const Q_DECL_OVERRIDE; + + void paint(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const Q_DECL_OVERRIDE; +}; + + +#endif // SEAFILE_CLIENT_UI_PRIVATE_SHARE_DIALOG_H diff --git a/src/utils/json-utils.cpp b/src/utils/json-utils.cpp index 1bc66c631..6d38cc75f 100644 --- a/src/utils/json-utils.cpp +++ b/src/utils/json-utils.cpp @@ -8,18 +8,40 @@ Json::Json(const json_t *root) QString Json::getString(const char *key) const { + if (!json_) { + return QString(); + } return QString::fromUtf8(json_string_value(json_object_get(json_, key))); } qint64 Json::getLong(const char *key) const { + if (!json_) { + return 0; + } return json_integer_value(json_object_get(json_, key)); } bool Json::getBool(const char *key) const { + if (!json_) { + return false; + } + json_t *value = json_object_get(json_, key); if (json_is_false(value)) return false; return json_is_true(value) || json_integer_value(value); } + +Json Json::getObject(const char *key) const +{ + if (!json_) { + return Json(); + } + + json_t *object = json_object_get(json_, key); + if (json_is_object(object)) + return Json(object); + return Json(); +} diff --git a/src/utils/json-utils.h b/src/utils/json-utils.h index fe64817fb..198a4cfe6 100644 --- a/src/utils/json-utils.h +++ b/src/utils/json-utils.h @@ -6,11 +6,12 @@ // A convenient class to access jasson `json_t` struct class Json { public: - Json(const json_t *root); + Json(const json_t *root = 0); QString getString(const char *name) const; qint64 getLong(const char *name) const; bool getBool(const char *name) const; + Json getObject(const char *name) const; private: const json_t *json_; diff --git a/ui/private-share-dialog.ui b/ui/private-share-dialog.ui new file mode 100644 index 000000000..7b1a979a9 --- /dev/null +++ b/ui/private-share-dialog.ui @@ -0,0 +1,114 @@ + + + PrivateShareDialog + + + + 0 + 0 + 455 + 155 + + + + Dialog + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Share To: + + + + + + + + + + Share + + + + + + + Permission: + + + + + + + + Read-Write + + + + + Read-Only + + + + + + + + mOkBtn + mOkBtn + + + + + + 10 + + + + + true + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Done + + + + + + + + + +