Synching and deleting single events from recurrent / repeating events



  • Hello!

    I use CM13/Android 6.1, aCalenar+, Nextcloud 10, Thunderbird/Lighting and quite recently I switched to DavDROID for synching. Before that I was using Marten Gajda's CalDAV.

    After switching to DavDROID I noticed that if I delete a single event from a series of repeating events within aCalendar+, that event is not shown in Android's calendars any more, but Thunderbird/Lighting still shows it as "canceled" after synching with NextCloud.

    Back when I used CalDAV, those deleted events where not shown in Thunderbird any more, they had been deleted instead of being marked as "canceled".

    I told the support about this and Rick told me it's not DavDROID's behavior that causes this. He suspected that CalDAV somehow wrongly turned the status from "canceled" into "deleted" before synching a series of events.

    I have to disagree to this, because whenever I delete a single event from a series of events within Thunderbird, it does get deleted, and not just marked as canceled. If Thunderbird (SoGO connector) would do this wrong, that would already make two applications that handle deleted events incorrect. And the same goes for other calendar applications, like KOrganize or Evolution: they all would do it wrong.

    No offense meant, but I highly doubt that!

    Has this been discussed before? If so, I need to apologize, I did search, but I did not find any thing about it.


  • developer

    Hello,

    @paranoid-android said in Synching and deleting single events from recurrent / repeating events:

    […] whenever I delete a single event from a series of events within Thunderbird, it does get deleted, and not just marked as canceled. If Thunderbird (SoGO connector) would do this wrong, that would already make two applications that handle deleted events incorrect. And the same goes for other calendar applications, like KOrganize or Evolution: they all would do it wrong.

    If you do it in Thunderbird, it probably makes an EXDATE. I can't say that because I haven't checked it, and I have never said anything about Thunderbird.

    What I said is that on Android, most calendar apps don't set the deleted instance as EXDATE (which would remove the instance), but let the instance active and mark it as status: cancelled.

    Proof:

    1. Create a recurring event in Nextcloud and sync it with DAVdroid:
    V/davdroid(32418): BEGIN:VEVENT
    V/davdroid(32418): CREATED:20161019T131131
    V/davdroid(32418): DTSTAMP:20161019T131131
    V/davdroid(32418): LAST-MODIFIED:20161019T131131
    V/davdroid(32418): UID:1hfhkjybp8u
    V/davdroid(32418): SUMMARY:Recur1
    V/davdroid(32418): CLASS:PUBLIC
    V/davdroid(32418): STATUS:CONFIRMED
    V/davdroid(32418): DTSTART;TZID=Europe/Berlin:20161019T091500
    V/davdroid(32418): DTEND;TZID=Europe/Berlin:20161019T101500
    V/davdroid(32418): RRULE:FREQ=DAILY;COUNT=3
    V/davdroid(32418): END:VEVENT
    

    After synchronizing, it shows up in the calendar as recurring event with 3 instances. If you have a look at the Android calendar database, you can find those entries:

    sqlite> select * from events;
                      _id = 117
                 _sync_id = ownCloud-fcewuc3fetwce9zoqv3br.ics
                    dirty = 0
                 mutators = 
               lastSynced = 0
              calendar_id = 1
                    title = Recur1
            eventLocation = 
              description = 
               eventColor = 
         eventColor_index = 
              eventStatus = 1
       selfAttendeeStatus = 0
                  dtstart = 1476861300000
                    dtend = 
            eventTimezone = Europe/Berlin
                 duration = PT1H
                   allDay = 0
              accessLevel = 3
             availability = 0
                 hasAlarm = 0
    hasExtendedProperties = 0
                    rrule = FREQ=DAILY;COUNT=3
                    rdate = 
                   exrule = 
                   exdate = 
              original_id = 
         original_sync_id = 
     originalInstanceTime = 
           originalAllDay = 
                 lastDate = 1477037700000
          hasAttendeeData = 1
          guestsCanModify = 0
    guestsCanInviteOthers = 1
       guestsCanSeeGuests = 1
                organizer = <account name>
              isOrganizer = 
                  deleted = 0
         eventEndTimezone = 
         customAppPackage = 
             customAppUri = 
                  uid2445 = 1hfhkjybp8u
               sync_data1 = dff8e0844b01409cc9c33741a3d9f633
               sync_data2 = 
               sync_data3 = 0
               sync_data4 = 
               sync_data5 = 
               sync_data6 = 
               sync_data7 = 
               sync_data8 = 
               sync_data9 = 
              sync_data10 = 
    
    sqlite> select * from Instances;
            _id = 1
       event_id = 117
          begin = 1476861300000
            end = 1476864900000
       startDay = 2457681
         endDay = 2457681
    startMinute = 555
      endMinute = 615
    
            _id = 2
       event_id = 117
          begin = 1476947700000
            end = 1476951300000
       startDay = 2457682
         endDay = 2457682
    startMinute = 555
      endMinute = 615
    
            _id = 3
       event_id = 117
          begin = 1477034100000
            end = 1477037700000
       startDay = 2457683
         endDay = 2457683
    startMinute = 555
      endMinute = 615
    
    1. Open the default CM13 calendar app, choose the event on 20 Oct 2016, Delete, "Only this event".

    Now look at the database again:

    sqlite> select * from events;
                      _id = 117
                 _sync_id = ownCloud-fcewuc3fetwce9zoqv3br.ics
                    dirty = 0
                 mutators = 
               lastSynced = 0
              calendar_id = 1
                    title = Recur1
            eventLocation = 
              description = 
               eventColor = 
         eventColor_index = 
              eventStatus = 1
       selfAttendeeStatus = 0
                  dtstart = 1476861300000
                    dtend = 
            eventTimezone = Europe/Berlin
                 duration = PT1H
                   allDay = 0
              accessLevel = 3
             availability = 0
                 hasAlarm = 0
    hasExtendedProperties = 0
                    rrule = FREQ=DAILY;COUNT=3
                    rdate = 
                   exrule = 
                   exdate = 
              original_id = 
         original_sync_id = 
     originalInstanceTime = 
           originalAllDay = 
                 lastDate = 1477037700000
          hasAttendeeData = 1
          guestsCanModify = 0
    guestsCanInviteOthers = 1
       guestsCanSeeGuests = 1
                organizer = <account name>
              isOrganizer = 
                  deleted = 0
         eventEndTimezone = 
         customAppPackage = 
             customAppUri = 
                  uid2445 = 1hfhkjybp8u
               sync_data1 = dff8e0844b01409cc9c33741a3d9f633
               sync_data2 = 
               sync_data3 = 0
               sync_data4 = 
               sync_data5 = 
               sync_data6 = 
               sync_data7 = 
               sync_data8 = 
               sync_data9 = 
              sync_data10 = 
    
                      _id = 118
                 _sync_id = 
                    dirty = 1
                 mutators = com.android.calendar
               lastSynced = 0
              calendar_id = 1
                    title = Recur1
            eventLocation = 
              description = 
               eventColor = 
         eventColor_index = 
              eventStatus = 2
       selfAttendeeStatus = 0
                  dtstart = 1476947700000
                    dtend = 1476951300000
            eventTimezone = Europe/Berlin
                 duration = 
                   allDay = 0
              accessLevel = 0
             availability = 0
                 hasAlarm = 0
    hasExtendedProperties = 0
                    rrule = 
                    rdate = 
                   exrule = 
                   exdate = 
              original_id = 117
         original_sync_id = ownCloud-fcewuc3fetwce9zoqv3br.ics
     originalInstanceTime = 1476947700000
           originalAllDay = 0
                 lastDate = 1476951300000
          hasAttendeeData = 0
          guestsCanModify = 0
    guestsCanInviteOthers = 1
       guestsCanSeeGuests = 1
                organizer = <account name>
              isOrganizer = 
                  deleted = 0
         eventEndTimezone = 
         customAppPackage = 
             customAppUri = 
                  uid2445 = 
               sync_data1 = 
               sync_data2 = 
               sync_data3 = 
               sync_data4 = 
               sync_data5 = 
               sync_data6 = 
               sync_data7 = 
               sync_data8 = 
               sync_data9 = 
              sync_data10 = 
    

    As you can see, the original event (_id 117) is the same and no exdate (or exrule) has been set. Instead, an exception event has been created (_id 118, referring to original_id 117) on 20 Oct 2016 (originalInstanceTime = 1476947700000 = Thu Oct 20 07:15:00 2016 UTC). This exception on 20 Oct has eventStatus=2, which is STATUS_CANCELED and means that this instance has been cancelled.

    Until now, DAVdroid hasn't even been involved – it's just the calendar app which creates an exception for the instance and sets it to cancelled (instead of setting it as an EXDATE).

    1. DAVdroid synchronizes this event:
    V/davdroid(32418): BEGIN:VEVENT
    V/davdroid(32418): DTSTAMP:20161019T112935Z
    V/davdroid(32418): UID:1hfhkjybp8u
    V/davdroid(32418): SEQUENCE:1
    V/davdroid(32418): DTSTART;TZID=Europe/Berlin:20161019T091500
    V/davdroid(32418): DURATION:PT1H
    V/davdroid(32418): RRULE:FREQ=DAILY;COUNT=3
    V/davdroid(32418): SUMMARY:Recur1
    V/davdroid(32418): STATUS:CONFIRMED
    V/davdroid(32418): CLASS:PUBLIC
    V/davdroid(32418): CREATED:20161019T131131
    V/davdroid(32418): END:VEVENT
    V/davdroid(32418): BEGIN:VEVENT
    V/davdroid(32418): DTSTAMP:20161019T112935Z
    V/davdroid(32418): UID:1hfhkjybp8u
    V/davdroid(32418): RECURRENCE-ID:20161020T071500Z
    V/davdroid(32418): SEQUENCE:1
    V/davdroid(32418): DTSTART;TZID=Europe/Berlin:20161020T091500
    V/davdroid(32418): DTEND;TZID=Europe/Berlin:20161020T101500
    V/davdroid(32418): SUMMARY:Recur1
    V/davdroid(32418): STATUS:CANCELLED
    V/davdroid(32418): END:VEVENT
    

    That's exactly what's DAVdroid has queried from the database (using the calendar provider): a recurring event, where the instance on RECURRENCE-ID:20161020T071500Z is marked as STATUS:CANCELLED.

    I don't see any room for interpretation here or where you see a problem. Maybe I have overseen something, but in this case, I would need a specific report on what's wrong.

    PS: My name is Ricki (with i) and I'm not a he.


  • developer


  • developer

    By the way, you can see in the AOSP Calendar app source code that "deleting" a single event just sets it status to cancelled, instead of setting an exdate.



  • @rfc2822: I am sorry about the lost "i" in your name, and I certainly did not want to turn you into a male!

    You are right about the EXDATE: I am not very familiar with Android's data bases, so I used Thunderbird for a test. I created a series of events in Thunderbird and deleted one of the events and this is the result:

    BEGIN:VEVENT
    CREATED:20161019T113937Z
    LAST-MODIFIED:20161019T114111Z
    DTSTAMP:20161019T114111Z
    UID:e3372b0e-f1da-4f0f-a374-a6262e583e99
    SUMMARY:Test Serientermin
    RRULE:FREQ=DAILY;UNTIL=20161112T130000Z
    EXDATE:20161110T130000Z
    DTSTART;TZID=Europe/Berlin:20161107T140000
    DTEND;TZID=Europe/Berlin:20161107T150000
    TRANSP:OPAQUE
    SEQUENCE:1
    X-MOZ-GENERATION:2
    BEGIN:VALARM
    ACTION:DISPLAY
    TRIGGER;VALUE=DURATION:-PT30M
    DESCRIPTION:Mozilla Standardbeschreibung
    END:VALARM
    END:VEVENT

    As you can see, TB has made an exception for that event. After that, I tried some other desktop applications, and they all did it this way.

    I will not argue with you if this is correct or not, I think you are the expert on this. But humbly I would like to ask this: if other applications (plural!) use EXDATE instead of marking events as canceled, wouldn't it make sense if DavDroid includes an option that will turn canceled events into deleted events while synching, if the user wants it?


  • developer

    @paranoid-android It would be possible, but it would be a dirty hack. In my opinion, the scope of this would be the calendar application. It could simply set an EXDATE instead of creating an instance with STATUS:CANCELED.

    Of course, the chances for getting this into the AOSP calendar app are small, but if you use aCalendar+, it shouldn't be a problem for the developers to implement this. It would also be a marketing argument for them ("use aCalendar instead of AOSP Calendar, because it handles deleted instances of recurring events much better"). :)



  • @rfc2822 said in Synching and deleting single events from recurrent / repeating events:

    Of course, the chances for getting this into the AOSP calendar app are small, but if you use aCalendar+, it shouldn't be a problem for the developers to implement this. It would also be a marketing argument for them ("use aCalendar instead of AOSP Calendar, because it handles deleted instances of recurring events much better"). :)

    Well, I did ask Matthias from "Tapir Apps" and guess what he told me, who I should ask about this. ;-)


  • admin

    Thanks for the update. This behaviour does not belong to DAVdroid, as it is a sync adapter, in any case... We have to talk to them ourselves then, I guess :)



  • @rfc2822 said in Synching and deleting single events from recurrent / repeating events:

    It would be possible, but it would be a dirty hack.

    On the one hand I wouldn't see anything bad in it, as long as users are free to pick up that dirt or leave it lying on the ground, if they don't want it. ;-)

    In other words: if it's an option that can be chosen to be activated or deactivated, it's up to the users whether or not they want this hack. And if they activate it, after reading an appropriate explanation, then there's a good chance that they know what they are doing.

    And, on the other hand: there are synchronizing adapters / apps out there, that do this hack without leaving the choice to deactivate it.

    What I said is that on Android, most calendar apps don't set the deleted instance as EXDATE (which would remove the instance), but let the instance active and mark it as status: cancelled.

    I won't argue with you about that, because I believe you know Android better than me. What I can add to this statement is, that I haven't met any calendar application on my Linux box so far, that does not use EXDATE for exceptions in a series of events. I can't tell you any thing about Windows applications, because I don't use Windows, but if any one with experience in this could give an answer to that question, I'd be happy to hear it, although I am afraid that in the end, like so many times, the question about which system and which application does it right and which does it wrong, could become a philosophical question, rather than a technical one. :-)


  • developer

    @paranoid-android

    On the one hand I wouldn't see anything bad in it, as long as users are free to pick up that dirt or leave it lying on the ground, if they don't want it. ;-)

    It would have to be developed, tested, maintained (and probably cause tons of following bugs which would have to be fixed and tested against, too). I just don't want to do all this work for a thing which is basically on the wrong place and causes much more work here as when it was implemented where it belongs to.

    I have written to the aCalendar+ team, hoping that they will implement that, which would be the correct solution and a good marketing advantage for them.


Log in to reply
 

Looks like your connection to Bitfire App Forums was lost, please wait while we try to reconnect.