diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt index 79d04d505b..4155d2e2ea 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt @@ -99,6 +99,8 @@ class WebdavEntry constructor( var livePhoto: String? = null private set var fileDownloadLimit = emptyList() + var shareTypes = emptyList() + var shareAttributes: String = "" private val gson = Gson() @@ -479,6 +481,33 @@ class WebdavEntry constructor( } parseLockProperties(ncNamespace, propSet) + + // OC share-types property + prop = propSet[EXTENDED_PROPERTY_SHARE_TYPES, ocNamespace] + if (prop != null && prop.value != null) { + shareTypes = + when (prop.value) { + is ArrayList<*> -> { + (prop.value as ArrayList<*>) + .filterIsInstance() + .mapNotNull { it.firstChild?.nodeValue?.toIntOrNull() } + } + + is Element -> { + listOfNotNull((prop.value as Element).firstChild?.nodeValue?.toIntOrNull()) + } + + else -> { + emptyList() + } + } + } + + // NC share-attributes property + prop = propSet[EXTENDED_PROPERTY_SHARE_ATTRIBUTES, ncNamespace] + if (prop != null && prop.value != null) { + shareAttributes = prop.value.toString() + } } else { Log_OC.e("WebdavEntry", "General error, no status for webdav response") } @@ -671,6 +700,8 @@ class WebdavEntry constructor( const val EXTENDED_PROPERTY_METADATA_LIVE_PHOTO = "metadata-files-live-photo" const val EXTENDED_PROPERTY_FILE_DOWNLOAD_LIMITS = "share-download-limits" + const val EXTENDED_PROPERTY_SHARE_TYPES = "share-types" + const val EXTENDED_PROPERTY_SHARE_ATTRIBUTES = "share-attributes" const val EXTENDED_PROPERTY_METADATA_PHOTOS_SIZE = "metadata-photos-size" const val EXTENDED_PROPERTY_METADATA_PHOTOS_GPS = "metadata-photos-gps" diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java b/library/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java index 0e73c5c7b9..8993c49a48 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java @@ -27,6 +27,34 @@ import javax.xml.parsers.ParserConfigurationException; import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_IS_ENCRYPTED; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_NAME_LOCAL_ID; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_MOUNT_TYPE; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_OWNER_ID; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_OWNER_DISPLAY_NAME; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_UNREAD_COMMENTS; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_NOTE; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_SHAREES; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_SHARE_TYPES; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_SHARE_ATTRIBUTES; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_HIDDEN; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_RICH_WORKSPACE; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_SYSTEM_TAGS; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_CREATION_TIME; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_UPLOAD_TIME; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_METADATA_LIVE_PHOTO; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_FILE_DOWNLOAD_LIMITS; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_METADATA_PHOTOS_SIZE; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_METADATA_PHOTOS_GPS; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_METADATA_SIZE; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_METADATA_GPS; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_OWNER_TYPE; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_OWNER; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_OWNER_DISPLAY_NAME; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_OWNER_EDITOR; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_TIME; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_TIMEOUT; +import static com.owncloud.android.lib.common.network.WebdavEntry.EXTENDED_PROPERTY_LOCK_TOKEN; import static com.owncloud.android.lib.common.network.WebdavEntry.NAMESPACE_NC; import static com.owncloud.android.lib.common.network.WebdavEntry.NAMESPACE_OC; @@ -100,11 +128,41 @@ private Document createQuery(String searchQuery) { Element quotaAvailableElement = query.createElementNS(DAV_NAMESPACE, "d:quota-available-bytes"); Element permissionsElement = query.createElementNS(NAMESPACE_OC, "oc:permissions"); Element remoteIdElement = query.createElementNS(NAMESPACE_OC, "oc:id"); + Element localIdElement = query.createElementNS(NAMESPACE_OC, "oc:" + EXTENDED_PROPERTY_NAME_LOCAL_ID); Element sizeElement = query.createElementNS(NAMESPACE_OC, "oc:size"); Element favoriteElement = query.createElementNS(NAMESPACE_OC, "oc:favorite"); Element previewElement = query.createElementNS(NAMESPACE_OC, "nc:has-preview"); Element encryptedElement = query.createElementNS(NAMESPACE_NC, EXTENDED_PROPERTY_IS_ENCRYPTED); + // Additional properties parsed by WebdavEntry + Element ownerIdElement = query.createElementNS(NAMESPACE_OC, "oc:" + EXTENDED_PROPERTY_OWNER_ID); + Element ownerDisplayNameElement = query.createElementNS(NAMESPACE_OC, "oc:" + EXTENDED_PROPERTY_OWNER_DISPLAY_NAME); + Element unreadCommentsElement = query.createElementNS(NAMESPACE_OC, "oc:" + EXTENDED_PROPERTY_UNREAD_COMMENTS); + Element shareTypesElement = query.createElementNS(NAMESPACE_OC, "oc:" + EXTENDED_PROPERTY_SHARE_TYPES); + Element mountTypeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_MOUNT_TYPE); + Element noteElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_NOTE); + Element shareesElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_SHAREES); + Element shareAttributesElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_SHARE_ATTRIBUTES); + Element hiddenElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_HIDDEN); + Element richWorkspaceElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_RICH_WORKSPACE); + Element systemTagsElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_SYSTEM_TAGS); + Element creationTimeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_CREATION_TIME); + Element uploadTimeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_UPLOAD_TIME); + Element livePhotoElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_METADATA_LIVE_PHOTO); + Element downloadLimitsElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_FILE_DOWNLOAD_LIMITS); + Element metaPhotosSizeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_METADATA_PHOTOS_SIZE); + Element metaPhotosGpsElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_METADATA_PHOTOS_GPS); + Element metaSizeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_METADATA_SIZE); + Element metaGpsElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_METADATA_GPS); + Element lockElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK); + Element lockOwnerTypeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_OWNER_TYPE); + Element lockOwnerElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_OWNER); + Element lockOwnerDisplayNameElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_OWNER_DISPLAY_NAME); + Element lockOwnerEditorElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_OWNER_EDITOR); + Element lockTimeElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_TIME); + Element lockTimeoutElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_TIMEOUT); + Element lockTokenElement = query.createElementNS(NAMESPACE_NC, "nc:" + EXTENDED_PROPERTY_LOCK_TOKEN); + if (searchType != SearchRemoteOperation.SearchType.GALLERY_SEARCH) { selectPropsElement.appendChild(displayNameElement); selectPropsElement.appendChild(creationDate); @@ -122,9 +180,42 @@ private Document createQuery(String searchQuery) { selectPropsElement.appendChild(lastModifiedElement); selectPropsElement.appendChild(etagElement); selectPropsElement.appendChild(remoteIdElement); + selectPropsElement.appendChild(localIdElement); selectPropsElement.appendChild(favoriteElement); selectPropsElement.appendChild(permissionsElement); + // Common additional properties for all search types + selectPropsElement.appendChild(ownerIdElement); + selectPropsElement.appendChild(ownerDisplayNameElement); + selectPropsElement.appendChild(unreadCommentsElement); + selectPropsElement.appendChild(shareTypesElement); + selectPropsElement.appendChild(mountTypeElement); + selectPropsElement.appendChild(noteElement); + selectPropsElement.appendChild(shareesElement); + selectPropsElement.appendChild(shareAttributesElement); + selectPropsElement.appendChild(hiddenElement); + selectPropsElement.appendChild(richWorkspaceElement); + selectPropsElement.appendChild(systemTagsElement); + selectPropsElement.appendChild(creationTimeElement); + selectPropsElement.appendChild(uploadTimeElement); + selectPropsElement.appendChild(livePhotoElement); + selectPropsElement.appendChild(downloadLimitsElement); + selectPropsElement.appendChild(metaPhotosSizeElement); + selectPropsElement.appendChild(metaPhotosGpsElement); + selectPropsElement.appendChild(metaSizeElement); + selectPropsElement.appendChild(metaGpsElement); + selectPropsElement.appendChild(lockElement); + selectPropsElement.appendChild(lockOwnerTypeElement); + selectPropsElement.appendChild(lockOwnerElement); + selectPropsElement.appendChild(lockOwnerDisplayNameElement); + selectPropsElement.appendChild(lockOwnerEditorElement); + selectPropsElement.appendChild(lockTimeElement); + selectPropsElement.appendChild(lockTimeoutElement); + selectPropsElement.appendChild(lockTokenElement); + if (searchType != SearchRemoteOperation.SearchType.GALLERY_SEARCH) { + selectPropsElement.appendChild(previewElement); + } + Element fromElement = query.createElementNS(DAV_NAMESPACE, "d:from"); Element scopeElement = query.createElementNS(DAV_NAMESPACE, "d:scope"); Element hrefElement = query.createElementNS(DAV_NAMESPACE, "d:href"); diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java deleted file mode 100644 index 531947db72..0000000000 --- a/library/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Nextcloud Android Library - * - * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: MIT - */ -package com.owncloud.android.lib.resources.files; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.WebDavFileUtils; -import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.status.OCCapability; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.client.methods.OptionsMethod; -import org.apache.jackrabbit.webdav.search.SearchInfo; -import org.apache.jackrabbit.webdav.xml.Namespace; - -import java.util.ArrayList; -import java.util.List; - -/** - * Remote operation performing the search in the Nextcloud server. - */ -public class SearchRemoteOperation extends RemoteOperation> { - - - public enum SearchType { - FILE_SEARCH, // search by name - FAVORITE_SEARCH, // get all favorite files/folder - RECENTLY_MODIFIED_SEARCH, // get files/folders that were modified within last 7 days, ordered descending by time - PHOTO_SEARCH, // gets all files with mimetype "image/%" - /** - * @deprecated unused, to be removed in a future version - */ - @Deprecated - SHARED_SEARCH, // show all shares - GALLERY_SEARCH, // combined photo and video - FILE_ID_SEARCH, // search one file specified by file id - CONTENT_TYPE_SEARCH, - RECENTLY_ADDED_SEARCH, - SHARED_FILTER - } - - private final String searchQuery; - private final SearchType searchType; - private final boolean filterOutFiles; - private int limit; - private long timestamp = -1; - private final OCCapability capability; - private Long startDate = null; - private Long endDate = null; - - public SearchRemoteOperation(String query, - SearchType searchType, - boolean filterOutFiles, - final OCCapability capability) { - this.searchQuery = query; - this.searchType = searchType; - this.filterOutFiles = filterOutFiles; - this.capability = capability; - } - - public void setLimit(int limit) { - this.limit = limit; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - public void setStartDate(Long startDate) { - this.startDate = startDate; - } - - public void setEndDate(Long endDate) { - this.endDate = endDate; - } - - @Override - protected RemoteOperationResult> run(OwnCloudClient client) { - RemoteOperationResult> result; - NcSearchMethod searchMethod = null; - OptionsMethod optionsMethod; - - String webDavUrl = client.getDavUri().toString(); - optionsMethod = new OptionsMethod(webDavUrl); - - try { - int optionsStatus = client.executeMethod(optionsMethod); - boolean isSearchSupported = optionsMethod.isAllowed("SEARCH"); - - if (isSearchSupported) { - searchMethod = new NcSearchMethod(webDavUrl, - new SearchInfo("NC", - Namespace.XMLNS_NAMESPACE, - searchQuery), - searchType, - getClient().getUserIdPlain(), - timestamp, - limit, - filterOutFiles, - capability, - startDate, - endDate); - - int status = client.executeMethod(searchMethod); - - // check and process response - boolean isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK); - - if (isSuccess) { - // get data from remote folder - MultiStatus dataInServer = searchMethod.getResponseBodyAsMultiStatus(); - WebDavFileUtils webDavFileUtils = new WebDavFileUtils(); - ArrayList mFolderAndFiles = webDavFileUtils.readData(dataInServer, - client, - false, - true); - - // Result of the operation - result = new RemoteOperationResult<>(true, status, searchMethod.getResponseHeaders()); - // Add data to the result - if (result.isSuccess()) { - result.setResultData(mFolderAndFiles); - } - } else { - // synchronization failed - client.exhaustResponse(searchMethod.getResponseBodyAsStream()); - result = new RemoteOperationResult<>(false, status, searchMethod.getResponseHeaders()); - } - } else { - client.exhaustResponse(optionsMethod.getResponseBodyAsStream()); - result = new RemoteOperationResult<>(false, optionsStatus, optionsMethod.getResponseHeaders()); - } - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - } finally { - if (searchMethod != null) { - searchMethod.releaseConnection(); // let the connection available for other methods - } - - optionsMethod.releaseConnection(); - } - return result; - } -} - diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.kt new file mode 100644 index 0000000000..eee3517f39 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.kt @@ -0,0 +1,103 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2017-2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-FileCopyrightText: 2017 Mario Danic + * SPDX-License-Identifier: MIT + */ +package com.owncloud.android.lib.resources.files + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.WebDavFileUtils +import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.lib.resources.status.OCCapability +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.OptionsMethod +import org.apache.jackrabbit.webdav.search.SearchInfo +import org.apache.jackrabbit.webdav.xml.Namespace + +class SearchRemoteOperation( + private val searchQuery: String?, + private val searchType: SearchType?, + private val filterOutFiles: Boolean, + private val capability: OCCapability? +) : RemoteOperation?>() { + enum class SearchType { + FILE_SEARCH, + FAVORITE_SEARCH, + RECENTLY_MODIFIED_SEARCH, + PHOTO_SEARCH, + + @Deprecated("unused, to be removed in a future version") + SHARED_SEARCH, + GALLERY_SEARCH, + FILE_ID_SEARCH, + CONTENT_TYPE_SEARCH, + RECENTLY_ADDED_SEARCH, + SHARED_FILTER + } + + var limit: Int = 0 + var timestamp: Long = -1 + var startDate: Long? = null + var endDate: Long? = null + + @Deprecated("Deprecated in Java") + @Suppress("Detekt.TooGenericExceptionCaught", "DEPRECATION") + override fun run(client: OwnCloudClient): RemoteOperationResult?> { + val webDavUrl = client.davUri.toString() + val optionsMethod = OptionsMethod(webDavUrl) + + return try { + val optionsStatus = client.executeMethod(optionsMethod) + + if (!optionsMethod.isAllowed("SEARCH")) { + client.exhaustResponse(optionsMethod.getResponseBodyAsStream()) + return RemoteOperationResult(false, optionsStatus, optionsMethod.responseHeaders) + } + + val searchMethod = + NcSearchMethod( + webDavUrl, + SearchInfo("NC", Namespace.XMLNS_NAMESPACE, searchQuery), + searchType, + client.userIdPlain, + timestamp, + limit, + filterOutFiles, + capability, + startDate, + endDate + ) + + try { + val status = client.executeMethod(searchMethod) + val isSuccess = status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK + + if (isSuccess) { + val files = + WebDavFileUtils().readData( + searchMethod.getResponseBodyAsMultiStatus(), + client, + false, + true + ) + RemoteOperationResult?>(true, status, searchMethod.responseHeaders) + .also { if (it.isSuccess) it.resultData = files } + } else { + client.exhaustResponse(searchMethod.getResponseBodyAsStream()) + RemoteOperationResult(false, status, searchMethod.responseHeaders) + } + } finally { + searchMethod.releaseConnection() + } + } catch (e: Exception) { + RemoteOperationResult(e) + } finally { + optionsMethod.releaseConnection() + } + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt index 4c9c567612..45ee0578c7 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt @@ -60,6 +60,8 @@ class RemoteFile : var hidden = false var livePhoto: String? = null var fileDownloadLimit: List = emptyList() + var shareTypes: List = emptyList() + var shareAttributes: String = "" constructor() { resetData() @@ -116,6 +118,8 @@ class RemoteFile : livePhoto = we.livePhoto hidden = we.hidden fileDownloadLimit = we.fileDownloadLimit + shareTypes = we.shareTypes + shareAttributes = we.shareAttributes } /**