diff --git a/CHANGELOG.md b/CHANGELOG.md index bc9f21b9c5e..0d33d190649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ ownCloud admins and users. * Enhancement - Workflow to build APK: [#4751](https://github.com/owncloud/android/pull/4751) * Enhancement - List links over a space: [#4752](https://github.com/owncloud/android/issues/4752) * Enhancement - Add a public link over a space: [#4753](https://github.com/owncloud/android/issues/4753) +* Enhancement - Remove space link: [#4757](https://github.com/owncloud/android/issues/4757) * Enhancement - Copy permanent link of a space: [#4758](https://github.com/owncloud/android/issues/4758) * Enhancement - Workflow to check Conventional Commits: [#4759](https://github.com/owncloud/android/pull/4759) * Enhancement - QA Content Provider: [#4776](https://github.com/owncloud/android/pull/4776) @@ -161,10 +162,12 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4612 https://github.com/owncloud/android/issues/4763 https://github.com/owncloud/android/issues/4772 + https://github.com/owncloud/android/issues/4782 https://github.com/owncloud/android/pull/4728 https://github.com/owncloud/android/pull/4765 https://github.com/owncloud/android/pull/4779 https://github.com/owncloud/android/pull/4784 + https://github.com/owncloud/android/pull/4809 * Enhancement - Add a member to a space: [#4613](https://github.com/owncloud/android/issues/4613) @@ -227,6 +230,14 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4753 https://github.com/owncloud/android/pull/4794 +* Enhancement - Remove space link: [#4757](https://github.com/owncloud/android/issues/4757) + + A new option to remove space public links from a space has been added. It will + be only visible for users with proper permissions. + + https://github.com/owncloud/android/issues/4757 + https://github.com/owncloud/android/pull/4816 + * Enhancement - Copy permanent link of a space: [#4758](https://github.com/owncloud/android/issues/4758) A new option to copy and share the permanent link of a space has been added next diff --git a/changelog/unreleased/4816 b/changelog/unreleased/4816 new file mode 100644 index 00000000000..26b0a5c59c2 --- /dev/null +++ b/changelog/unreleased/4816 @@ -0,0 +1,6 @@ +Enhancement: Remove space link + +A new option to remove space public links from a space has been added. It will be only visible for users with proper permissions. + +https://github.com/owncloud/android/issues/4757 +https://github.com/owncloud/android/pull/4816 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 a771da5d52c..a86015d5fe6 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -80,6 +80,7 @@ import com.owncloud.android.domain.files.usecases.SortFilesUseCase import com.owncloud.android.domain.files.usecases.SortFilesWithSyncInfoUseCase import com.owncloud.android.domain.files.usecases.UpdateAlreadyDownloadedFilesPathUseCase import com.owncloud.android.domain.links.usecases.AddLinkUseCase +import com.owncloud.android.domain.links.usecases.RemoveLinkUseCase import com.owncloud.android.domain.members.usecases.AddMemberUseCase import com.owncloud.android.domain.members.usecases.EditMemberUseCase import com.owncloud.android.domain.members.usecases.RemoveMemberUseCase @@ -320,4 +321,5 @@ val useCaseModule = module { // Links factoryOf(::AddLinkUseCase) + factoryOf(::RemoveLinkUseCase) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksAdapter.kt index 76ead379cb2..9bf99387c66 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksAdapter.kt @@ -38,6 +38,7 @@ class SpaceLinksAdapter( ): RecyclerView.Adapter() { private var spaceLinks: List = emptyList() + private var canRemoveLinks = false override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SpaceLinksViewHolder { val inflater = LayoutInflater.from(parent.context) @@ -69,12 +70,21 @@ class SpaceLinksAdapter( listener.onCopyOrSendPublicLink(spaceLink.webUrl) } } + + removePublicLinkButton.apply { + contentDescription = holder.itemView.context.getString(R.string.content_description_delete_public_link, spaceLink.displayName) + isVisible = canRemoveLinks + setOnClickListener { + listener.onRemovePublicLink(spaceLink.id, spaceLink.displayName) + } + } } } override fun getItemCount(): Int = spaceLinks.size - fun setSpaceLinks(spaceLinks: List) { + fun setSpaceLinks(spaceLinks: List, canRemoveLinks: Boolean) { + this.canRemoveLinks = canRemoveLinks val diffCallback = SpaceLinksDiffUtil(this.spaceLinks, spaceLinks) val diffResult = DiffUtil.calculateDiff(diffCallback) this.spaceLinks = spaceLinks @@ -87,5 +97,6 @@ class SpaceLinksAdapter( interface SpaceLinksAdapterListener { fun onCopyOrSendPublicLink(publicLinkUrl: String) + fun onRemovePublicLink(publicLinkId: String, publicLinkDisplayName: String) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksViewModel.kt index dfd3b7ceeba..8fc26d98bda 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/links/SpaceLinksViewModel.kt @@ -23,17 +23,21 @@ package com.owncloud.android.presentation.spaces.links import androidx.lifecycle.ViewModel import com.owncloud.android.domain.links.model.OCLinkType import com.owncloud.android.domain.links.usecases.AddLinkUseCase +import com.owncloud.android.domain.links.usecases.RemoveLinkUseCase import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult import com.owncloud.android.presentation.common.UIResult import com.owncloud.android.providers.CoroutinesDispatcherProvider +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update class SpaceLinksViewModel( private val addLinkUseCase: AddLinkUseCase, + private val removeLinkUseCase: RemoveLinkUseCase, private val accountName: String, private val space: OCSpace, private val coroutineDispatcherProvider: CoroutinesDispatcherProvider @@ -45,6 +49,9 @@ class SpaceLinksViewModel( private val _addLinkResultFlow = MutableStateFlow>?>(null) val addLinkResultFlow: StateFlow>?> = _addLinkResultFlow + private val _removeLinkResultFlow = MutableSharedFlow>() + val removeLinkResultFlow: SharedFlow> = _removeLinkResultFlow + init { _addPublicLinkUIState.value = AddPublicLinkUIState() } @@ -79,6 +86,19 @@ class SpaceLinksViewModel( } } + fun removePublicLink(linkId: String) { + runUseCaseWithResult( + coroutineDispatcher = coroutineDispatcherProvider.io, + sharedFlow = _removeLinkResultFlow, + useCase = removeLinkUseCase, + useCaseParams = RemoveLinkUseCase.Params( + accountName = accountName, + spaceId = space.id, + linkId = linkId + ) + ) + } + fun resetViewModel() { _addLinkResultFlow.value = null _addPublicLinkUIState.value = AddPublicLinkUIState() diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt index 7c3cd1e0466..395e70f78c2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt @@ -22,6 +22,7 @@ package com.owncloud.android.presentation.spaces.members import android.app.AlertDialog import android.content.Context +import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -38,6 +39,7 @@ import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.model.SpaceMember import com.owncloud.android.extensions.avoidScreenshotsIfNeeded 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.presentation.common.UIResult @@ -77,7 +79,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter private var addMemberRoles: List = emptyList() private var spaceMembers: List = emptyList() private var listener: SpaceMemberFragmentListener? = null - private var canRemoveMembers = false + private var canRemoveMembersAndLinks = false private var canEditMembers = false private var canReadMembers = false private var numberOfManagers = 1 @@ -104,7 +106,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter currentSpace = requireArguments().getParcelable(ARG_CURRENT_SPACE) ?: return savedInstanceState?.let { - canRemoveMembers = it.getBoolean(CAN_REMOVE_MEMBERS, false) + canRemoveMembersAndLinks = it.getBoolean(CAN_REMOVE_MEMBERS, false) canEditMembers = it.getBoolean(CAN_EDIT_MEMBERS, false) canReadMembers = it.getBoolean(CAN_READ_MEMBERS, false) } @@ -150,7 +152,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putBoolean(CAN_REMOVE_MEMBERS, canRemoveMembers) + outState.putBoolean(CAN_REMOVE_MEMBERS, canRemoveMembersAndLinks) outState.putBoolean(CAN_EDIT_MEMBERS, canEditMembers) outState.putBoolean(CAN_READ_MEMBERS, canReadMembers) } @@ -180,6 +182,16 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter listener?.copyOrSendPublicLink(publicLinkUrl, currentSpace.name) } + override fun onRemovePublicLink(publicLinkId: String, publicLinkDisplayName: String) { + showAlertDialog( + title = getString(R.string.public_link_remove_dialog_title, publicLinkDisplayName), + message = getString(R.string.public_link_remove_dialog_message), + positiveButtonText = getString(R.string.common_yes), + positiveButtonListener = { _: DialogInterface?, _: Int -> spaceLinksViewModel.removePublicLink(publicLinkId) }, + negativeButtonText = getString(R.string.common_no) + ) + } + private fun subscribeToViewModels() { observeRoles() observeSpaceMembers() @@ -188,6 +200,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter observeRemoveMemberResult() observeEditMemberResult() observeAddLinkResult() + observeRemoveLinkResult() } private fun observeRoles() { @@ -221,7 +234,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter spaceMembers = it.members addMemberRoles = it.roles if (canReadMembers) { - spaceMembersAdapter.setSpaceMembers(spaceMembers, roles, canRemoveMembers, canEditMembers, numberOfManagers) + showSpaceMembers() val hasLinks = it.links.isNotEmpty() showOrHideEmptyView(hasLinks) if (hasLinks) { showSpaceLinks(it.links) } @@ -248,7 +261,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter uiResult.data?.let { spacePermissions -> checkPermissions(spacePermissions) if (canReadMembers) { - spaceMembersAdapter.setSpaceMembers(spaceMembers, roles, canRemoveMembers, canEditMembers, numberOfManagers) + showSpaceMembers() } } } @@ -322,9 +335,25 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter } } + private fun observeRemoveLinkResult() { + collectLatestLifecycleFlow(spaceLinksViewModel.removeLinkResultFlow) { uiResult -> + when (uiResult) { + is UIResult.Loading -> { } + is UIResult.Success -> { + showMessageInSnackbar(getString(R.string.public_link_remove_correctly)) + spaceMembersViewModel.getSpaceMembers() + } + is UIResult.Error -> { + Timber.e(uiResult.error, "Failed to remove a public link from space: ${currentSpace.id}") + showErrorInSnackbar(R.string.public_link_remove_failed, uiResult.error) + } + } + } + } + private fun checkPermissions(spacePermissions: List) { val hasCreatePermission = DRIVES_CREATE_PERMISSION in spacePermissions - canRemoveMembers = DRIVES_DELETE_PERMISSION in spacePermissions + canRemoveMembersAndLinks = DRIVES_DELETE_PERMISSION in spacePermissions canEditMembers = DRIVES_UPDATE_PERMISSION in spacePermissions canReadMembers = DRIVES_READ_PERMISSION in spacePermissions binding.apply { @@ -342,17 +371,30 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter } } + private fun showSpaceMembers() { + spaceMembersAdapter.setSpaceMembers( + spaceMembers = spaceMembers, + roles = roles, + canRemoveMembers = canRemoveMembersAndLinks, + canEditMembers = canEditMembers, + numberOfManagers = numberOfManagers + ) + } + private fun showSpaceLinks(spaceLinks: List) { val formatter = SimpleDateFormat(DisplayUtils.DATE_FORMAT_ISO, Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("UTC") } - spaceLinksAdapter.setSpaceLinks(spaceLinks.sortedByDescending { spaceLink -> - if (spaceLink.createdDateTime.isNotEmpty()) { - formatter.parse(spaceLink.createdDateTime) - } else { - Date(0) - } - }) + spaceLinksAdapter.setSpaceLinks( + spaceLinks = spaceLinks.sortedByDescending { spaceLink -> + if (spaceLink.createdDateTime.isNotEmpty()) { + formatter.parse(spaceLink.createdDateTime) + } else { + Date(0) + } + }, + canRemoveLinks = canRemoveMembersAndLinks + ) } interface SpaceMemberFragmentListener { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt index d30b7ab574b..281d35da300 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt @@ -109,6 +109,7 @@ class SpaceMembersViewModel( requiresConnection = true ) + // Used to fetch all members and public links of a space fun getSpaceMembers() = runUseCaseWithResult( coroutineDispatcher = coroutineDispatcherProvider.io, flow = _spaceMembers, diff --git a/owncloudApp/src/main/res/layout/public_link_item.xml b/owncloudApp/src/main/res/layout/public_link_item.xml index 2779f9d3a9e..06c4402c69c 100644 --- a/owncloudApp/src/main/res/layout/public_link_item.xml +++ b/owncloudApp/src/main/res/layout/public_link_item.xml @@ -101,7 +101,7 @@ android:layout_height="40dp" android:layout_gravity="center_vertical" android:layout_marginStart="@dimen/standard_half_margin" - android:layout_marginEnd="@dimen/standard_margin" + android:layout_marginEnd="@dimen/standard_half_margin" android:scaleType="centerCrop" android:padding="@dimen/standard_half_padding" android:background="?android:attr/selectableItemBackground" @@ -109,6 +109,19 @@ android:src="@drawable/copy_link" android:visibility="visible"/> + + Unnamed Link Public link created correctly Public link could not be created + Do you really want to remove the link: %1$s? + Recreating the same link again is not possible + Public link removed correctly + Public link could not be removed forum or contribute in our GitHub repo]]> diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/RemoveRemoteLinkOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/RemoveRemoteLinkOperation.kt new file mode 100644 index 00000000000..0e6052d395c --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/RemoveRemoteLinkOperation.kt @@ -0,0 +1,70 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * Copyright (C) 2026 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.links + +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 RemoveRemoteLinkOperation( + private val spaceId: String, + private val linkId: String +): RemoteOperation() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + try { + val uriBuilder = client.baseUri.buildUpon().apply { + appendEncodedPath(GRAPH_API_SPACES_PATH) + appendEncodedPath(spaceId) + appendEncodedPath(GRAPH_API_ROOT_PERMISSIONS_PATH) + appendEncodedPath(linkId) + } + + 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 removing a space public link; status code: $status, response: $response") + } + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Exception while removing a space public link") + } + return result + } + + companion object { + private const val GRAPH_API_SPACES_PATH = "graph/v1beta1/drives/" + private const val GRAPH_API_ROOT_PERMISSIONS_PATH = "root/permissions" + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/LinksService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/LinksService.kt index f88face3482..211cd11cb39 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/LinksService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/LinksService.kt @@ -25,4 +25,5 @@ import com.owncloud.android.lib.resources.Service interface LinksService: Service { fun addLink(spaceId: String, displayName: String, type: String, expirationDate: String?, password: String?): RemoteOperationResult + fun removeLink(spaceId: String, linkId: String): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/OCLinksService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/OCLinksService.kt index 15fdf5207a7..955ac2d6176 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/OCLinksService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/links/services/OCLinksService.kt @@ -23,6 +23,7 @@ package com.owncloud.android.lib.resources.links.services import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.links.AddRemoteLinkOperation +import com.owncloud.android.lib.resources.links.RemoveRemoteLinkOperation class OCLinksService(override val client: OwnCloudClient) : LinksService { @@ -40,4 +41,10 @@ class OCLinksService(override val client: OwnCloudClient) : LinksService { expirationDate = expirationDate, password = password ).execute(client) + + override fun removeLink(spaceId: String, linkId: String): RemoteOperationResult = + RemoveRemoteLinkOperation( + spaceId = spaceId, + linkId = linkId + ).execute(client) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/RemoteLinksDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/RemoteLinksDataSource.kt index 41924c3d827..64ca776647d 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/RemoteLinksDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/RemoteLinksDataSource.kt @@ -24,4 +24,5 @@ import com.owncloud.android.domain.links.model.OCLinkType interface RemoteLinksDataSource { fun addLink(accountName: String, spaceId: String, displayName: String, type: OCLinkType, expirationDate: String?, password: String?) + fun removeLink(accountName: String, spaceId: String, linkId: String) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSource.kt index c8122c097ad..ea6fc7a275e 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSource.kt @@ -34,4 +34,10 @@ class OCRemoteLinksDataSource( clientManager.getLinksService(accountName).addLink(spaceId, displayName, OCLinkType.toString(type), expirationDate, password) } } + + override fun removeLink(accountName: String, spaceId: String, linkId: String) { + executeRemoteOperation { + clientManager.getLinksService(accountName).removeLink(spaceId, linkId) + } + } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/links/repository/OCLinksRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/links/repository/OCLinksRepository.kt index f985e641ef4..20be0168676 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/links/repository/OCLinksRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/links/repository/OCLinksRepository.kt @@ -31,4 +31,8 @@ class OCLinksRepository( override fun addLink(accountName: String, spaceId: String, displayName: String, type: OCLinkType, expirationDate: String?, password: String?) { remoteLinksDataSource.addLink(accountName, spaceId, displayName, type, expirationDate, password) } + + override fun removeLink(accountName: String, spaceId: String, linkId: String) { + remoteLinksDataSource.removeLink(accountName, spaceId, linkId) + } } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSourceTest.kt index cdd0c024156..c1e88db4ff9 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/links/datasources/implementation/OCRemoteLinksDataSourceTest.kt @@ -82,4 +82,30 @@ class OCRemoteLinksDataSourceTest { ) } } + + @Test + fun `removeLink removes a public link from a project space correctly`() { + val removeLinkResult = createRemoteOperationResultMock(Unit, isSuccess = true) + + every { + ocLinksService.removeLink( + spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id, + linkId = SPACE_MEMBERS.links[0].id + ) + } returns removeLinkResult + + ocRemoteLinksDataSource.removeLink( + accountName = OC_ACCOUNT_NAME, + spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id, + linkId = SPACE_MEMBERS.links[0].id + ) + + verify(exactly = 1) { + clientManager.getLinksService(OC_ACCOUNT_NAME) + ocLinksService.removeLink( + spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id, + linkId = SPACE_MEMBERS.links[0].id + ) + } + } } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/links/repository/OCLinksRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/links/repository/OCLinksRepositoryTest.kt index 3406b9f2901..11ca72aac84 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/links/repository/OCLinksRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/links/repository/OCLinksRepositoryTest.kt @@ -69,4 +69,29 @@ class OCLinksRepositoryTest { ) } } + + @Test + fun `removeLink removes a public link from a space correctly`() { + every { + remoteLinksDataSource.removeLink( + accountName = OC_ACCOUNT_NAME, + spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id, + linkId = SPACE_MEMBERS.links[0].id + ) + } returns Unit + + ocLinksRepository.removeLink( + accountName = OC_ACCOUNT_NAME, + spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id, + linkId = SPACE_MEMBERS.links[0].id + ) + + verify(exactly = 1) { + remoteLinksDataSource.removeLink( + accountName = OC_ACCOUNT_NAME, + spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id, + linkId = SPACE_MEMBERS.links[0].id + ) + } + } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/links/LinksRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/links/LinksRepository.kt index 9bb8e5a4ad8..3ffc549ecc1 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/links/LinksRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/links/LinksRepository.kt @@ -24,4 +24,5 @@ import com.owncloud.android.domain.links.model.OCLinkType interface LinksRepository { fun addLink(accountName: String, spaceId: String, displayName: String, type: OCLinkType, expirationDate: String?, password: String?) + fun removeLink(accountName: String, spaceId: String, linkId: String) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/links/usecases/RemoveLinkUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/links/usecases/RemoveLinkUseCase.kt new file mode 100644 index 00000000000..1895502ad0b --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/links/usecases/RemoveLinkUseCase.kt @@ -0,0 +1,40 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * Copyright (C) 2026 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.links.usecases + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.links.LinksRepository + +class RemoveLinkUseCase( + private val linksRepository: LinksRepository +): BaseUseCaseWithResult() { + + override fun run(params: Params) { + linksRepository.removeLink(params.accountName, params.spaceId, params.linkId) + } + + data class Params( + val accountName: String, + val spaceId: String, + val linkId: String + ) + +}