Skip to content

Commit 20b4703

Browse files
Merge pull request #1706 from nextcloud/add-attributes-to-oc-share
Add Attributes to Share
2 parents 4b8c5ac + d922fac commit 20b4703

10 files changed

Lines changed: 202 additions & 26 deletions

File tree

library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ package com.owncloud.android.lib.resources.shares
99

1010
import com.owncloud.android.AbstractIT
1111
import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation
12+
import com.owncloud.android.lib.resources.shares.attributes.ShareAttributes
13+
import com.owncloud.android.lib.resources.shares.attributes.ShareAttributesJsonHandler
1214
import com.owncloud.android.lib.resources.status.GetStatusRemoteOperation
1315
import com.owncloud.android.lib.resources.status.NextcloudVersion
1416
import com.owncloud.android.lib.resources.status.OwnCloudVersion
15-
import org.junit.Assert
1617
import org.junit.Assert.assertEquals
18+
import org.junit.Assert.assertTrue
1719
import org.junit.Assume
1820
import org.junit.Before
1921
import org.junit.Test
@@ -22,40 +24,58 @@ class CreateShareRemoteOperationIT : AbstractIT() {
2224
@Before
2325
fun before() {
2426
val result = GetStatusRemoteOperation(context).execute(client)
25-
Assert.assertTrue(result.isSuccess)
27+
assertTrue(result.isSuccess)
2628
val data = result.data as ArrayList<Any>
2729
val ownCloudVersion = data[0] as OwnCloudVersion
2830
Assume.assumeTrue(ownCloudVersion.isNewerOrEqual(NextcloudVersion.nextcloud_24))
2931
}
3032

33+
@Test
34+
fun createShareWithNoteAndAttributes() {
35+
val attributes = listOf(ShareAttributes.createDownloadAttributes(true))
36+
val note = "Note with attributes"
37+
val path = "/shareWithAttributes/"
38+
39+
createFolder(path)
40+
val share = createShare(path, "admin", note, ShareAttributesJsonHandler.toJson(attributes))
41+
assertEquals(note, share.note)
42+
assertEquals(attributes, ShareAttributesJsonHandler.toList(share.attributes))
43+
}
44+
3145
@Test
3246
fun createShareWithNote() {
3347
val note = "This is the note"
48+
val path = "/share/"
3449

35-
Assert.assertTrue(
36-
CreateFolderRemoteOperation(
37-
"/share/",
38-
true
39-
).execute(client).isSuccess
40-
)
50+
createFolder(path)
51+
val share = createShare(path, "admin", note)
52+
assertEquals(note, share.note)
53+
}
4154

42-
// share folder to user "admin"
43-
val sut =
55+
private fun createFolder(path: String) {
56+
assertTrue(CreateFolderRemoteOperation(path, true).execute(client).isSuccess)
57+
}
58+
59+
private fun createShare(
60+
path: String,
61+
accountName: String,
62+
note: String,
63+
attributes: String? = null
64+
): OCShare {
65+
val operation =
4466
CreateShareRemoteOperation(
45-
"/share/",
67+
path,
4668
ShareType.USER,
47-
"admin",
69+
accountName,
4870
false,
4971
"",
5072
OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER,
5173
true,
52-
note
53-
).execute(client)
54-
55-
junit.framework.Assert.assertTrue(sut.isSuccess)
56-
57-
val share = sut.resultData[0]
58-
59-
assertEquals(note, share.note)
74+
note,
75+
attributes
76+
)
77+
val result = operation.execute(client)
78+
assertTrue(result.isSuccess)
79+
return result.resultData[0]
6080
}
6181
}

library/src/androidTest/java/com/owncloud/android/lib/resources/shares/UpdateShareRemoteOperationIT.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ class UpdateShareRemoteOperationIT : AbstractIT() {
5757
"",
5858
OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER,
5959
true,
60-
""
60+
"",
61+
null
6162
).execute(client)
6263

6364
assertTrue(createOperationResult.isSuccess)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2025 Alper Ozturk <alper.ozturk@nextcloud.com>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
package com.nextcloud.extensions
10+
11+
import com.google.gson.JsonObject
12+
13+
@Suppress("ReturnCount")
14+
fun JsonObject?.getBoolean(key: String): Boolean? {
15+
if (this == null) {
16+
return null
17+
}
18+
19+
if (has(key) && get(key).isJsonPrimitive) {
20+
return try {
21+
get(key).asBoolean
22+
} catch (_: UnsupportedOperationException) {
23+
null
24+
}
25+
}
26+
27+
return null
28+
}

