Add access column to CaldavCalendar

pull/1381/head
Alex Baker 5 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:access>
</oc:user> </oc:user>
</oc:invite> </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:prop>
<d:status>HTTP/1.1 200 OK</d:status> <d:status>HTTP/1.1 200 OK</d:status>
</d:propstat> </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, CaldavTask::class,
CaldavAccount::class, CaldavAccount::class,
GoogleTaskAccount::class], GoogleTaskAccount::class],
version = 76) version = 77)
abstract class Database : RoomDatabase() { abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDao abstract fun notificationDao(): NotificationDao
abstract val tagDataDao: TagDataDao abstract val tagDataDao: TagDataDao

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

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

@ -4,6 +4,7 @@ import android.content.Context
import at.bitfire.dav4jvm.DavCalendar import at.bitfire.dav4jvm.DavCalendar
import at.bitfire.dav4jvm.DavCalendar.Companion.MIME_ICALENDAR import at.bitfire.dav4jvm.DavCalendar.Companion.MIME_ICALENDAR
import at.bitfire.dav4jvm.DavResource import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.PropertyRegistry
import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation import at.bitfire.dav4jvm.Response.HrefRelation
import at.bitfire.dav4jvm.exception.DavException import at.bitfire.dav4jvm.exception.DavException
@ -29,9 +30,19 @@ import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.caldav.iCalendar.Companion.fromVtodo 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
import org.tasks.data.CaldavAccount.Companion.ERROR_UNAUTHORIZED import org.tasks.data.CaldavAccount.Companion.ERROR_UNAUTHORIZED
import org.tasks.data.CaldavCalendar 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.CaldavDao
import org.tasks.data.CaldavTask import org.tasks.data.CaldavTask
import timber.log.Timber import timber.log.Timber
@ -120,6 +131,7 @@ class CaldavSynchronizer @Inject constructor(
var calendar = caldavDao.getCalendarByUrl(account.uuid!!, url) var calendar = caldavDao.getCalendarByUrl(account.uuid!!, url)
val remoteName = resource[DisplayName::class.java]!!.displayName val remoteName = resource[DisplayName::class.java]!!.displayName
val calendarColor = resource[CalendarColor::class.java] val calendarColor = resource[CalendarColor::class.java]
val access = resource.accessLevel
val color = calendarColor?.color ?: 0 val color = calendarColor?.color ?: 0
if (calendar == null) { if (calendar == null) {
calendar = CaldavCalendar() calendar = CaldavCalendar()
@ -128,10 +140,15 @@ class CaldavSynchronizer @Inject constructor(
calendar.url = url calendar.url = url
calendar.uuid = UUIDHelper.newUUID() calendar.uuid = UUIDHelper.newUUID()
calendar.color = color calendar.color = color
calendar.access = access
caldavDao.insert(calendar) 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.color = color
calendar.name = remoteName calendar.name = remoteName
calendar.access = access
caldavDao.update(calendar) caldavDao.update(calendar)
localBroadcastManager.broadcastRefreshList() localBroadcastManager.broadcastRefreshList()
} }
@ -156,7 +173,9 @@ class CaldavSynchronizer @Inject constructor(
httpClient: OkHttpClient) { httpClient: OkHttpClient) {
Timber.d("sync(%s)", caldavCalendar) Timber.d("sync(%s)", caldavCalendar)
val httpUrl = resource.href val httpUrl = resource.href
pushLocalChanges(caldavCalendar, httpClient, httpUrl) if (caldavCalendar.access != ACCESS_READ_ONLY) {
pushLocalChanges(caldavCalendar, httpClient, httpUrl)
}
val remoteCtag = resource.ctag val remoteCtag = resource.ctag
if (caldavCalendar.ctag?.equals(remoteCtag) == true) { if (caldavCalendar.ctag?.equals(remoteCtag) == true) {
Timber.d("%s up to date", caldavCalendar.name) 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") 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? val Response.ctag: String?
get() = this[SyncToken::class.java]?.token ?: this[GetCTag::class.java]?.cTag 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") @ColumnInfo(name = "cdl_order")
var order = NO_ORDER var order = NO_ORDER
@ColumnInfo(name = "cdl_access")
var access = 0
constructor() constructor()
@Ignore @Ignore
@ -61,6 +64,7 @@ class CaldavCalendar : Parcelable {
url = source.readString() url = source.readString()
icon = source.readInt() icon = source.readInt()
order = source.readInt() order = source.readInt()
access = source.readInt()
} }
fun getIcon(): Int? { fun getIcon(): Int? {
@ -84,6 +88,7 @@ class CaldavCalendar : Parcelable {
writeString(url) writeString(url)
writeInt(getIcon()!!) writeInt(getIcon()!!)
writeInt(order) writeInt(order)
writeInt(access)
} }
} }
@ -100,6 +105,7 @@ class CaldavCalendar : Parcelable {
if (url != other.url) return false if (url != other.url) return false
if (icon != other.icon) return false if (icon != other.icon) return false
if (order != other.order) return false if (order != other.order) return false
if (access != other.access) return false
return true return true
} }
@ -114,13 +120,19 @@ class CaldavCalendar : Parcelable {
result = 31 * result + (url?.hashCode() ?: 0) result = 31 * result + (url?.hashCode() ?: 0)
result = 31 * result + (icon ?: 0) result = 31 * result + (icon ?: 0)
result = 31 * result + order result = 31 * result + order
result = 31 * result + access
return result return result
} }
override fun toString(): String = override fun toString(): String {
"CaldavCalendar(id=$id, account=$account, uuid=$uuid, name=$name, color=$color, ctag=$ctag, url=$url, icon=$icon, order=$order)" return "CaldavCalendar(id=$id, account=$account, uuid=$uuid, name=$name, color=$color, ctag=$ctag, url=$url, icon=$icon, order=$order, access=$access)"
}
companion object { 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") @JvmField val TABLE = Table("caldav_lists")
val ACCOUNT = TABLE.column("cdl_account") val ACCOUNT = TABLE.column("cdl_account")
@JvmField val UUID = TABLE.column("cdl_uuid") @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( val MIGRATIONS = arrayOf(
MIGRATION_35_36, MIGRATION_35_36,
MIGRATION_36_37, MIGRATION_36_37,
@ -389,7 +396,8 @@ object Migrations {
MIGRATION_72_73, MIGRATION_72_73,
MIGRATION_73_74, MIGRATION_73_74,
MIGRATION_74_75, MIGRATION_74_75,
MIGRATION_75_76 MIGRATION_75_76,
MIGRATION_76_77,
) )
private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) { private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) {

Loading…
Cancel
Save