Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUse
import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase
import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase
import com.owncloud.android.domain.spaces.usecases.CreateSpaceUseCase
import com.owncloud.android.domain.spaces.usecases.DisableSpaceUseCase
import com.owncloud.android.domain.spaces.usecases.EditSpaceUseCase
import com.owncloud.android.domain.spaces.usecases.FilterSpaceMenuOptionsUseCase
import com.owncloud.android.domain.spaces.usecases.GetPersonalAndProjectSpacesForAccountUseCase
Expand Down Expand Up @@ -225,6 +226,7 @@ val useCaseModule = module {

// Spaces
factoryOf(::CreateSpaceUseCase)
factoryOf(::DisableSpaceUseCase)
factoryOf(::EditSpaceUseCase)
factoryOf(::FilterSpaceMenuOptionsUseCase)
factoryOf(::GetPersonalAndProjectSpacesForAccountUseCase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,6 @@ val viewModelModule = module {
get()) }
viewModel { ReceiveExternalFilesViewModel(get(), get(), get(), get()) }
viewModel { (accountName: String, showPersonalSpace: Boolean) ->
SpacesListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), accountName, showPersonalSpace)
SpacesListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), accountName, showPersonalSpace)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import com.owncloud.android.domain.spaces.model.SpaceMenuOption
fun SpaceMenuOption.toStringResId() =
when (this) {
SpaceMenuOption.EDIT -> R.string.edit_space
SpaceMenuOption.DISABLE -> R.string.disable_space
}

fun SpaceMenuOption.toDrawableResId() =
when (this) {
SpaceMenuOption.EDIT -> R.drawable.ic_pencil
SpaceMenuOption.DISABLE -> R.drawable.ic_disable_space
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

package com.owncloud.android.presentation.spaces

import android.content.DialogInterface
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
Expand All @@ -44,7 +45,9 @@ import com.owncloud.android.databinding.SpacesListFragmentBinding
import com.owncloud.android.domain.files.model.FileListOption
import com.owncloud.android.domain.spaces.model.OCSpace
import com.owncloud.android.domain.spaces.model.SpaceMenuOption
import com.owncloud.android.domain.user.model.UserPermissions
import com.owncloud.android.extensions.collectLatestLifecycleFlow
import com.owncloud.android.extensions.showAlertDialog
import com.owncloud.android.extensions.showErrorInSnackbar
import com.owncloud.android.extensions.showMessageInSnackbar
import com.owncloud.android.extensions.toDrawableRes
Expand All @@ -71,7 +74,7 @@ class SpacesListFragment :
private val binding get() = _binding!!

private var isMultiPersonal = false
private var editSpacesPermission = false
private var userPermissions = mutableSetOf<UserPermissions>()
private var editQuotaPermission = false
private lateinit var currentSpace: OCSpace

Expand Down Expand Up @@ -181,8 +184,9 @@ class SpacesListFragment :
Timber.d("The permissions for $accountName are: ${uiResult.data}")
uiResult.data?.let {
binding.fabCreateSpace.isVisible = it.contains(DRIVES_CREATE_ALL_PERMISSION)
editSpacesPermission = it.contains(DRIVES_READ_WRITE_ALL_PERMISSION)
if(it.contains(DRIVES_READ_WRITE_ALL_PERMISSION)) userPermissions.add(UserPermissions.CAN_EDIT_SPACES)
editQuotaPermission = it.contains(DRIVES_READ_WRITE_PROJECT_QUOTA_ALL_PERMISSION)
if(it.contains(DRIVES_DELETE_PROJECT_ALL_PERMISSION)) userPermissions.add(UserPermissions.CAN_DELETE_SPACES)
}
}
is UIResult.Loading -> { }
Expand Down Expand Up @@ -214,6 +218,16 @@ class SpacesListFragment :
}
}

collectLatestLifecycleFlow(spacesListViewModel.disableSpaceFlow) { event ->
event?.let {
when (val uiResult = event.peekContent()) {
is UIResult.Success -> { showMessageInSnackbar(getString(R.string.disable_space_correctly)) }
is UIResult.Loading -> { }
is UIResult.Error -> { showErrorInSnackbar(R.string.disable_space_failed, uiResult.error) }
}
}
}

collectLatestLifecycleFlow(spacesListViewModel.menuOptions) { menuOptions ->
showSpaceMenuOptionsDialog(menuOptions)
}
Expand Down Expand Up @@ -243,7 +257,7 @@ class SpacesListFragment :