library/src/main/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperation.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class CreateShareRemoteOperation extends RemoteOperation<List<OCShare>> {
3737
private static final String PARAM_PASSWORD = "password";
3838
private static final String PARAM_PERMISSIONS = "permissions";
3939
private static final String PARAM_NOTE = "note";
40+
private static final String PARAM_ATTRIBUTES = "attributes";
4041

4142
private final String remoteFilePath;
4243
private final ShareType shareType;
@@ -46,6 +47,7 @@ public class CreateShareRemoteOperation extends RemoteOperation<List<OCShare>> {
4647
private final int permissions;
4748
private boolean getShareDetails;
4849
private String note;
50+
private String attributes;
4951

5052
/**
5153
* Constructor
@@ -67,6 +69,7 @@ public class CreateShareRemoteOperation extends RemoteOperation<List<OCShare>> {
6769
* For user or group shares.
6870
* To obtain combinations, add the desired values together.
6971
* For instance, for Re-Share, delete, read, update, add 16+8+2+1 = 27.
72+
* @param attributes Share attributes are used for more advanced flags like permissions.
7073
*/
7174
public CreateShareRemoteOperation(
7275
String remoteFilePath,
@@ -76,7 +79,8 @@ public CreateShareRemoteOperation(
7679
String password,
7780
int permissions,
7881
boolean getShareDetails,
79-
String note
82+
String note,
83+
String attributes
8084
) {
8185
this.remoteFilePath = remoteFilePath;
8286
this.shareType = shareType;
@@ -86,6 +90,7 @@ public CreateShareRemoteOperation(
8690
this.permissions = permissions;
8791
this.getShareDetails = getShareDetails; // defaults to false for backwards compatibility
8892
this.note = note;
93+
this.attributes = attributes;
8994
}
9095

9196
public CreateShareRemoteOperation(
@@ -95,7 +100,7 @@ public CreateShareRemoteOperation(
95100
boolean publicUpload,
96101
String password,
97102
int permissions) {
98-
this(remoteFilePath, shareType, shareWith, publicUpload, password, permissions, false, "");
103+
this(remoteFilePath, shareType, shareWith, publicUpload, password, permissions, false, "", null);
99104
}
100105

101106
public CreateShareRemoteOperation(
@@ -105,8 +110,9 @@ public CreateShareRemoteOperation(
105110
boolean publicUpload,
106111
String password,
107112
int permissions,
108-
String note) {
109-
this(remoteFilePath, shareType, shareWith, publicUpload, password, permissions, false, note);
113+
String note,
114+
String attributes) {
115+
this(remoteFilePath, shareType, shareWith, publicUpload, password, permissions, false, note, attributes);
110116
}
111117

112118
public CreateShareRemoteOperation(
@@ -117,7 +123,7 @@ public CreateShareRemoteOperation(
117123
String password,
118124
int permissions,
119125
boolean getShareDetails) {
120-
this(remoteFilePath, shareType, shareWith, publicUpload, password, permissions, getShareDetails, "");
126+
this(remoteFilePath, shareType, shareWith, publicUpload, password, permissions, getShareDetails, "", null);
121127
}
122128

123129
public boolean isGettingShareDetails() {
@@ -158,6 +164,10 @@ protected RemoteOperationResult<List<OCShare>> run(OwnCloudClient client) {
158164
post.addParameter(PARAM_NOTE, note);
159165
}
160166

167+
if (!TextUtils.isEmpty(attributes)) {
168+
post.addParameter(PARAM_ATTRIBUTES, attributes);
169+
}
170+
161171
post.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
162172

163173
status = client.executeMethod(post);

library/src/main/java/com/owncloud/android/lib/resources/shares/OCShare.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class OCShare :
7373
var ownerDisplayName: String? = null
7474
var isFavorite = false
7575
var fileDownloadLimit: FileDownloadLimit? = null
76+
var attributes: String? = null
7677

7778
constructor() : super() {
7879
resetData()
@@ -114,6 +115,7 @@ class OCShare :
114115
mimetype = ""
115116
ownerDisplayName = ""
116117
fileDownloadLimit = null
118+
attributes = null
117119
}
118120

119121
/**
@@ -154,6 +156,7 @@ class OCShare :
154156
mimetype = source.readString()
155157
ownerDisplayName = source.readString()
156158
fileDownloadLimit = source.readSerializableCompat()
159+
attributes = source.readString()
157160
}
158161

159162
override fun describeContents(): Int = this.hashCode()
@@ -184,6 +187,7 @@ class OCShare :
184187
dest.writeString(mimetype)
185188
dest.writeString(ownerDisplayName)
186189
dest.writeSerializable(fileDownloadLimit)
190+
dest.writeString(attributes)
187191
}
188192

189193
companion object {

library/src/main/java/com/owncloud/android/lib/resources/shares/ShareXMLParser.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Date;
2525
import java.util.List;
2626

27+
2728
/**
2829
* Parser for Share API Response
2930
*
@@ -70,6 +71,7 @@ public class ShareXMLParser {
7071
private static final String NODE_DISPLAYNAME_FILE_OWNER = "displayname_file_owner";
7172
private static final String NODE_TAGS = "tags";
7273
private static final String NODE_URL = "url";
74+
private static final String NODE_ATTRIBUTES = "attributes";
7375

7476
private static final String TAG_FAVORITE = "_$!<Favorite>";
7577

@@ -418,6 +420,10 @@ private void readElement(XmlPullParser parser, ArrayList<OCShare> shares)
418420
}
419421
break;
420422

423+
case NODE_ATTRIBUTES:
424+
share.setAttributes(readText(parser));
425+
break;
426+
421427
default:
422428
skip(parser);
423429
break;

library/src/main/java/com/owncloud/android/lib/resources/shares/UpdateShareRemoteOperation.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public class UpdateShareRemoteOperation extends RemoteOperation {
4949
private static final String FORMAT_EXPIRATION_DATE = "yyyy-MM-dd";
5050
private static final String ENTITY_CONTENT_TYPE = "application/x-www-form-urlencoded";
5151
private static final String ENTITY_CHARSET = "UTF-8";
52+
private static final String PARAM_ATTRIBUTES = "attributes";
5253

5354

5455
/**
@@ -78,6 +79,7 @@ public class UpdateShareRemoteOperation extends RemoteOperation {
7879

7980
private String note;
8081
private String label;
82+
private String attributes;
8183

8284

8385
/**
@@ -137,6 +139,10 @@ public void setLabel(String label) {
137139
this.label = label;
138140
}
139141

142+
public void setAttributes(String attributes) {
143+
this.attributes = attributes;
144+
}
145+
140146
public void setNote(String note) {
141147
this.note = note;
142148
}
@@ -181,6 +187,10 @@ protected RemoteOperationResult<List<OCShare>> run(OwnCloudClient client) {
181187
parametersToUpdate.add(new Pair<>(PARAM_LABEL, URLEncoder.encode(label)));
182188
}
183189

190+
if (attributes != null) {
191+
parametersToUpdate.add(new Pair<>(PARAM_ATTRIBUTES, URLEncoder.encode(attributes)));
192+
}
193+
184194
/// perform required PUT requests
185195
PutMethod put = null;
186196
String uriString;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2025 Alper Ozturk <alper.ozturk@nextcloud.com>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
package com.owncloud.android.lib.resources.shares.attributes
10+
11+
data class ShareAttributes(
12+
val scope: String,
13+
val key: String,
14+
var value: Boolean
15+
) {
16+
companion object {
17+
const val DOWNLOAD_ATTRIBUTE_KEY = "download"
18+
19+
fun createDownloadAttributes(value: Boolean): ShareAttributes =
20+
ShareAttributes(scope = "permissions", key = DOWNLOAD_ATTRIBUTE_KEY, value = value)
21+
}
22+
}
23+
24+
fun List<ShareAttributes>?.getDownloadAttribute(): ShareAttributes? =
25+
this?.find { it.key == ShareAttributes.DOWNLOAD_ATTRIBUTE_KEY }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Nextcloud Android Library
3+
*
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-FileCopyrightText: 2025 Alper Ozturk <alper.ozturk@nextcloud.com>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
package com.owncloud.android.lib.resources.shares.attributes
10+
11+
import com.google.gson.JsonDeserializationContext
12+
import com.google.gson.JsonDeserializer
13+
import com.google.gson.JsonElement
14+
import com.nextcloud.extensions.getBoolean
15+
import java.lang.reflect.Type
16+
17+
/**
18+
* Custom serializer for the ShareAttributes class.
19+
* This handles the deserialization and serialization of the ShareAttributes data class.
20+
* Since Nextcloud 30, the enabled key have been renamed to value and supports more than boolean.
21+
*
22+
* https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-share-api.html#share-attributes
23+
*/
24+
class ShareAttributesDeserializer : JsonDeserializer<ShareAttributes> {
25+
override fun deserialize(
26+
json: JsonElement?,
27+
typeOfT: Type?,
28+
context: JsonDeserializationContext?
29+
): ShareAttributes? {
30+
val jsonObject = json?.asJsonObject
31+
val scope = jsonObject?.get("scope")?.asString ?: ""
32+
val key = jsonObject?.get("key")?.asString ?: ""
33+
val value = (jsonObject.getBoolean("value") ?: jsonObject.getBoolean("enabled")) == true
34+
return ShareAttributes(scope, key, value)
35+
}
36+
}

0 commit comments

Comments
 (0)