diff --git a/CHANGELOG.md b/CHANGELOG.md index f3431bba751..32db84d4880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ ownCloud admins and users. * Enhancement - Update test in GitHub Actions: [#4663](https://github.com/owncloud/android/pull/4663) * Enhancement - New workflow to generate a build from "latest" tag on demand: [#4681](https://github.com/owncloud/android/pull/4681) * Enhancement - Make Update test more robust: [#4690](https://github.com/owncloud/android/pull/4690) +* Enhancement - Add user role to spaces: [#4698](https://github.com/owncloud/android/pull/4698) ## Details @@ -156,6 +157,13 @@ ownCloud admins and users. https://github.com/owncloud/android/pull/4690 +* Enhancement - Add user role to spaces: [#4698](https://github.com/owncloud/android/pull/4698) + + A new field representing the user role has been added to the space model and to + the spaces table in the database. + + https://github.com/owncloud/android/pull/4698 + # Changelog for ownCloud Android Client [4.6.2] (2025-08-13) The following sections list the changes in ownCloud Android Client 4.6.2 relevant to diff --git a/changelog/unreleased/4698 b/changelog/unreleased/4698 new file mode 100644 index 00000000000..9ec0d4b1158 --- /dev/null +++ b/changelog/unreleased/4698 @@ -0,0 +1,5 @@ +Enhancement: Add user role to spaces + +A new field representing the user role has been added to the space model and to the spaces table in the database. + +https://github.com/owncloud/android/pull/4698 diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 94e55d086f6..799cff81337 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -109,6 +109,7 @@ import com.owncloud.android.domain.transfers.usecases.UpdatePendingUploadsPathUs import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase import com.owncloud.android.domain.user.usecases.GetStoredQuotaAsStreamUseCase import com.owncloud.android.domain.user.usecases.GetUserAvatarAsyncUseCase +import com.owncloud.android.domain.user.usecases.GetUserGroupsAsyncUseCase import com.owncloud.android.domain.user.usecases.GetUserInfoAsyncUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasAsStreamUseCase @@ -269,6 +270,7 @@ val useCaseModule = module { factoryOf(::GetStoredQuotaAsStreamUseCase) factoryOf(::GetStoredQuotaUseCase) factoryOf(::GetUserAvatarAsyncUseCase) + factoryOf(::GetUserGroupsAsyncUseCase) factoryOf(::GetUserIdAsyncUseCase) factoryOf(::GetUserInfoAsyncUseCase) factoryOf(::GetUserPermissionsAsyncUseCase) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt index 27bb123db3a..6e53cf5848b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt @@ -64,6 +64,7 @@ data class RootResponse( val id: String, val webDavUrl: String, val deleted: DeleteResponse?, + val permissions: List? ) @JsonClass(generateAdapter = true) @@ -97,3 +98,20 @@ data class DeleteResponse( data class SpecialFolderResponse( val name: String ) + +@JsonClass(generateAdapter = true) +data class PermissionsResponse( + val grantedToV2: GrantedToV2Response, + val roles: List +) + +@JsonClass(generateAdapter = true) +data class GrantedToV2Response( + val user: UserResponse?, + val group: GroupResponse? +) + +@JsonClass(generateAdapter = true) +data class GroupResponse( + val id: String +) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserGroupsOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserGroupsOperation.kt new file mode 100644 index 00000000000..968f43017cc --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserGroupsOperation.kt @@ -0,0 +1,81 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * Copyright (C) 2025 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.lib.resources.users + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.resources.spaces.responses.GroupResponse +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Moshi +import timber.log.Timber +import java.net.URL + +class GetRemoteUserGroupsOperation: RemoteOperation>() { + override fun run(client: OwnCloudClient): RemoteOperationResult> { + var result: RemoteOperationResult> + try { + val uriBuilder = client.baseUri.buildUpon().apply { + appendEncodedPath(GRAPH_ME_ENDPOINT) + appendQueryParameter(QUERY_PARAMETER_EXPAND, QUERY_PARAMETER_EXPAND_VALUE) + } + + val getMethod = GetMethod(URL(uriBuilder.build().toString())) + + val status = client.executeHttpMethod(getMethod) + + val response = getMethod.getResponseBodyAsString() + + if (status == HttpConstants.HTTP_OK) { + Timber.d("Successful response: $response") + + val moshi: Moshi = Moshi.Builder().build() + val adapter: JsonAdapter = moshi.adapter(GraphMeResponse::class.java) + + result = RemoteOperationResult(ResultCode.OK) + result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it)!!.memberOf.map {group -> group.id } } + + Timber.d("Get user groups completed and parsed to ${result.data}") + } else { + result = RemoteOperationResult(getMethod) + Timber.e("Failed response while getting user groups; status code: $status, response: $response") + } + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Exception while getting oCIS user groups") + } + return result + } + + @JsonClass(generateAdapter = true) + data class GraphMeResponse(val memberOf: List) + + companion object { + private const val GRAPH_ME_ENDPOINT = "graph/v1.0/me" + private const val QUERY_PARAMETER_EXPAND = "\$expand" + private const val QUERY_PARAMETER_EXPAND_VALUE = "memberOf" + } + +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/UserService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/UserService.kt index dad0c9322e2..57e50c34e11 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/UserService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/UserService.kt @@ -36,4 +36,5 @@ interface UserService : Service { fun getUserAvatar(avatarDimension: Int): RemoteOperationResult fun getUserId(): RemoteOperationResult fun getUserPermissions(accountId: String): RemoteOperationResult> + fun getUserGroups(): RemoteOperationResult> } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt index 1b7b822f01c..769c36ce210 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt @@ -29,6 +29,7 @@ package com.owncloud.android.lib.resources.users.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.users.GetRemoteUserAvatarOperation +import com.owncloud.android.lib.resources.users.GetRemoteUserGroupsOperation import com.owncloud.android.lib.resources.users.GetRemoteUserIdOperation import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation import com.owncloud.android.lib.resources.users.GetRemoteUserPermissionsOperation @@ -53,4 +54,7 @@ class OCUserService(override val client: OwnCloudClient) : UserService { override fun getUserPermissions(accountId: String): RemoteOperationResult> = GetRemoteUserPermissionsOperation(accountId).execute(client) + override fun getUserGroups(): RemoteOperationResult> = + GetRemoteUserGroupsOperation().execute(client) + } diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/48.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/48.json new file mode 100644 index 00000000000..2523f675a34 --- /dev/null +++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/48.json @@ -0,0 +1,1152 @@ +{ + "formatVersion": 1, + "database": { + "version": 48, + "identityHash": "bdc84541199b1207ea6f091aa0e125b6", + "entities": [ + { + "tableName": "app_registry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `ext` TEXT, `app_providers` TEXT NOT NULL, `name` TEXT, `icon` TEXT, `description` TEXT, `allow_creation` INTEGER, `default_application` TEXT, PRIMARY KEY(`account_name`, `mime_type`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ext", + "columnName": "ext", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders", + "columnName": "app_providers", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "allowCreation", + "columnName": "allow_creation", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "defaultApplication", + "columnName": "default_application", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "account_name", + "mime_type" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "folder_backup", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `behavior` TEXT NOT NULL, `sourcePath` TEXT NOT NULL, `uploadPath` TEXT NOT NULL, `wifiOnly` INTEGER NOT NULL, `chargingOnly` INTEGER NOT NULL, `name` TEXT NOT NULL, `lastSyncTimestamp` INTEGER NOT NULL, `spaceId` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "behavior", + "columnName": "behavior", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sourcePath", + "columnName": "sourcePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uploadPath", + "columnName": "uploadPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifiOnly", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargingOnly", + "columnName": "chargingOnly", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastSyncTimestamp", + "columnName": "lastSyncTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "spaceId", + "columnName": "spaceId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_major` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `sharing_user_profile_picture` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `files_private_links` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `app_providers_enabled` INTEGER, `app_providers_version` TEXT, `app_providers_appsUrl` TEXT, `app_providers_openUrl` TEXT, `app_providers_openWebUrl` TEXT, `app_providers_newUrl` TEXT, `spaces_enabled` INTEGER, `spaces_projects` INTEGER, `spaces_shareJail` INTEGER, `spaces_hasMultiplePersonalSpaces` INTEGER, `password_policy_maxCharacters` INTEGER, `password_policy_minCharacters` INTEGER, `password_policy_minDigits` INTEGER, `password_policy_minLowercaseCharacters` INTEGER, `password_policy_minSpecialCharacters` INTEGER, `password_policy_minUppercaseCharacters` INTEGER)", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_major", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEdition", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "corePollInterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "davChunkingVersion", + "columnName": "dav_chunking_version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filesSharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedReadOnly", + "columnName": "sharing_public_password_enforced_read_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedReadWrite", + "columnName": "sharing_public_password_enforced_read_write", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedUploadOnly", + "columnName": "sharing_public_password_enforced_public_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filesSharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicMultiple", + "columnName": "sharing_public_multiple", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicSupportsUploadOnly", + "columnName": "supports_upload_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingUserProfilePicture", + "columnName": "sharing_user_profile_picture", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesBigFileChunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesPrivateLinks", + "columnName": "files_private_links", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "appProviders.enabled", + "columnName": "app_providers_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "appProviders.version", + "columnName": "app_providers_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.appsUrl", + "columnName": "app_providers_appsUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.openUrl", + "columnName": "app_providers_openUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.openWebUrl", + "columnName": "app_providers_openWebUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.newUrl", + "columnName": "app_providers_newUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "spaces.enabled", + "columnName": "spaces_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spaces.projects", + "columnName": "spaces_projects", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spaces.shareJail", + "columnName": "spaces_shareJail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spaces.hasMultiplePersonalSpaces", + "columnName": "spaces_hasMultiplePersonalSpaces", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.maxCharacters", + "columnName": "password_policy_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minCharacters", + "columnName": "password_policy_minCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minDigits", + "columnName": "password_policy_minDigits", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minLowercaseCharacters", + "columnName": "password_policy_minLowercaseCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minSpecialCharacters", + "columnName": "password_policy_minSpecialCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minUppercaseCharacters", + "columnName": "password_policy_minUppercaseCharacters", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentId` INTEGER, `owner` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `remoteId` TEXT, `length` INTEGER NOT NULL, `creationTimestamp` INTEGER, `modificationTimestamp` INTEGER NOT NULL, `mimeType` TEXT NOT NULL, `etag` TEXT, `permissions` TEXT, `privateLink` TEXT, `storagePath` TEXT, `name` TEXT, `treeEtag` TEXT, `keepInSync` INTEGER, `lastSyncDateForData` INTEGER, `lastUsage` INTEGER, `fileShareViaLink` INTEGER, `needsToUpdateThumbnail` INTEGER NOT NULL, `modifiedAtLastSyncForData` INTEGER, `etagInConflict` TEXT, `fileIsDownloading` INTEGER, `sharedWithSharee` INTEGER, `sharedByLink` INTEGER NOT NULL, `spaceId` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`owner`, `spaceId`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "owner", + "columnName": "owner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remotePath", + "columnName": "remotePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "length", + "columnName": "length", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationTimestamp", + "columnName": "creationTimestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modificationTimestamp", + "columnName": "modificationTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mimeType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "privateLink", + "columnName": "privateLink", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "storagePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "treeEtag", + "columnName": "treeEtag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "availableOfflineStatus", + "columnName": "keepInSync", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "lastSyncDateForData", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastUsage", + "columnName": "lastUsage", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileShareViaLink", + "columnName": "fileShareViaLink", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "needsToUpdateThumbnail", + "columnName": "needsToUpdateThumbnail", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modifiedAtLastSyncForData", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etagInConflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsDownloading", + "columnName": "fileIsDownloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "sharedWithSharee", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedByLink", + "columnName": "sharedByLink", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "spaceId", + "columnName": "spaceId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "spaces", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "owner", + "spaceId" + ], + "referencedColumns": [ + "account_name", + "space_id" + ] + } + ] + }, + { + "tableName": "files_sync", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`fileId` INTEGER NOT NULL, `uploadWorkerUuid` BLOB, `downloadWorkerUuid` BLOB, `isSynchronizing` INTEGER NOT NULL, PRIMARY KEY(`fileId`), FOREIGN KEY(`fileId`) REFERENCES `files`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "fileId", + "columnName": "fileId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uploadWorkerUuid", + "columnName": "uploadWorkerUuid", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "downloadWorkerUuid", + "columnName": "downloadWorkerUuid", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "isSynchronizing", + "columnName": "isSynchronizing", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "fileId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "files", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "fileId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`share_type` INTEGER NOT NULL, `share_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `id_remote_shared` TEXT NOT NULL, `owner_share` TEXT NOT NULL, `name` TEXT, `url` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shareWith", + "columnName": "share_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithAdditionalInfo", + "columnName": "share_with_additional_info", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFolder", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "id_remote_shared", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transfers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localPath` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `accountName` TEXT NOT NULL, `fileSize` INTEGER NOT NULL, `status` INTEGER NOT NULL, `localBehaviour` INTEGER NOT NULL, `forceOverwrite` INTEGER NOT NULL, `transferEndTimestamp` INTEGER, `lastResult` INTEGER, `createdBy` INTEGER NOT NULL, `transferId` TEXT, `spaceId` TEXT, `sourcePath` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "localPath", + "columnName": "localPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remotePath", + "columnName": "remotePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileSize", + "columnName": "fileSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localBehaviour", + "columnName": "localBehaviour", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "forceOverwrite", + "columnName": "forceOverwrite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "transferEndTimestamp", + "columnName": "transferEndTimestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "lastResult", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "createdBy", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "transferId", + "columnName": "transferId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "spaceId", + "columnName": "spaceId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sourcePath", + "columnName": "sourcePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "spaces", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `drive_alias` TEXT, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT, `name` TEXT NOT NULL, `owner_id` TEXT, `web_url` TEXT, `description` TEXT, `quota_remaining` INTEGER, `quota_state` TEXT, `quota_total` INTEGER, `quota_used` INTEGER, `root_etag` TEXT, `root_id` TEXT NOT NULL, `root_web_dav_url` TEXT NOT NULL, `root_deleted_state` TEXT, `space_role` TEXT, PRIMARY KEY(`account_name`, `space_id`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "driveAlias", + "columnName": "drive_alias", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "driveType", + "columnName": "drive_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "space_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModifiedDateTime", + "columnName": "last_modified_date_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "webUrl", + "columnName": "web_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quota.remaining", + "columnName": "quota_remaining", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quota.state", + "columnName": "quota_state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quota.total", + "columnName": "quota_total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quota.used", + "columnName": "quota_used", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "root.eTag", + "columnName": "root_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "root.id", + "columnName": "root_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "root.webDavUrl", + "columnName": "root_web_dav_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "root.deleteState", + "columnName": "root_deleted_state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "root.spaceRole", + "columnName": "space_role", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "account_name", + "space_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "spaces_special", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_special_account_name` TEXT NOT NULL, `spaces_special_space_id` TEXT NOT NULL, `spaces_special_etag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `special_folder_name` TEXT NOT NULL, `special_web_dav_url` TEXT NOT NULL, PRIMARY KEY(`spaces_special_space_id`, `special_id`), FOREIGN KEY(`spaces_special_account_name`, `spaces_special_space_id`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "spaces_special_account_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "spaceId", + "columnName": "spaces_special_space_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eTag", + "columnName": "spaces_special_etag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileMimeType", + "columnName": "file_mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "special_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModifiedDateTime", + "columnName": "last_modified_date_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "specialFolderName", + "columnName": "special_folder_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "webDavUrl", + "columnName": "special_web_dav_url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "spaces_special_space_id", + "special_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "spaces", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "spaces_special_account_name", + "spaces_special_space_id" + ], + "referencedColumns": [ + "account_name", + "space_id" + ] + } + ] + }, + { + "tableName": "user_quotas", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, `total` INTEGER, `state` TEXT, PRIMARY KEY(`accountName`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "used", + "columnName": "used", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountName" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bdc84541199b1207ea6f091aa0e125b6')" + ] + } +} \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt index b67a8d8a0eb..db30f05b676 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt @@ -82,6 +82,7 @@ import com.owncloud.android.data.user.db.UserQuotaEntity AutoMigration(from = 44, to = 45), AutoMigration(from = 45, to = 46), AutoMigration(from = 46, to = 47), + AutoMigration(from = 47, to = 48), ], version = ProviderMeta.DB_VERSION, exportSchema = true diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java index 2870fd4605c..8788f7285d8 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java +++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java @@ -31,7 +31,7 @@ public class ProviderMeta { public static final String DB_NAME = "filelist"; public static final String NEW_DB_NAME = "owncloud_database"; - public static final int DB_VERSION = 47; + public static final int DB_VERSION = 48; private ProviderMeta() { } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt index eb7a2673104..ca313d274b7 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt @@ -23,7 +23,7 @@ package com.owncloud.android.data.spaces.datasources import com.owncloud.android.domain.spaces.model.OCSpace interface RemoteSpacesDataSource { - fun refreshSpacesForAccount(accountName: String): List + fun refreshSpacesForAccount(accountName: String, userId: String, userGroups: List): List fun createSpace(accountName: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long): OCSpace fun getSpacePermissions(accountName: String, spaceId: String): List fun editSpace(accountName: String, spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): OCSpace diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt index 53d61b636eb..e0c95237b06 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -162,6 +162,7 @@ class OCLocalSpacesDataSource( id = spaceRootEntity.id, webDavUrl = spaceRootEntity.webDavUrl, deleted = spaceRootEntity.deleteState?.let { SpaceDeleted(state = it) }, + role = spaceRootEntity.role ) }, webUrl = webUrl, @@ -183,7 +184,9 @@ class OCLocalSpacesDataSource( SpaceQuotaEntity(remaining = quotaModel.remaining, state = quotaModel.state, total = quotaModel.total, used = quotaModel.used) }, root = root.let { rootModel -> - SpaceRootEntity(eTag = rootModel.eTag, id = rootModel.id, webDavUrl = rootModel.webDavUrl, deleteState = rootModel.deleted?.state) + SpaceRootEntity(eTag = rootModel.eTag, id = rootModel.id, webDavUrl = rootModel.webDavUrl, deleteState = rootModel.deleted?.state, + role = rootModel.role + ) }, webUrl = webUrl, description = description, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt index 582a58e7273..fe1e7bb0c36 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -33,17 +33,18 @@ import com.owncloud.android.domain.spaces.model.SpaceRoot import com.owncloud.android.domain.spaces.model.SpaceSpecial import com.owncloud.android.domain.spaces.model.SpaceSpecialFolder import com.owncloud.android.domain.spaces.model.SpaceUser +import com.owncloud.android.lib.resources.spaces.responses.RootResponse import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse class OCRemoteSpacesDataSource( private val clientManager: ClientManager, ) : RemoteSpacesDataSource { - override fun refreshSpacesForAccount(accountName: String): List { + override fun refreshSpacesForAccount(accountName: String, userId: String, userGroups: List): List { val spacesResponse = executeRemoteOperation { clientManager.getSpacesService(accountName).getSpaces() } - return spacesResponse.map { it.toModel(accountName) } + return spacesResponse.map { it.toModel(accountName, userId, userGroups) } } override fun createSpace(accountName: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long): OCSpace { @@ -64,6 +65,9 @@ class OCRemoteSpacesDataSource( } companion object { + private const val MANAGER_ROLE = "manager" + private const val EDITOR_ROLE = "editor" + private const val VIEWER_ROLE = "viewer" @VisibleForTesting fun SpaceResponse.toModel(accountName: String): OCSpace = @@ -93,7 +97,8 @@ class OCRemoteSpacesDataSource( eTag = root.eTag, id = root.id, webDavUrl = root.webDavUrl, - deleted = root.deleted?.let { SpaceDeleted(state = it.state) } + deleted = root.deleted?.let { SpaceDeleted(state = it.state) }, + role = null ), webUrl = webUrl, description = description, @@ -110,5 +115,60 @@ class OCRemoteSpacesDataSource( ) } ) + + @VisibleForTesting + fun SpaceResponse.toModel(accountName: String, userId: String, userGroups: List): OCSpace = + OCSpace( + accountName = accountName, + driveAlias = driveAlias, + driveType = driveType, + id = id, + lastModifiedDateTime = lastModifiedDateTime, + name = name, + owner = owner?.let { ownerResponse -> + SpaceOwner( + user = SpaceUser( + id = ownerResponse.user.id + ) + ) + }, + quota = quota?.let { quotaResponse -> + SpaceQuota( + remaining = quotaResponse.remaining, + state = quotaResponse.state, + total = quotaResponse.total, + used = quotaResponse.used, + ) + }, + root = SpaceRoot( + eTag = root.eTag, + id = root.id, + webDavUrl = root.webDavUrl, + deleted = root.deleted?.let { SpaceDeleted(state = it.state) }, + role = getRoleForUser(root, userId, userGroups) + ), + webUrl = webUrl, + description = description, + special = special?.map { specialResponse -> + SpaceSpecial( + eTag = specialResponse.eTag, + file = SpaceFile(mimeType = specialResponse.file.mimeType), + id = specialResponse.id, + lastModifiedDateTime = specialResponse.lastModifiedDateTime, + name = specialResponse.name, + size = specialResponse.size, + specialFolder = SpaceSpecialFolder(name = specialResponse.specialFolder.name), + webDavUrl = specialResponse.webDavUrl + ) + } + ) + + private fun getRoleForUser(root: RootResponse, userId: String, userGroups: List): String? { + val priorityOrder = listOf(MANAGER_ROLE, EDITOR_ROLE, VIEWER_ROLE) + val matchingPermissions = root.permissions.orEmpty().filter { permission -> + permission.grantedToV2.user?.id == userId || permission.grantedToV2.group?.id in userGroups + } + return matchingPermissions.flatMap { it.roles }.minByOrNull { priorityOrder.indexOf(it) } + } } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt index df26552bc71..379bdbf4457 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt @@ -3,8 +3,9 @@ * * @author Abel García de Prada * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2024 ownCloud GmbH. + * Copyright (C) 2025 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -32,6 +33,7 @@ import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_R import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_STATE import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_TOTAL import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_USED +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROLE import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROOT_DELETED_STATE import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROOT_ETAG import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROOT_ID @@ -80,6 +82,7 @@ data class SpacesEntity( const val SPACES_ROOT_ID = "root_id" const val SPACES_ROOT_WEB_DAV_URL = "root_web_dav_url" const val SPACES_ROOT_DELETED_STATE = "root_deleted_state" + const val SPACES_ROLE = "space_role" } } @@ -103,6 +106,8 @@ data class SpaceRootEntity( val webDavUrl: String, @ColumnInfo(name = SPACES_ROOT_DELETED_STATE) val deleteState: String?, + @ColumnInfo(name = SPACES_ROLE) + val role: String? ) data class SpacesWithSpecials( diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index f8b5ebccde1..f352583c078 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -37,8 +37,8 @@ class OCSpacesRepository( private val remoteSpacesDataSource: RemoteSpacesDataSource, private val localCapabilitiesDataSource: LocalCapabilitiesDataSource, ) : SpacesRepository { - override fun refreshSpacesForAccount(accountName: String) { - remoteSpacesDataSource.refreshSpacesForAccount(accountName).also { listOfSpaces -> + override fun refreshSpacesForAccount(accountName: String, userId: String, userGroups: List) { + remoteSpacesDataSource.refreshSpacesForAccount(accountName, userId, userGroups).also { listOfSpaces -> localSpacesDataSource.saveSpacesForAccount(listOfSpaces) val personalSpace = listOfSpaces.find { it.isPersonal } val capabilities = localCapabilitiesDataSource.getCapabilitiesForAccount(accountName) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/RemoteUserDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/RemoteUserDataSource.kt index db114fd133b..5204e5fbb39 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/RemoteUserDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/RemoteUserDataSource.kt @@ -31,4 +31,5 @@ interface RemoteUserDataSource { fun getUserAvatar(accountName: String): UserAvatar fun getUserId(accountName: String): String fun getUserPermissions(accountName: String, accountId: String): List + fun getUserGroups(accountName: String): List } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSource.kt index a93ad000ddf..614ce424fbd 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSource.kt @@ -61,6 +61,11 @@ class OCRemoteUserDataSource( clientManager.getUserService(accountName).getUserPermissions(accountId) } + override fun getUserGroups(accountName: String): List = + executeRemoteOperation { + clientManager.getUserService(accountName).getUserGroups() + } + } /************************************************************************************************************** diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt index 289b2e55d72..b1dc5874924 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt @@ -66,4 +66,7 @@ class OCUserRepository( override fun getUserPermissions(accountName: String, accountId: String): List = remoteUserDataSource.getUserPermissions(accountName, accountId) + override fun getUserGroups(accountName: String): List = + remoteUserDataSource.getUserGroups(accountName) + } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt index 0bbd47f412d..ba1dd1c19df 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt @@ -27,6 +27,8 @@ import com.owncloud.android.data.spaces.datasources.implementation.OCRemoteSpace import com.owncloud.android.lib.resources.spaces.services.OCSpacesService import com.owncloud.android.testutil.OC_ACCOUNT_NAME import com.owncloud.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE +import com.owncloud.android.testutil.OC_USER_GROUPS +import com.owncloud.android.testutil.OC_USER_ID import com.owncloud.android.testutil.SPACE_PERMISSIONS import com.owncloud.android.testutil.SPACE_RESPONSE import com.owncloud.android.utils.createRemoteOperationResultMock @@ -58,7 +60,7 @@ class OCRemoteSpacesDataSourceTest { every { ocSpaceService.getSpaces() } returns getRemoteSpacesOperationResult - val resultActual = ocRemoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + val resultActual = ocRemoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) assertEquals(listOf(SPACE_RESPONSE.toModel(OC_ACCOUNT_NAME)), resultActual) diff --git a/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt index 96f48cbff60..dfcb50a15c4 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt @@ -31,6 +31,8 @@ import com.owncloud.android.testutil.OC_SPACE_PERSONAL import com.owncloud.android.testutil.OC_SPACE_PERSONAL_WITH_LIMITED_QUOTA import com.owncloud.android.testutil.OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA import com.owncloud.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE +import com.owncloud.android.testutil.OC_USER_GROUPS +import com.owncloud.android.testutil.OC_USER_ID import com.owncloud.android.testutil.OC_USER_QUOTA_LIMITED import com.owncloud.android.testutil.OC_USER_QUOTA_UNLIMITED import com.owncloud.android.testutil.OC_USER_QUOTA_WITHOUT_PERSONAL @@ -57,17 +59,17 @@ class OCSpacesRepositoryTest { @Test fun `refreshSpacesForAccount refreshes spaces for account correctly when multipersonal is enabled`() { every { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) } returns listOf(OC_SPACE_PERSONAL) every { localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns OC_CAPABILITY_WITH_MULTIPERSONAL_ENABLED - ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME) + ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) verify(exactly = 1) { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PERSONAL)) localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_WITHOUT_PERSONAL) @@ -77,17 +79,17 @@ class OCSpacesRepositoryTest { @Test fun `refreshSpacesForAccount refreshes spaces for account correctly when quota is unlimited`() { every { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) } returns listOf(OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA) every { localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns OC_CAPABILITY - ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME) + ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) verify(exactly = 1) { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA)) localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_UNLIMITED) @@ -97,17 +99,17 @@ class OCSpacesRepositoryTest { @Test fun `refreshSpacesForAccount refreshes spaces for account correctly when quota is limited`() { every { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) } returns listOf(OC_SPACE_PERSONAL_WITH_LIMITED_QUOTA) every { localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns OC_CAPABILITY - ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME) + ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) verify(exactly = 1) { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PERSONAL_WITH_LIMITED_QUOTA)) localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_LIMITED) @@ -117,17 +119,17 @@ class OCSpacesRepositoryTest { @Test fun `refreshSpacesForAccount refreshes spaces for account correctly when personal space does not exist`() { every { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) } returns listOf(OC_SPACE_PROJECT_WITH_IMAGE) every { localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns OC_CAPABILITY - ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME) + ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) verify(exactly = 1) { - remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME) + remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME, OC_USER_ID, OC_USER_GROUPS) localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PROJECT_WITH_IMAGE)) localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_WITHOUT_PERSONAL) diff --git a/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSourceTest.kt index 9f2d0c38f3d..c6cf90cd04c 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCRemoteUserDataSourceTest.kt @@ -29,6 +29,7 @@ import com.owncloud.android.lib.resources.users.RemoteUserInfo import com.owncloud.android.lib.resources.users.services.implementation.OCUserService import com.owncloud.android.testutil.OC_ACCOUNT_NAME import com.owncloud.android.testutil.OC_USER_AVATAR +import com.owncloud.android.testutil.OC_USER_GROUPS import com.owncloud.android.testutil.OC_USER_ID import com.owncloud.android.testutil.OC_USER_INFO import com.owncloud.android.testutil.OC_USER_PERMISSIONS @@ -161,5 +162,22 @@ class OCRemoteUserDataSourceTest { } } + @Test + fun `getUserGroups returns a list of String with all user groups`() { + val getUserGroupsResult: RemoteOperationResult> = + createRemoteOperationResultMock(data = OC_USER_GROUPS, isSuccess = true) + + every { + ocUserService.getUserGroups() + } returns getUserGroupsResult + + val userGroups = ocRemoteUserDataSource.getUserGroups(OC_ACCOUNT_NAME) + assertEquals(OC_USER_GROUPS, userGroups) + + verify(exactly = 1) { + ocUserService.getUserGroups() + } + } + } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/user/repository/OCUserRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/user/repository/OCUserRepositoryTest.kt index 434d4965f6b..26c1f6735a5 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/user/repository/OCUserRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/user/repository/OCUserRepositoryTest.kt @@ -26,6 +26,7 @@ import com.owncloud.android.data.user.datasources.LocalUserDataSource import com.owncloud.android.data.user.datasources.RemoteUserDataSource import com.owncloud.android.testutil.OC_ACCOUNT_NAME import com.owncloud.android.testutil.OC_USER_AVATAR +import com.owncloud.android.testutil.OC_USER_GROUPS import com.owncloud.android.testutil.OC_USER_ID import com.owncloud.android.testutil.OC_USER_INFO import com.owncloud.android.testutil.OC_USER_PERMISSIONS @@ -207,4 +208,18 @@ class OCUserRepositoryTest { } } + @Test + fun `getUserGroups returns a list of String with user groups`() { + every { + remoteUserDataSource.getUserGroups(OC_ACCOUNT_NAME) + } returns OC_USER_GROUPS + + val userGroups = ocUserRepository.getUserGroups(OC_ACCOUNT_NAME) + assertEquals(OC_USER_GROUPS, userGroups) + + verify(exactly = 1) { + remoteUserDataSource.getUserGroups(OC_ACCOUNT_NAME) + } + } + } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt index 5f5482b62e2..280cac87a8a 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt @@ -26,7 +26,7 @@ import com.owncloud.android.domain.spaces.model.OCSpace import kotlinx.coroutines.flow.Flow interface SpacesRepository { - fun refreshSpacesForAccount(accountName: String) + fun refreshSpacesForAccount(accountName: String, userId: String, userGroups: List) fun getSpacesFromEveryAccountAsStream(): Flow> fun getSpacesByDriveTypeWithSpecialsForAccountAsFlow(accountName: String, filterDriveTypes: Set): Flow> fun getPersonalSpaceForAccount(accountName: String): OCSpace? diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 8db903993b9..c96fde883ea 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -81,6 +81,7 @@ data class SpaceRoot( val id: String, val webDavUrl: String, val deleted: SpaceDeleted?, + val role: String? ) : Parcelable @Parcelize diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt index 87b666d4569..4fa0cd3157e 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt @@ -19,14 +19,29 @@ package com.owncloud.android.domain.spaces.usecases import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.UseCaseResult import com.owncloud.android.domain.spaces.SpacesRepository +import com.owncloud.android.domain.user.usecases.GetUserGroupsAsyncUseCase +import com.owncloud.android.domain.user.usecases.GetUserIdAsyncUseCase class RefreshSpacesFromServerAsyncUseCase( - private val spacesRepository: SpacesRepository + private val spacesRepository: SpacesRepository, + private val getUserIdAsyncUseCase: GetUserIdAsyncUseCase, + private val getUserGroupsAsyncUseCase: GetUserGroupsAsyncUseCase ) : BaseUseCaseWithResult() { - override fun run(params: Params) = - spacesRepository.refreshSpacesForAccount(accountName = params.accountName) + override fun run(params: Params) { + val userId = when (val userIdResult = getUserIdAsyncUseCase(GetUserIdAsyncUseCase.Params(params.accountName))) { + is UseCaseResult.Error -> "" + is UseCaseResult.Success -> userIdResult.data + } + val userGroups = when (val userGroupsResults = getUserGroupsAsyncUseCase(GetUserGroupsAsyncUseCase.Params(params.accountName))) { + is UseCaseResult.Error -> emptyList() + is UseCaseResult.Success -> userGroupsResults.data + } + spacesRepository.refreshSpacesForAccount(accountName = params.accountName, userId = userId, userGroups = userGroups) + + } data class Params( val accountName: String, diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt index 2291aff11e8..4bf8ae564d7 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt @@ -37,4 +37,5 @@ interface UserRepository { fun getUserAvatar(accountName: String): UserAvatar fun getUserId(accountName: String): String fun getUserPermissions(accountName: String, accountId: String): List + fun getUserGroups(accountName: String): List } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetUserGroupsAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetUserGroupsAsyncUseCase.kt new file mode 100644 index 00000000000..693f765b3b6 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetUserGroupsAsyncUseCase.kt @@ -0,0 +1,33 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * Copyright (C) 2025 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.domain.user.usecases + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.user.UserRepository + +class GetUserGroupsAsyncUseCase( + private val userRepository: UserRepository, +) : BaseUseCaseWithResult, GetUserGroupsAsyncUseCase.Params>() { + + override fun run(params: Params) = userRepository.getUserGroups(params.accountName) + + data class Params(val accountName: String) +} diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt index c8ac160dec5..97ca2e70b26 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt @@ -2,8 +2,9 @@ * ownCloud Android client application * * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2024 ownCloud GmbH. + * Copyright (C) 2025 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -35,9 +36,12 @@ import com.owncloud.android.domain.spaces.model.SpaceRoot import com.owncloud.android.domain.spaces.model.SpaceSpecial import com.owncloud.android.domain.spaces.model.SpaceSpecialFolder import com.owncloud.android.domain.spaces.model.SpaceUser +import com.owncloud.android.lib.resources.spaces.responses.GrantedToV2Response +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.owncloud.android.lib.resources.spaces.responses.QuotaResponse import com.owncloud.android.lib.resources.spaces.responses.RootResponse import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse +import com.owncloud.android.lib.resources.spaces.responses.UserResponse const val WEB_DAV_URL = "https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f3805bca744-d89f-4e9c-a990-25a0d7f03fe9" @@ -97,7 +101,8 @@ val OC_SPACE_PROJECT_WITH_IMAGE = OCSpace( eTag = "989c7968dbbbde8c5fd9849b9123c384", id = "8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", - deleted = null + deleted = null, + role = "manager" ), webUrl = "https://server.com/f/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", description = "This is the description of the space", @@ -114,7 +119,8 @@ val OC_SPACE_PROJECT_WITHOUT_IMAGE = OC_SPACE_PROJECT_WITH_IMAGE.copy( eTag = "989c7968dbbbde8c5fd9849b9123c384", id = "8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-1234566789", webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-1234566789", - deleted = null + deleted = null, + role = "manager" ), webUrl = "https://server.com/f/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-1234566789", special = listOf(OC_SPACE_SPECIAL_README) @@ -125,7 +131,15 @@ val OC_SPACE_PERSONAL = OC_SPACE_PROJECT_WITH_IMAGE.copy( driveType = "personal", name = "Admin", description = null, - special = null + special = null, + root = SpaceRoot( + eTag = "989c7968dbbbde8c5fd9849b9123c384", + id = "8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", + webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", + deleted = null, + role = null + ), + ) val OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA = OC_SPACE_PERSONAL.copy( @@ -159,7 +173,8 @@ val OC_SPACE_SHARES = OCSpace( eTag = "989c7968dbbbde8c5fd9849b9123c384", id = "a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc668", webDavUrl = "https://server.com/dav/spaces/a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc668", - deleted = null + deleted = null, + role = null ), webUrl = "https://server.com/f/a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc669", description = null, @@ -179,7 +194,8 @@ val OC_SPACE_PROJECT_DISABLED = OC_SPACE_PROJECT_WITH_IMAGE.copy( webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", deleted = SpaceDeleted( state = "trashed" - ) + ), + role = "manager" ), special = null ) @@ -198,7 +214,8 @@ val SPACE_ENTITY_WITH_SPECIALS = SpacesWithSpecials( eTag = "eTag", id = "id", webDavUrl = WEB_DAV_URL, - deleteState = "state" + deleteState = "state", + role = "manager" ), webUrl = "webUrl", description = "description" @@ -237,7 +254,8 @@ val SPACE_ENTITY_PERSONAL = SpacesEntity( eTag = "989c7968dbbbde8c5fd9849b9123c384", id = "8871f4f3-fc6f-4a66-8bed-62f175f76f38\$0aa0e03c-ec36-498c-bb9f-857315568199", webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", - deleteState = null + deleteState = null, + role = null ), webUrl = "https://server.com/f/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199", description = null @@ -258,7 +276,8 @@ val SPACE_ENTITY_SHARE = SpacesWithSpecials( eTag = "eTag", id = "id", webDavUrl = WEB_DAV_URL, - deleteState = "state" + deleteState = "state", + role = null ), webUrl = "webUrl", description = "description" @@ -292,7 +311,13 @@ val SPACE_RESPONSE = eTag = "eTag", id = OC_ACCOUNT_ID, webDavUrl = "https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f3805bca744-d89f-4e9c-a990-25a0d7f03fe9", - deleted = null + deleted = null, + permissions = listOf( + PermissionsResponse( + grantedToV2 = GrantedToV2Response(UserResponse(id = OC_CLIENT_ID), null), + roles = listOf("manager") + ) + ) ), quota = QuotaResponse( remaining = 1, diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCUser.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCUser.kt index 2c9ab66fda2..a69af99b780 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCUser.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCUser.kt @@ -74,3 +74,8 @@ val OC_USER_PERMISSIONS = listOf( ) const val OC_USER_ID = "us3r1d" + +val OC_USER_GROUPS = listOf( + "509a9dcd-bb37-4f4f-a01a-19dca27d9c", + "79c867e9-31f8-4795-b0d2-6a519eb0c5" +)