override fun onThreeDotButtonClick(ocSpace: OCSpace) {
currentSpace = ocSpace
spacesListViewModel.filterMenuOptions(ocSpace, editSpacesPermission)
spacesListViewModel.filterMenuOptions(ocSpace, userPermissions)
}

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
Expand Down Expand Up @@ -337,6 +351,15 @@ class SpacesListFragment :
)
editDialog.show(requireActivity().supportFragmentManager, DIALOG_CREATE_SPACE)
}
SpaceMenuOption.DISABLE -> {
showAlertDialog(
title = getString(R.string.disable_space_dialog_title, currentSpace.name),
message = getString(R.string.disable_space_dialog_message),
positiveButtonText = getString(R.string.common_yes),
positiveButtonListener = { _: DialogInterface?, _: Int -> spacesListViewModel.disableSpace(currentSpace.id) },
negativeButtonText = getString(R.string.common_no)
)
}
}
}
}
Expand All @@ -351,6 +374,7 @@ class SpacesListFragment :
const val DRIVES_CREATE_ALL_PERMISSION = "Drives.Create.all"
const val DRIVES_READ_WRITE_ALL_PERMISSION = "Drives.ReadWrite.all"
const val DRIVES_READ_WRITE_PROJECT_QUOTA_ALL_PERMISSION = "Drives.ReadWriteProjectQuota.all"
const val DRIVES_DELETE_PROJECT_ALL_PERMISSION = "Drives.DeleteProject.all"

