Add access column to CaldavCalendar

pull/1381/head
Alex Baker 3 years ago
parent b76b7dcb71
commit bb071f2027

File diff suppressed because it is too large Load Diff

@ -119,6 +119,44 @@ class CaldavSynchronizerTest : CaldavTest() {
</oc:access>
</oc:user>
</oc:invite>
<d:current-user-privilege-set>
<d:privilege>
<d:write />
</d:privilege>
<d:privilege>
<d:write-properties />
</d:privilege>
<d:privilege>
<d:write-content />
</d:privilege>
<d:privilege>
<d:unlock />
</d:privilege>
<d:privilege>
<d:bind />
</d:privilege>
<d:privilege>
<d:unbind />
</d:privilege>
<d:privilege>
<d:write-acl />
</d:privilege>
<d:privilege>
<d:read />
</d:privilege>
<d:privilege>
<d:read-acl />
</d:privilege>
<d:privilege>
<d:read-current-user-privilege-set />
</d:privilege>
<d:privilege>
<cal:read-free-busy />
</d:privilege>
</d:current-user-privilege-set>
<d:current-user-principal>
<d:href>/remote.php/dav/principals/users/user1/</d:href>
</d:current-user-principal>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>

@ -0,0 +1,134 @@
package org.tasks.caldav
import com.todoroo.astrid.helper.UUIDHelper
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import kotlinx.coroutines.runBlocking
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER
import org.tasks.injection.ProductionModule
@UninstallModules(ProductionModule::class)
@HiltAndroidTest
class OwnCloudSynchronizationTest : CaldavTest() {
@Before
override fun setUp() = runBlocking {
super.setUp()
account = CaldavAccount().apply {
uuid = UUIDHelper.newUUID()
username = "username"
password = encryption.encrypt("password")
url = server.url("/remote.php/dav/calendars/user1/").toString()
id = caldavDao.insert(this)
}
}
@Test
fun calendarOwner() = runBlocking {
val calendar = CaldavCalendar().apply {
account = this@OwnCloudSynchronizationTest.account.uuid
ctag = "http://sabre.io/ns/sync/1"
url = "${this@OwnCloudSynchronizationTest.account.url}test-shared/"
caldavDao.insert(this)
}
enqueue(OC_OWNER)
enqueueFailure()
synchronizer.sync(account)
assertEquals(ACCESS_OWNER, caldavDao.getCalendarByUuid(calendar.uuid!!)?.access)
}
companion object {
init {
CaldavSynchronizer.registerFactories()
}
private val OC_OWNER = """
<?xml version="1.0"?>
<d:multistatus xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav"
xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns">
<d:response>
<d:href>/remote.php/dav/calendars/user1/test-shared/</d:href>
<d:propstat>
<d:prop>
<d:resourcetype>
<d:collection />
<cal:calendar />
</d:resourcetype>
<d:displayname>Test shared</d:displayname>
<cal:supported-calendar-component-set>
<cal:comp name="VTODO" />
</cal:supported-calendar-component-set>
<cs:getctag>http://sabre.io/ns/sync/1</cs:getctag>
<x1:calendar-color xmlns:x1="http://apple.com/ns/ical/">#0082c9</x1:calendar-color>
<d:sync-token>http://sabre.io/ns/sync/1</d:sync-token>
<oc:owner-principal>principals/users/user1</oc:owner-principal>
<oc:invite>
<oc:user>
<d:href>principal:principals/users/user2</d:href>
<oc:common-name>user2</oc:common-name>
<oc:invite-accepted />
<oc:access>
<oc:read />
</oc:access>
</oc:user>
</oc:invite>
<d:current-user-privilege-set>
<d:privilege>
<d:write />
</d:privilege>
<d:privilege>
<d:write-properties />
</d:privilege>
<d:privilege>
<d:write-content />
</d:privilege>
<d:privilege>
<d:unlock />
</d:privilege>
<d:privilege>
<d:bind />
</d:privilege>
<d:privilege>
<d:unbind />
</d:privilege>
<d:privilege>
<d:write-acl />
</d:privilege>
<d:privilege>
<d:read />
</d:privilege>
<d:privilege>
<d:read-acl />
</d:privilege>
<d:privilege>
<d:read-current-user-privilege-set />
</d:privilege>
<d:privilege>
<cal:read-free-busy />
</d:privilege>
</d:current-user-privilege-set>
<d:current-user-principal>
<d:href>/remote.php/dav/principals/users/user1/</d:href>
</d:current-user-principal>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
<d:propstat>
<d:prop>
<d:share-access />
<d:invite />
</d:prop>
<d:status>HTTP/1.1 404 Not Found</d:status>
</d:propstat>
</d:response>
</d:multistatus>
""".trimIndent()
}
}

@ -27,7 +27,7 @@ import org.tasks.notifications.NotificationDao
CaldavTask::class,
CaldavAccount::class,
GoogleTaskAccount::class],
version = 76)
version = 77)
abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDao
abstract val tagDataDao: TagDataDao

