Skip to content

Commit 640ece7

Browse files
committed
Album remote operations.
Signed-off-by: A117870935 <[email protected]>
1 parent 8e561c7 commit 640ece7

12 files changed

+862
-0
lines changed

library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud Android Library
33
*
4+
* SPDX-FileCopyrightText: 2025 TSI-mc <[email protected]>
45
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
56
* SPDX-FileCopyrightText: 2023 Alper Ozturk <[email protected]>
67
* SPDX-FileCopyrightText: 2023 Álvaro Brey <[email protected]>
@@ -692,6 +693,11 @@ class WebdavEntry constructor(
692693
const val SHAREES_SHARE_TYPE = "type"
693694
const val PROPERTY_QUOTA_USED_BYTES = "quota-used-bytes"
694695
const val PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes"
696+
const val PROPERTY_LAST_PHOTO = "last-photo"
697+
const val PROPERTY_NB_ITEMS = "nbItems"
698+
const val PROPERTY_LOCATION = "location"
699+
const val PROPERTY_DATE_RANGE = "dateRange"
700+
const val PROPERTY_COLLABORATORS = "collaborators"
695701
private const val IS_ENCRYPTED = "1"
696702
private const val CODE_PROP_NOT_FOUND = 404
697703
}

library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud Android Library
33
*
4+
* SPDX-FileCopyrightText: 2025 TSI-mc <[email protected]>
45
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
56
* SPDX-FileCopyrightText: 2023 Alper Ozturk <[email protected]>
67
* SPDX-FileCopyrightText: 2022 Álvaro Brey <[email protected]>
@@ -221,6 +222,20 @@ public static DavPropertyNameSet getChunksPropSet() {
221222
return propSet;
222223
}
223224

225+
public static DavPropertyNameSet getAlbumPropSet() {
226+
DavPropertyNameSet propertySet = new DavPropertyNameSet();
227+
Namespace ncNamespace = Namespace.getNamespace("nc",WebdavEntry.NAMESPACE_NC);
228+
229+
propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LAST_PHOTO,ncNamespace));
230+
propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_NB_ITEMS, ncNamespace));
231+
propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LOCATION, ncNamespace));
232+
propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_DATE_RANGE, ncNamespace));
233+
propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_COLLABORATORS, ncNamespace));
234+
235+
return propertySet;
236+
}
237+
238+
224239
/**
225240
*
226241
* @param rawEtag

library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
/*
22
* Nextcloud Android Library
33
*
4+
* SPDX-FileCopyrightText: 2025 TSI-mc <[email protected]>
45
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
56
* SPDX-FileCopyrightText: 2017 Mario Danic <[email protected]>
67
* SPDX-License-Identifier: MIT
78
*/
89
package com.owncloud.android.lib.common.utils;
910

