feat(nag): sync recurring task instance completion via ActiveSync#30
Merged
TDannhauer merged 2 commits intoJun 24, 2026
Merged
Conversation
Apply single-instance completion, un-completion, and series-master updates from EAS clients (iOS, Outlook) to the recurring Nag task without spawning duplicate rows. - fromASTask(): route dead-occurrence Adds and master Modifies to dedicated recurrence handlers; track completions[] instead of toggling - Api::replace()/import(): resolve the series master for instance messages and merge dead-occurrence Adds into the master UID - Driver::modifyFromHash(): persist recurrence/completion state without toggleComplete() side effects - preserve the total RRULE COUNT on master Modify (clients send the remaining-occurrence count, not the series total) Adds Nag_Unit_Task_ActiveSyncRecurrenceTest covering completion, un-completion, master advance, final-instance reopen, and count preservation.
ralflang
approved these changes
Jun 24, 2026
Recurring tasks whose series is fully complete (or whose due date is implausible) return null from Nag_Task::getNextDue(). The task detail view and the portal Summary block dereferenced the result directly, causing "Call to a member function timestamp() on null". Guard both call sites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Motivation
Completing one occurrence of a recurring task on a device did not stick in Nag and could spawn duplicate task rows: clients send a dead-occurrence
Add(often reusing a client id) plus a masterModify, which the previous import/replace path mishandled. Un-completing an occurrence (including the final one) did not reopen the series correctly.Changes
Nag_Task::fromASTask()— route dead-occurrenceAdds and masterModifys to dedicated recurrence handlers; recordcompletions[]instead of callingtoggleComplete()._applyActiveSyncRecurrenceInstance()— complete/uncomplete a single occurrence, with rollback of the master due date when reopening a past instance._applyActiveSyncRecurrenceMasterChange()— apply next-due advance and reopen the final instance of a fully-complete series; preserve the total RRULE COUNT (clients send the remaining occurrence count, not the series total)._setActiveSyncCompletion()— shared completion-flag/date helper.Nag_Api::replace()/import()— resolve the series master for instance messages and merge dead-occurrenceAdds into the master UID (across editable tasklists); exposeresolveActiveSyncSeriesMasterUid().Nag_Driver::modifyFromHash()— persist recurrence/completion state withouttoggleComplete()side effects.toASTask()) reports the remaining occurrence count and next due date.Companion PRs
resolveTaskSeriesMasterUid()bridge.Test plan
vendor/bin/phpunit -c vendor/horde/nag/phpunit.xml.dist --bootstrap vendor/autoload.php vendor/horde/nag/test/Nag/Unit/Task/ActiveSyncRecurrenceTest.php(29 tests)