@ -8,7 +8,6 @@ import android.util.Log
import androidx.core.app.JobIntentService
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import at.bitfire.dav4jvm.PropertyRegistry
import com.todoroo.astrid.service.Upgrader
import dagger.Lazy
import dagger.hilt.android.HiltAndroidApp
@ -18,11 +17,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.tasks.billing.BillingClient
import org.tasks.billing.Inventory
import org.tasks.caldav.property.Invite
import org.tasks.caldav.property.OCInvite
import org.tasks.caldav.property.OCOwnerPrincipal
import org.tasks.caldav.property.PropertyUtils.register
import org.tasks.caldav.property.ShareAccess
import org.tasks.caldav.CaldavSynchronizer
import org.tasks.files.FileHelper
import org.tasks.injection.InjectingJobIntentService
import org.tasks.jobs.WorkManager
@ -96,12 +91,7 @@ class Tasks : Application(), Configuration.Provider {
FileHelper.delete(context, preferences.cacheDirectory)
billingClient.get().queryPurchases()
appWidgetManager.get().reconfigureWidgets()
PropertyRegistry.register(
ShareAccess.Factory(),
Invite.Factory(),
OCOwnerPrincipal.Factory(),
OCInvite.Factory(),
)
CaldavSynchronizer.registerFactories()
}
override fun getWorkManagerConfiguration(): Configuration = Configuration.Builder()

