Templating System (Developer Guide)
This document explains how the templating system works end-to-end for developers: data model, server endpoints, client flows, and expected copy semantics.
Conceptβ
A template is a first-class course record with template = true. It acts as a blueprint to create new courses. Creating from a template performs a deep copy of the original courseβs configuration.
Data Model and Constraintsβ
course.template(boolean) marks a course as a template.- Migrations support template courses: e.g.,
0015_check_dates_template.up.sqlallowsend_dateto be null for template courses. - Name/SemesterTag validation on the client prevents
-in either field. - ECTS is fixed for some course types (Seminar = 5, Practical = 10) via UI rules; Lecture requires manual input.
Server (Core) Endpointsβ
All routes below are in servers/core and use Gin with permission middleware.
- GET
/api/courses/templateβ List template courses- Implementation:
course/router.go#getTemplateCoursesβcourse/service.go#GetTemplateCourses - Admins get all template courses; Lecturers see only templates they have roles for (restricted query).
- Implementation:
- GET
/api/courses/:uuid/templateβ Check if a course is a template- Implementation:
course/router.go#checkCourseTemplateStatusβservice.CheckCourseTemplateStatus
- Implementation:
- PUT
/api/courses/:uuid/templateβ Mark/unmark a course as a template- Implementation:
course/router.go#updateCourseTemplateStatusβservice.UpdateCourseTemplateStatus - DB:
MarkCourseAsTemplate/UnmarkCourseAsTemplateindb/query/template.sql
- Implementation:
- POST
/api/courses/:uuid/copyβ Copy a course (optionally creating a template)- Implementation:
course/copy/router.go#copyCourseβcopy.copyCourseInternal - See Copy Semantics below.
- Implementation:
- GET
/api/courses/:uuid/copyableβ Verify all phases expose/copyendpoint and are reachable.
Copy Semantics (servers/core/course/copy/copy_course.go)β
When copying with Template = false:
- Creates a new course with given Name, SemesterTag, StartDate, EndDate
- Copies:
- Course phases and their order (phase graph)
- Phase/data/participation graphs
- DTO mappings and meta graphs
- Application form if both source and target course have an application phase
- Student-readable and restricted metadata
- CourseType and ECTS
- Creates Keycloak roles/groups for the new course
When copying with Template = true:
- Same as above, but:
- StartDate and EndDate are omitted (NULL dates)
- The new course is marked as template (
MarkCourseAsTemplate)
Error handling: Runs within a DB transaction; partial failures roll back. Some operations (e.g., phase configuration copy) are executed after commit and may still return errors up the call chain.
Server (Assessment) β Assessment Templatesβ
Separate from course templates, the assessment service exposes assessment template management:
- Router:
servers/assessment/assessmentTemplates/router.go - Service:
servers/assessment/assessmentTemplates/service.go - DTOs:
assessmentTemplateDTO/*
Endpoints under /assessment-template (Prompt Admin for mutating routes):
- GET
/assessment-templateβ List - GET
/assessment-template/:templateIDβ Get by ID - POST
/assessment-templateβ Create - PUT
/assessment-template/:templateIDβ Update - DELETE
/assessment-template/:templateIDβ Delete
Use GetCoursePhasesByAssessmentTemplate to find course phases using a given assessment template.
Client (Core) Flowβ
Key files under clients/core:
- Course creation choices UI:
managementConsole/courseOverview/AddingCourse/components/CourseCreationChoiceDialog.tsx- Offers: New Course, Create New Template, Use Template
- Create Template from scratch:
managementConsole/courseOverview/AddTemplateDialog.tsx- Properties form:
AddTemplateProperties.tsxwithtemplateFormSchema - Creates course via POST
/api/courses/withtemplate: true
- Properties form:
- Make Template from existing course:
managementConsole/courseSettings/components/CourseTemplateToggle.tsx- Opens
CopyCourseDialogwithcreateTemplate=true useTemplateFormβ validates name/semester tag- Backend call: POST
/api/courses/:id/copywithTemplate = true
- Opens
- Use Template to create course:
TemplateSelectionDialog.tsx- Queries templates with
getTemplateCourses(GET/api/courses/template) - On select β opens
CopyCourseDialog(useTemplateCopy = true,createTemplate = false) - Backend call: POST
/api/courses/:templateId/copywithTemplate = false
- Queries templates with
Networking helpers:
src/network/queries/getTemplateCourses.tssrc/network/queries/checkCourseTemplateStatus.tssrc/network/mutations/updateCourseTemplateStatus.tssrc/network/hooks/useCopyCourse.tssrc/network/hooks/useTemplateForm.ts
Validations:
src/validations/template.tsandmakeTemplateCourse.tsenforce name and semester tag rules and ECTS input constraints.
Permissionsβ
- Listing templates: Prompt Admin, Course Lecturer
- Creating templates (from scratch or from existing course): Prompt Admin, Course Lecturer
- Using templates to create a course: Prompt Admin, Course Lecturer
The server additionally filters template visibility for non-admins based on the userβs course roles.
Edge Cases and Notesβ
- Name/SemesterTag uniqueness and format are enforced downstream; UI prevents hyphens and empty values.
- If the source course lacks an application phase, the application form is not copied (no-op).
- Copyability check:
/copyableverifies all phases implement copy; UI shows a confirmation step. - After creating a course (or template), the client refreshes Keycloak token and refetches courses to ensure new roles are loaded for navigation.
Extending the Systemβ
- To add fields that should be copied, update
copy_course.goand the DTO/metadata conversion utilities. - To expose new template kinds (e.g., per-phase templates), add appropriate endpoints in the respective service and mirror the client flows.