11+
import android.net.Uri;
12+
1013
import com.owncloud.android.lib.common.OwnCloudClient;
1114
import com.owncloud.android.lib.common.network.WebdavEntry;
1215
import com.owncloud.android.lib.resources.files.model.RemoteFile;
@@ -59,4 +62,29 @@ public ArrayList<RemoteFile> readData(MultiStatus remoteData,
5962

6063
return mFolderAndFiles;
6164
}
65+
66+
/**
67+
* Read the data retrieved from the server about the contents of the target folder
68+
*
69+
* @param remoteData Full response got from the server with the data of the target
70+
* folder and its direct children.
71+
* @param client Client instance to the remote server where the data were
72+
* retrieved.
73+
* @return
74+
*/
75+
public ArrayList<RemoteFile> readAlbumData(MultiStatus remoteData, OwnCloudClient client) {
76+
String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId();
77+
78+
ArrayList<RemoteFile> mFolderAndFiles = new ArrayList<>();
79+
80+
// loop to update every child
81+
// reading from 1 as 0th item will be just the root album path
82+
for (int i = 1; i < remoteData.getResponses().length; ++i) {
83+
/// new OCFile instance with the data from the server
84+
WebdavEntry we = new WebdavEntry(remoteData.getResponses()[i], Uri.parse(url).getEncodedPath());
85+
RemoteFile remoteFile = new RemoteFile(we);
86+
mFolderAndFiles.add(remoteFile);
87+
}
88+
return mFolderAndFiles;
89+
}
6290
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2025 TSI-mc <[email protected]>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.lib.resources.albums;
9+
10+
import android.util.Log;
11+
12+
import com.nextcloud.common.SessionTimeOut;
13+
import com.nextcloud.common.SessionTimeOutKt;
14+
import com.owncloud.android.lib.common.OwnCloudClient;
15+
import com.owncloud.android.lib.common.network.WebdavUtils;
16+
import com.owncloud.android.lib.common.operations.RemoteOperation;
17+
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
18+
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
19+
20+
import org.apache.commons.httpclient.HttpStatus;
21+
import org.apache.jackrabbit.webdav.DavException;
22+
import org.apache.jackrabbit.webdav.MultiStatusResponse;
23+
import org.apache.jackrabbit.webdav.Status;
24+
import org.apache.jackrabbit.webdav.client.methods.CopyMethod;
25+
26+
import java.io.IOException;
27+
28+
29+
/**
30+
* Remote operation moving a remote file or folder in the ownCloud server to a different folder
31+
* in the same account.
32+
* <p>
33+
* Allows renaming the moving file/folder at the same time.
34+
*/
35+
public class CopyFileToAlbumRemoteOperation extends RemoteOperation {
36+
private static final String TAG = CopyFileToAlbumRemoteOperation.class.getSimpleName();
37+
38+
private final String mSrcRemotePath;
39+
private final String mTargetRemotePath;
40+
41+
private final SessionTimeOut sessionTimeOut;
42+
43+
public CopyFileToAlbumRemoteOperation(String srcRemotePath, String targetRemotePath) {
44+
this(srcRemotePath, targetRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut());
45+
}
46+
47+
public CopyFileToAlbumRemoteOperation(String srcRemotePath, String targetRemotePath, SessionTimeOut sessionTimeOut) {
48+
mSrcRemotePath = srcRemotePath;
49+
mTargetRemotePath = targetRemotePath;
50+
this.sessionTimeOut = sessionTimeOut;
51+
}
52+
53+
/**
54+
* Performs the operation.
55+
*
56+
* @param client Client object to communicate with the remote ownCloud server.
57+
*/
58+
@Override
59+
protected RemoteOperationResult run(OwnCloudClient client) {
60+
61+
/// check parameters
62+
if (mTargetRemotePath.equals(mSrcRemotePath)) {
63+
// nothing to do!
64+
return new RemoteOperationResult<>(ResultCode.OK);
65+
}
66+
67+
if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
68+
return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT);
69+
}
70+
71+
/// perform remote operation
72+
CopyMethod copyMethod = null;
73+
RemoteOperationResult result;
74+
try {
75+
copyMethod = new CopyMethod(
76+
client.getFilesDavUri(this.mSrcRemotePath),
77+
client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(mTargetRemotePath),
78+
false
79+
);
80+
int status = client.executeMethod(copyMethod, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut());
81+
82+
/// process response
83+
if (status == HttpStatus.SC_MULTI_STATUS) {
84+
result = processPartialError(copyMethod);
85+
86+
} else if (status == HttpStatus.SC_PRECONDITION_FAILED) {
87+
88+
result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
89+
client.exhaustResponse(copyMethod.getResponseBodyAsStream());
90+
91+
} else {
92+
result = new RemoteOperationResult<>(isSuccess(status), copyMethod);
93+
client.exhaustResponse(copyMethod.getResponseBodyAsStream());
94+
}
95+
96+
Log.i(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
97+
98+
} catch (Exception e) {
99+
result = new RemoteOperationResult<>(e);
100+
Log.e(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage(), e);
101+
102+
} finally {
103+
if (copyMethod != null) {
104+
copyMethod.releaseConnection();
105+
}
106+
}
107+
108+
return result;
109+
}
110+
111+
112+
/**
113+
* Analyzes a multistatus response from the OC server to generate an appropriate result.
114+
* <p>
115+
* In WebDAV, a COPY request on collections (folders) can be PARTIALLY successful: some
116+
* children are copied, some other aren't.
117+
* <p>
118+
* According to the WebDAV specification, a multistatus response SHOULD NOT include partial
119+
* successes (201, 204) nor for descendants of already failed children (424) in the response
120+
* entity. But SHOULD NOT != MUST NOT, so take carefully.
121+
*
122+
* @param copyMethod Copy operation just finished with a multistatus response
123+
* @return A result for the {@link CopyFileToAlbumRemoteOperation} caller
124+
* @throws java.io.IOException If the response body could not be parsed
125+
* @throws org.apache.jackrabbit.webdav.DavException If the status code is other than MultiStatus or if obtaining
126+
* the response XML document fails
127+
*/
128+
private RemoteOperationResult processPartialError(CopyMethod copyMethod)
129+
throws IOException, DavException {
130+
// Adding a list of failed descendants to the result could be interesting; or maybe not.
131+
// For the moment, let's take the easy way.
132+
133+
/// check that some error really occurred
134+
MultiStatusResponse[] responses = copyMethod.getResponseBodyAsMultiStatus().getResponses();
135+
Status[] status;
136+
boolean failFound = false;
137+
for (int i = 0; i < responses.length && !failFound; i++) {
138+
status = responses[i].getStatus();
139+
failFound = (
140+
status != null &&
141+
status.length > 0 &&
142+
status[0].getStatusCode() > 299
143+
);
144+
}
145+
146+
RemoteOperationResult result;
147+
if (failFound) {
148+
result = new RemoteOperationResult<>(ResultCode.PARTIAL_COPY_DONE);
149+
} else {
150+
result = new RemoteOperationResult<>(true, copyMethod);
151+
}
152+
153+
return result;
154+
}
155+
156+
protected boolean isSuccess(int status) {
157+
return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT;
158+
}
159+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2025 TSI-mc <[email protected]>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.lib.resources.albums;
9+
10+
import com.nextcloud.common.SessionTimeOut;
11+
import com.nextcloud.common.SessionTimeOutKt;
12+
import com.owncloud.android.lib.common.OwnCloudClient;
13+
import com.owncloud.android.lib.common.network.WebdavUtils;
14+
import com.owncloud.android.lib.common.operations.RemoteOperation;
15+
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
16+
import com.owncloud.android.lib.common.utils.Log_OC;
17+
18+
import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
19+
20+
public class CreateNewAlbumRemoteOperation extends RemoteOperation<Void> {
21+
private static final String TAG = CreateNewAlbumRemoteOperation.class.getSimpleName();
22+
private final String newAlbumName;
23+
private final SessionTimeOut sessionTimeOut;
24+
25+
public CreateNewAlbumRemoteOperation(String newAlbumName) {
26+
this(newAlbumName, SessionTimeOutKt.getDefaultSessionTimeOut());
27+
}
28+
29+
public CreateNewAlbumRemoteOperation(String newAlbumName, SessionTimeOut sessionTimeOut) {
30+
this.newAlbumName = newAlbumName;
31+
this.sessionTimeOut = sessionTimeOut;
32+
}
33+
34+
public String getNewAlbumName() {
35+
return newAlbumName;
36+
}
37+
38+
/**
39+
* Performs the operation.
40+
*
41+
* @param client Client object to communicate with the remote ownCloud server.
42+
*/
43+
@Override
44+
protected RemoteOperationResult<Void> run(OwnCloudClient client) {
45+
MkColMethod mkCol = null;
46+
RemoteOperationResult<Void> result;
47+
try {
48+
mkCol = new MkColMethod(client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(newAlbumName));
49+
client.executeMethod(mkCol, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut());
50+
if (405 == mkCol.getStatusCode()) {
51+
result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS);
52+
} else {
53+
result = new RemoteOperationResult<>(mkCol.succeeded(), mkCol);
54+
result.setResultData(null);
55+
}
56+
57+
Log_OC.d(TAG, "Create album " + newAlbumName + ": " + result.getLogMessage());
58+
client.exhaustResponse(mkCol.getResponseBodyAsStream());
59+
} catch (Exception e) {
60+
result = new RemoteOperationResult<>(e);
61+
Log_OC.e(TAG, "Create album " + newAlbumName + ": " + result.getLogMessage(), e);
62+
} finally {
63+
if (mkCol != null) {
64+
mkCol.releaseConnection();
65+
}
66+
67+
}
68+
69+
return result;
70+
}
71+
72+
}

0 commit comments

Comments
 (0)