@ -224,6 +224,8 @@ open class CaldavClient(
Invite.NAME,
OCOwnerPrincipal.NAME,
OCInvite.NAME,
CurrentUserPrivilegeSet.NAME,
CurrentUserPrincipal.NAME,
)
private suspend fun DavResource.propfind(

@ -4,6 +4,7 @@ import android.content.Context
import at.bitfire.dav4jvm.DavCalendar
import at.bitfire.dav4jvm.DavCalendar.Companion.MIME_ICALENDAR
import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.PropertyRegistry
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation
import at.bitfire.dav4jvm.exception.DavException
@ -29,9 +30,19 @@ import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory
import org.tasks.caldav.iCalendar.Companion.fromVtodo
import org.tasks.caldav.property.Invite
import org.tasks.caldav.property.OCInvite
import org.tasks.caldav.property.OCOwnerPrincipal
import org.tasks.caldav.property.PropertyUtils.register
import org.tasks.caldav.property.ShareAccess
import org.tasks.caldav.property.ShareAccess.Companion.READ
import org.tasks.caldav.property.ShareAccess.Companion.SHARED_OWNER
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavAccount.Companion.ERROR_UNAUTHORIZED
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER
import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_ONLY
import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_WRITE
import org.tasks.data.CaldavDao
import org.tasks.data.CaldavTask
import timber.log.Timber
@ -120,6 +131,7 @@ class CaldavSynchronizer @Inject constructor(
var calendar = caldavDao.getCalendarByUrl(account.uuid!!, url)
val remoteName = resource[DisplayName::class.java]!!.displayName
val calendarColor = resource[CalendarColor::class.java]
val access = resource.accessLevel
val color = calendarColor?.color ?: 0
if (calendar == null) {
calendar = CaldavCalendar()
@ -128,10 +140,15 @@ class CaldavSynchronizer @Inject constructor(
calendar.url = url
calendar.uuid = UUIDHelper.newUUID()
calendar.color = color
calendar.access = access
caldavDao.insert(calendar)
} else if (calendar.name != remoteName || calendar.color != color) {
} else if (calendar.name != remoteName
|| calendar.color != color
|| calendar.access != access
) {
calendar.color = color
calendar.name = remoteName
calendar.access = access
caldavDao.update(calendar)
localBroadcastManager.broadcastRefreshList()
}
@ -156,7 +173,9 @@ class CaldavSynchronizer @Inject constructor(
httpClient: OkHttpClient) {
Timber.d("sync(%s)", caldavCalendar)
val httpUrl = resource.href
pushLocalChanges(caldavCalendar, httpClient, httpUrl)
if (caldavCalendar.access != ACCESS_READ_ONLY) {
pushLocalChanges(caldavCalendar, httpClient, httpUrl)
}
val remoteCtag = resource.ctag
if (caldavCalendar.ctag?.equals(remoteCtag) == true) {
Timber.d("%s up to date", caldavCalendar.name)
@ -291,7 +310,37 @@ class CaldavSynchronizer @Inject constructor(
prodId = ProdId("+//IDN tasks.org//android-" + BuildConfig.VERSION_CODE + "//EN")
}
fun registerFactories() {
PropertyRegistry.register(
ShareAccess.Factory(),
Invite.Factory(),
OCOwnerPrincipal.Factory(),
OCInvite.Factory(),
)
}
val Response.ctag: String?
get() = this[SyncToken::class.java]?.token ?: this[GetCTag::class.java]?.cTag
val Response.accessLevel: Int
get() {
this[ShareAccess::class.java]?.let {
return when (it.access) {
SHARED_OWNER -> ACCESS_OWNER
READ -> ACCESS_READ_ONLY
else -> ACCESS_READ_WRITE
}
}
this[OCOwnerPrincipal::class.java]?.owner?.let {
val current = this[CurrentUserPrincipal::class.java]?.href
if (current?.endsWith("$it/") == true) {
return ACCESS_OWNER
}
}
return when (this[CurrentUserPrivilegeSet::class.java]?.mayWriteContent) {
false -> ACCESS_READ_ONLY
else -> ACCESS_READ_WRITE
}
}
}
}

@ -42,6 +42,9 @@ class CaldavCalendar : Parcelable {
@ColumnInfo(name = "cdl_order")
var order = NO_ORDER
@ColumnInfo(name = "cdl_access")
var access = 0
constructor()
@Ignore
@ -61,6 +64,7 @@ class CaldavCalendar : Parcelable {
url = source.readString()
icon = source.readInt()
order = source.readInt()
access = source.readInt()
}
fun getIcon(): Int? {
@ -84,6 +88,7 @@ class CaldavCalendar : Parcelable {
writeString(url)
writeInt(getIcon()!!)
writeInt(order)
writeInt(access)
}
}
@ -100,6 +105,7 @@ class CaldavCalendar : Parcelable {
if (url != other.url) return false
if (icon != other.icon) return false
if (order != other.order) return false
if (access != other.access) return false
return true
}
@ -114,13 +120,19 @@ class CaldavCalendar : Parcelable {
result = 31 * result + (url?.hashCode() ?: 0)
result = 31 * result + (icon ?: 0)
result = 31 * result + order
result = 31 * result + access
return result
}
override fun toString(): String =
"CaldavCalendar(id=$id, account=$account, uuid=$uuid, name=$name, color=$color, ctag=$ctag, url=$url, icon=$icon, order=$order)"
override fun toString(): String {
return "CaldavCalendar(id=$id, account=$account, uuid=$uuid, name=$name, color=$color, ctag=$ctag, url=$url, icon=$icon, order=$order, access=$access)"
}
companion object {
const val ACCESS_OWNER = 0
const val ACCESS_READ_WRITE = 1
const val ACCESS_READ_ONLY = 2
@JvmField val TABLE = Table("caldav_lists")
val ACCOUNT = TABLE.column("cdl_account")
@JvmField val UUID = TABLE.column("cdl_uuid")

@ -357,6 +357,13 @@ object Migrations {
}
}
private val MIGRATION_76_77: Migration = object : Migration(76, 77) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"ALTER TABLE `caldav_lists` ADD COLUMN `cdl_access` INTEGER NOT NULL DEFAULT 0")
}
}
val MIGRATIONS = arrayOf(
MIGRATION_35_36,
MIGRATION_36_37,
@ -389,7 +396,8 @@ object Migrations {
MIGRATION_72_73,
MIGRATION_73_74,
MIGRATION_74_75,
MIGRATION_75_76
MIGRATION_75_76,
MIGRATION_76_77,
)
private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) {

Loading…
Cancel
Save