Ga naar hoofdinhoud
AcademytutorialBuild a Nextcloud app on the Conduction stack — Part 3: Schema-driven integrations

Build a Nextcloud app on the Conduction stack — Part 3: Schema-driven integrations

One calendarProvider block on the order schema. Order ship-dates appear in NC Calendar with no controller, no event listener, no per-app glue.

TutorialApp developmentOpenRegisterNC CalendarCalDAVSchema integrationsTutorial series
7 min read

This is Part 3 of the nine-part app-building tutorial series. Part 2 wired the pet and order schemas. Part 3 makes those orders appear in every user's Nextcloud Calendar (without writing a controller, an event listener, or any per-app calendar glue).

The trick: OpenRegister already ships an ICalendarProvider (RegisterCalendarProvider) that exposes any schema with a calendarProvider block as a virtual calendar. You declare the block on the order schema. NC Calendar shows the events. That's the whole story.

Why not OpenConnector or a custom listener?

When you first hear "sync PetStore order ship-dates to Nextcloud Calendar" your instinct is probably one of:

  1. Write an OpenConnector flow that listens to OrderCreatedEvent and POSTs to the CalDAV endpoint
  2. Subclass IEventListener<OrderCreatedEvent> in PHP and create a CalDAV event by hand
  3. Have the frontend POST /remote.php/dav/... when the user clicks Place Order

All three work. All three are wrong for this case, because they treat NC Calendar as a destination you push to. OpenRegister already exposes every schema as a calendar. You're not pushing anywhere; you're declaring that the order schema is a calendar source.

The same pattern applies to:

IntegrationSchema annotationWhat you get
NC CalendarcalendarProvider blockRead-only virtual calendar per schema
NC Activity(automatic, free)CRUD events in the activity stream + dashboard widget + email digest
NC Mail Smart PickerlinkedTypes: ["email"]Paste a pet URL into NC Mail and get a rich preview
NC ContactslinkedTypes: ["contact"]/api/contacts/match returns matching pet owners

Anti-pattern: hand-coding <App>CalendarSync.vue, <App>NotesService, <App>MailSidebar.vue. Use the providers.

Step 1: Add the calendarProvider block

Open lib/Settings/petstore_register.json and add a configuration block to the order schema:

"order": {
  "slug": "order",
  "title": "Order",
  /* ... existing properties ... */

  "configuration": {
    "calendarProvider": {
      "enabled": true,
      "dtstart": "shipDate",
      "titleTemplate": "Order #{id} · {pet}",
      "color": "#003a8c"
    }
  }
}

The four fields:

  • enabled: true exposes the schema as a virtual calendar.
  • dtstart: name of the property that holds the event start. We use the shipDate property already on every order.
  • dtend: optional. When missing (as here), OpenRegister falls back to a sensible default (next-hour for date-time, end-of-day for date-only). Orders are point-in-time events, no end needed.
  • titleTemplate: string template for the event summary. Tokens in {braces} are resolved against the object's properties. We use Order #{id} · {pet}. To resolve relations (the pet property is a uuid pointing at a pet object), include _extend=relations when the calendar provider fetches. See the OpenRegister side.
  • color: hex colour of the calendar. We use cobalt-blue (--c-blue-cobalt).

That's the whole change. No other file needs to be touched.

Step 2: Re-import the configuration

Schemas in OpenRegister have a version. Bumping the schema version + re-running the import is the canonical way to update them in place. Bump the file's info.version (0.2.00.3.0) and the order schema's version:

"info":  { "version": "0.3.0" },
"order": { "version": "0.3.0", /* ... */ }

Then trigger the import the same way as Part 2:

docker exec nextcloud php occ app:disable petstore
docker exec nextcloud php occ app:enable petstore

(Or hit POST /api/settings/load if you've wired admin settings yet.)

Step 3: Verify in NC Calendar

Open /apps/calendar/. You should see a new entry in the calendar list:

  • Order: cobalt-blue colour, read-only badge, owned by your user

Click it. The seed orders from Part 2 (the Monday ship-date for Order #1 'doggie', the Tuesday ship-date for Order #2 'tabby', the Wednesday ship-date for Order #3 'koi') appear as events. Click one. The popover shows the order id + pet title + the ship date.

If you don't see the calendar:

  • Confirm OpenRegister registered its provider: docker exec nextcloud php occ config:list | grep openregister
  • Make sure your order schema has configuration.calendarProvider.enabled: true after the re-import
  • Check nextcloud.log for RegisterCalendarProvider messages

How it works under the hood

OpenRegister's RegisterCalendarProvider implements Nextcloud's ICalendarProvider interface. On every Calendar API request, it:

  1. Lists every schema with configuration.calendarProvider.enabled === true
  2. For each, returns a RegisterCalendar (a lightweight ICalendar implementation)
  3. When the user opens that calendar, queries OpenRegister for objects where dtstart is in the requested range
  4. Transforms each object into an iCalendar VEVENT via CalendarEventTransformer, applying the titleTemplate

Per-object ACLs gate event content. The provider bypasses multi-tenancy when listing schemas (so cross-tenant calendars work), but every individual event is still gated by RBAC.

The same pattern is followed by the spec.

What you've now bought yourself

Activity (NC Activity) is already wired. Browse /apps/activity/ and you'll see your order creates / updates. The dashboard activity widget + email digest both show PetStore events. Zero code on your end.

Mail Smart Picker too: in NC Mail, paste a pet URL (/apps/petstore/pets/pet-doggie) and the picker resolves it into a rich preview card with the pet name + category + status.

You've effectively integrated PetStore into four Nextcloud apps with one JSON block. That's the schema-driven discipline paying off.

Troubleshooting

What's next

Volgende stappen