private const val DIALOG_CREATE_SPACE = "DIALOG_CREATE_SPACE"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ import com.owncloud.android.domain.files.usecases.GetFileByRemotePathUseCase
import com.owncloud.android.domain.spaces.model.OCSpace
import com.owncloud.android.domain.spaces.model.SpaceMenuOption
import com.owncloud.android.domain.spaces.usecases.CreateSpaceUseCase
import com.owncloud.android.domain.spaces.usecases.DisableSpaceUseCase
import com.owncloud.android.domain.spaces.usecases.EditSpaceUseCase
import com.owncloud.android.domain.spaces.usecases.FilterSpaceMenuOptionsUseCase
import com.owncloud.android.domain.spaces.usecases.GetPersonalAndProjectSpacesWithSpecialsForAccountAsStreamUseCase
import com.owncloud.android.domain.spaces.usecases.GetPersonalSpacesWithSpecialsForAccountAsStreamUseCase
import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesWithSpecialsForAccountAsStreamUseCase
import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
import com.owncloud.android.domain.user.model.UserPermissions
import com.owncloud.android.domain.user.usecases.GetUserIdAsyncUseCase
import com.owncloud.android.domain.user.usecases.GetUserPermissionsAsyncUseCase
import com.owncloud.android.domain.utils.Event
Expand All @@ -62,6 +64,7 @@ class SpacesListViewModel(
private val createSpaceUseCase: CreateSpaceUseCase,
private val filterSpaceMenuOptionsUseCase: FilterSpaceMenuOptionsUseCase,
private val editSpaceUseCase: EditSpaceUseCase,
private val disableSpaceUseCase: DisableSpaceUseCase,
private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider,
private val accountName: String,
private val showPersonalSpace: Boolean,
Expand All @@ -86,6 +89,9 @@ class SpacesListViewModel(
private val _editSpaceFlow = MutableSharedFlow<Event<UIResult<Unit>>?>()
val editSpaceFlow: SharedFlow<Event<UIResult<Unit>>?> = _editSpaceFlow

private val _disableSpaceFlow = MutableSharedFlow<Event<UIResult<Unit>>?>()
val disableSpaceFlow: SharedFlow<Event<UIResult<Unit>>?> = _disableSpaceFlow

init {
viewModelScope.launch(coroutinesDispatcherProvider.io) {
refreshSpacesFromServer()
Expand Down Expand Up @@ -176,19 +182,29 @@ class SpacesListViewModel(
}
}

fun filterMenuOptions(space: OCSpace, editSpacesPermission: Boolean) {
fun filterMenuOptions(space: OCSpace, userPermissions: Set<UserPermissions>) {
viewModelScope.launch(coroutinesDispatcherProvider.io) {
val result = filterSpaceMenuOptionsUseCase(
FilterSpaceMenuOptionsUseCase.Params(
accountName = accountName,
space = space,
editSpacesPermission = editSpacesPermission
userPermissions = userPermissions
)
)
_menuOptions.emit(result)
}
}

fun disableSpace(spaceId: String){
viewModelScope.launch(coroutinesDispatcherProvider.io) {
when (val result = disableSpaceUseCase(DisableSpaceUseCase.Params(accountName, spaceId))) {
is UseCaseResult.Success -> _disableSpaceFlow.emit(Event(UIResult.Success(result.getDataOrNull())))
is UseCaseResult.Error -> _disableSpaceFlow.emit(Event(UIResult.Error(error = result.getThrowableOrNull())))
}
refreshSpacesFromServerAsyncUseCase(RefreshSpacesFromServerAsyncUseCase.Params(accountName))
}
}

data class SpacesListUiState(
val spaces: List<OCSpace>,
val rootFolderFromSelectedSpace: OCFile? = null,
Expand Down
10 changes: 10 additions & 0 deletions owncloudApp/src/main/res/drawable/ic_disable_space.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M320,640L640,640L640,320L320,320L320,640ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>
5 changes: 5 additions & 0 deletions owncloudApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,11 @@
<string name="edit_space">Edit space</string>
<string name="edit_space_correctly">Space updated correctly</string>
<string name="edit_space_failed">Space could not be updated</string>
<string name="disable_space">Disable space</string>
<string name="disable_space_dialog_title">Do you really want to disable the space: %1$s?</string>
<string name="disable_space_dialog_message">If you disable the selected space, it can no longer be accessed. Only Space managers will still have access. Note: No files will be deleted from the server.</string>
<string name="disable_space_correctly">Space disabled correctly</string>
<string name="disable_space_failed">Space could not be disabled</string>

<string name="feedback_dialog_get_in_contact_description"><![CDATA[ Ask for help in our <a href=\"%1$s\"><b>forum</b></a> or contribute in our <a href=\"%2$s\"><b>GitHub repo</b></a>]]></string>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/

package com.owncloud.android.lib.resources.spaces

import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod
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 timber.log.Timber
import java.net.URL

class DisableRemoteSpaceOperation(
private val spaceId: String
): RemoteOperation<Unit>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
var result: RemoteOperationResult<Unit>
try {
val uriBuilder = client.baseUri.buildUpon().apply {
appendEncodedPath(GRAPH_API_SPACES_PATH)
appendEncodedPath(spaceId)
}

val deleteMethod = DeleteMethod(URL(uriBuilder.build().toString()))

val status = client.executeHttpMethod(deleteMethod)

val response = deleteMethod.getResponseBodyAsString()

if (status == HttpConstants.HTTP_NO_CONTENT) {
Timber.d("Successful response: $response")
result = RemoteOperationResult(ResultCode.OK)
} else {
result = RemoteOperationResult(deleteMethod)
Timber.e("Failed response while disabling the space; status code: $status, response: $response")
}
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Exception while disabling the space $spaceId")
}
return result
}

companion object {
private const val GRAPH_API_SPACES_PATH = "graph/v1.0/drives/"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ package com.owncloud.android.lib.resources.spaces.services
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.spaces.CreateRemoteSpaceOperation
import com.owncloud.android.lib.resources.spaces.DisableRemoteSpaceOperation
import com.owncloud.android.lib.resources.spaces.EditRemoteSpaceOperation
import com.owncloud.android.lib.resources.spaces.GetRemoteSpacePermissionsOperation
import com.owncloud.android.lib.resources.spaces.GetRemoteSpacesOperation
Expand All @@ -43,4 +44,7 @@ class OCSpacesService(override val client: OwnCloudClient) : SpacesService {
override fun editSpace(spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): RemoteOperationResult<SpaceResponse> =
EditRemoteSpaceOperation(spaceId, spaceName, spaceSubtitle, spaceQuota).execute(client)

override fun disableSpace(spaceId: String): RemoteOperationResult<Unit> =
DisableRemoteSpaceOperation(spaceId).execute(client)

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ interface SpacesService : Service {
fun createSpace(spaceName: String, spaceSubtitle: String, spaceQuota: Long): RemoteOperationResult<SpaceResponse>
fun getSpacePermissions(spaceId: String): RemoteOperationResult<List<String>>
fun editSpace(spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): RemoteOperationResult<SpaceResponse>
fun disableSpace(spaceId: String): RemoteOperationResult<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ interface RemoteSpacesDataSource {
fun createSpace(accountName: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long): OCSpace
fun getSpacePermissions(accountName: String, spaceId: String): List<String>
fun editSpace(accountName: String, spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): OCSpace
fun disableSpace(accountName: String, spaceId: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class OCRemoteSpacesDataSource(
return spaceResponse.toModel(accountName)
}

override fun disableSpace(accountName: String, spaceId: String) {
executeRemoteOperation { clientManager.getSpacesService(accountName).disableSpace(spaceId) }
}

companion object {

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,8 @@ class OCSpacesRepository(
remoteSpacesDataSource.editSpace(accountName, spaceId, spaceName, spaceSubtitle, spaceQuota)
}

override fun disableSpace(accountName: String, spaceId: String) {
remoteSpacesDataSource.disableSpace(accountName, spaceId)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ interface SpacesRepository {
fun getWebDavUrlForSpace(accountName: String, spaceId: String?): String?
fun createSpace(accountName: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long)
fun editSpace(accountName: String, spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?)
fun disableSpace(accountName: String, spaceId: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
package com.owncloud.android.domain.spaces.model

enum class SpaceMenuOption {
EDIT
EDIT, DISABLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/

package com.owncloud.android.domain.spaces.usecases

import com.owncloud.android.domain.BaseUseCaseWithResult
import com.owncloud.android.domain.spaces.SpacesRepository

class DisableSpaceUseCase(
private val spacesRepository: SpacesRepository
): BaseUseCaseWithResult<Unit, DisableSpaceUseCase.Params>() {

override fun run(params: Params) = spacesRepository.disableSpace(params.accountName, params.spaceId)

data class Params (
val accountName: String,
val spaceId: String
)

}
Loading
Loading