The release notes provide information on the features and improvements in the specified version. The release dates that are related to the version of the release are published in the Curriculum/Workload Management release schedule.
A better look and feel
We are working through Curriculum page by page, improving the look and feel, the layout and the keyboard and screen reader support as we go. A rebuilt page keeps the configuration and the permissions of the page it replaces; what changes is that it is easier to read, easier to work in, and more consistent across our suite.
Planning an advised route through a study - CUR-3196, CUR-3194
An advised route is a recommended path through a study programme — the route a student can follow without having to work out every choice for themselves. Institutions use them to distinguish a September intake from a February intake, or to lay out the paths that lead to different qualifications, and a study normally has several. A route is built from steps, each pointing at a module or a module group and placing it in a phase and an offering.
Two pages carry this. The Planner, at /study/{id}/planner, is where a route is built: it shows the study's module group tree, you switch groups and modules on or off, choose each module's offering and phase, and save the result as the route. The route overview is the read-only result, laid out across the academic calendar; it is a page template an administrator adds to a study. Both were reworked in this release, and the overview now follows the same plan layout as the TimeEdit Study Planner, so the two read the same way. This is where the advised routes themselves are defined: today they leave Curriculum through the API and the CSV import and export, and in time they will be available in the Study Planner as well.
On the Planner:
- Required groups and modules are preselected and locked, with a tooltip saying so, instead of only carrying an asterisk, so they can no longer fall out of a route by accident.
- A new Selection rules panel gathers the credits and the qualifications the selection reaches, with the module groups each qualification still requires and a met or not-met status beside it. The rules are stated in words rather than left to colour alone.
- Credit totals now match what is on screen. A group over its own maximum, or one whose children missed their rules, had its credits silently discarded, so a study could read 30 out of 60 with far more than that selected underneath. Totals now count everything selected, and a group can read as not satisfying its rule while its credits still count.
- Collapsing a group no longer empties its totals, a group can be opened to look at without selecting it, and there is a select all and deselect all per group plus expand and collapse all.
- A page can be configured to show extra module fields as columns, each of which also becomes a filter next to Period and Phase.
- Leaving with unsaved changes now asks properly, offering Cancel, Discard or Save.
- A custom field configured on a route is now stored. The edit window reported success and the value was gone on reopen; those values now also travel with the CSV import and export and the API.
On the route overview, the table of part of day against period is replaced by a card per phase, a column per period inside it, and a card per planned module showing its code, its name, its part of day and a link to it. Credits are now shown, per module and as a total in each period's heading; a module spanning several periods splits its credits evenly across them.



Creating and editing a team with custom fields - CUR-4279
The teams administration page at /admin/teams was rebuilt. Creating or editing a team now shows every field configured on the Team object type, now with custom fields included; the previous window listed a fixed set of eight fields, so anything else you added was never shown.
Creating a team goes through the same window as the other create actions, so a create page configured on the object type applies to it too, and editing asks for a motivation where the custom type requires one. The page gained a search box, an Active, Inactive and All filter, and a code that links through to the team.
The team's own tabs and the teams page that can be added to another object keep the previous window for now.

Activity groups can be defined per offering - CUR-4192
Groups are how the students taking an activity are divided into parallel deliveries — practical session groups, for example, where a lecture normally has one group and a practical has several. Until now a group belonged to an activity serie, and the number of groups was a single number on the teaching method, so every offering of that method got the same groups. A method offered to 150 students in one semester and 20 in the next could not express that: with a maximum group size of 25 the first needs six groups and the second one.
Groups can now be defined on the teaching method while still differing per offering. The number is worked out for each offering separately, from a formula on the number-of-groups field that can read the offering's own figures — for example the number of students on the offering divided by the maximum group size, rounded up.
For instance, a formula like this can now be configured on the method's numberOfGroups field:
ceil(:offering.totalExpectedParticipants / :maxGroupSize)
Groups are worked out when the groups page is opened and whenever the offering changes, and a workflow step can generate them for every active offering in its period.
Turn it on under Administration -> Configuration by setting schedule.generate_method_groups to true. It is off by default.
- With the setting off nothing changes. Groups are generated on the activity serie exactly as before, with the same count, the same numbering and the same values, and existing groups keep the activity serie they belong to.
- Assessments are never affected. Assessment series keep owning their own groups.
- Groups defined per offering stay inside Curriculum. They appear on the groups page and in the activity group report, but they are not part (yet) of the CSV import and export or the export to Activity Manager, which can only carry a group that belongs to an activity serie. If you run one of those integrations, leave the setting off for now.
The activity groups page and the activity group report were rebuilt at the same time, and that part applies to everyone. Each group is now opened and saved on its own in a window, rather than every group being edited at once behind one save button, and the list is ordered by group number instead of in no particular order. Lowering the number of groups no longer removes what was entered: the groups above the new count are marked as removed and the change history keeps their values. Raising the number again brings back an empty group rather than one carrying values entered for something else — to the person looking at it, a group that reappears is a group nobody has filled in yet.
Improved object admin tabs - CUR-3912
The /admin tab of fourteen objects — study, module, module group, faculty, organisation, specification, qualification, assessment, method, assignment, person, team, rule and definition — now all look and work the same way.
Each object used to carry its own hard-coded list on top of the fields you configured: team and organisation showed a fixed set and nothing else, person left out every field an institution had added, and the other objects forced a handful of fields out of sight. The Admin tab now shows all the visible fields configured on the object type, in the order and with the visibility you set on them. Four fields are the exception and are always shown here, whatever the configuration says, because they are what this page is for: the UID, the publication date, the import date and the dispatch date. Those four are now read-only, so they can be read and copied but not edited.
The actions moved into the header of the page - Import, Export, Invoke hook, Restore and Save, with Delete and Delete (+ History) under Actions beside them - and each is shown only where it applies.
Simulations - CUR-4111
My simulations at /simulations, Create simulation and Edit simulation were rebuilt. The Simulations group in the academic year menu no longer runs off the bottom of the screen when there are many simulations: it scrolls, and it opens upwards where there is not enough room below. New simulation and Manage simulations moved to the top of that menu, and in the simulation lists each row's edit and delete button now says which row it belongs to, which matters when working with the keyboard or a screen reader.
Working with a removed academic year was tightened up at the same time. Deleting an academic year now clears its default flag, so a deleted year is no longer the configured default and a restored one has to be chosen again. Every switch into a year refuses a year that has been removed, including Back to original on a simulation whose origin year no longer exists. Deleting the simulation you are working in returns you to the default academic year, and when the default year itself has been removed the application falls back to a year you can select instead of starting with no year at all.
Improvements
New functionality, user experience improvements and bug fixes. Issues marked as Hotfix were developed in this sprint but applied to the previous release or directly to production, depending on impact.
A read-only field with HTML text could not be read - CUR-4162 (Hotfix)
A field can be read only because of its own settings, or because a user does not have the permission to edit it. In the second case an HTML or Markdown field was drawn as a disabled input box one line high, so the text could not be read. It is now shown as formatted, read-only text that grows to fit. One consequence: a Markdown field a user may not edit now shows the formatted text rather than the Markdown source.
Saving could fail for a user who is not an administrator - CUR-4284, CUR-4294, CUR-4251 (Hotfix)
On a page that creates a new additional, a user who is not an administrator could get an error on save, with no explanation and nothing stored. It happened only where that page holds fields the user is not allowed to edit: those fields are reset on save, and the reset collided with reading the same set of fields. An administrator may edit everything, so the problem never appeared for one. We've also fixed some other issues related to creating objects.
The Save button stayed inactive on a create window with nothing left to fill in - CUR-4253 (Hotfix)
This happened only when every field on the window was already filled by the default values configured on the object type, so there was nothing for the user to change: the form counted as untouched and Save stayed disabled. Clearing a value and entering the same one again did enable it. A create window that is complete and valid can now be submitted without typing anything. One that is incomplete or invalid still cannot be submitted, as before.
The create window did not close when it opened an edit workflow - CUR-4228 (Hotfix)
On an object type whose Action after create is set to Open popup, configured at Administration, Custom types, General, the edit workflow opened behind the create window and could not be reached. The create window now closes before the workflow opens.
A cost page can be configured without the option to create modules - CUR-4239 (Hotfix)
On the study-cost page, the window for adding a module offers New and New version next to Existing. Institutions that add only existing modules there can now switch both create tabs off: set hideModuleCreate to true on the study-cost page template in the page editor. A version counts as a new module, so both tabs disappear together. The option is off by default.
Copying activities from another offering - CUR-4221 (Hotfix)
The Method schema tab of a module shows its teaching methods and the activities attached to each, and Copy takes the activities of another offering. Three things went wrong. The Amount in the copy window counted the module's own activity series rather than the set about to be copied, so it could read 0 and block the button. A copy that met one row it could not create stopped there and silently left the rest uncopied. And the activity series of a method without a code were skipped altogether, while the copy reported success. All three are fixed, and the copy now reports how many activity series it created. The same Copy window is used on the appraisals tab and on the activity page templates.
Form validation on a submit button is clearer and reachable with the keyboard - CUR-4221 (Hotfix)
On every form in the application, a submit button that cannot yet be used keeps its place in the keyboard order instead of dropping out of it. It still looks and behaves as unavailable, and it now explains which fields are blocking the submit, so the reason reaches anyone working with the keyboard or a screen reader and not only someone who can see the whole form.
Advanced filters for new reports - CUR-4139
The Changes, Assignments, Competences and Applicants reports share one component, so this applies to all four. Each column now has its own filter beside its search box, offering Contains, Exact, Empty and Not empty. Not empty is new; the other three behave as they did. The search box suggests values that occur in that column, eight at a time, saying how many were left out, and boolean columns offer all four options again: Yes, No, No or undefined, and Undefined.
Two things work differently. The Search button in the toolbar is replaced by a Clear button, shown only while something is filtered: the old button cleared the filters when it hid them, so a report opened from a template with stored filters could come back short with nothing on screen to explain why. And because setting a filter has to load every page of the report, there is now a panel above the table showing how far it has got against the total, with a Cancel. The result is kept for five minutes, so typing does not start the loading again.
On the Assignments report the name search moved into the Name column itself. That one is answered by the server, so it is the first filter on these reports that does not have to load the whole report first. It matches the assignment's code and the person's name as well as the name in the column.

The Changes pages can be configured for consulting instead of reviewing - CUR-4264
The changes-list and changes-report page templates each take two new parameters. Set hide review buttons to true to remove Approve change and Reject change, the bulk toolbar and the selection boxes, while leaving the comments, the replies and the change status untouched. Set read only to true for that plus the reply action. This helps where the right to approve cannot be taken away by permission because an integration depends on it, and the page should still be open for discussion.

Changes recorded on a version are now shown in every academic year - CUR-2694
For institutions using versioning. A change recorded on a study or module that is not bound to an academic year kept the year it happened to be made in, so it appeared under that one year while the version itself belongs to every year. This has been fixed, and existing changes are corrected when this version is deployed. A change on an offering keeps its academic year, because an offering belongs to one.
The Changes report of such a study or module also opened empty, because it was limited to the selected academic year. The Limited to year switch now starts off there, and on for a faculty or an organisation, where every change carries a year and the year is the useful limit. The switch itself works as before.
The version switcher now always shows which academic year and which version you are looking at, instead of hiding the choice when there was only one to make, and the version selector appears only on the object types that can hold versions: study, module group and module. How a version is marked as active is unchanged — a version is active when its own start and end dates overlap the academic year you are viewing, not the date of today.
See Curriculum: versioning configuration and usage for the full configuration.
Linking a module to a module group is now shown as a change - CUR-3999
Linking a module under a module group takes effect immediately, but the link never appeared among the pending changes on the Changes pages while Hide empty was set, and opening it showed nothing. A link carries no field values — it is the connection between two objects, not a value that went from one thing to another — and both the list and the detail treated "no values" as "nothing happened".
Adding a link and removing one are now changes in their own right and appear on both. Where there are no field values to show, the detail states that the object was created instead of reporting "No changes compared to the original". A change on the relation between a study and a module is now also named after the module it concerns, rather than carrying a generic heading.
A new module could end up in no module group - CUR-4271
This happened on the Structure tab of a study or module group, and only where the module's object type has Action after create set to Open popup. Add module creates the module and then writes the link that puts it in the group; with that setting the edit workflow opens in between and waits for an answer, and the link was only written once the user had given one. A user who closed the tab or navigated away instead — reasonably, since the module was visibly created and reported as created — left the module in no group. The link is now written before the workflow opens.
The roll over now copies the data on the relation between a study and a module - CUR-4265
A custom field can be configured on the relation between a study and a module inside a module group, for example to record whether a module is condonable. The copy script behind the roll over did not include that relation, so those values came out empty in the new academic year. It now copies them, and so does the script that clones a simulation. Roll overs performed before this version were made with the previous script, so values recorded on the study-module relation will be missing in the years they produced.
The same gap also made deleting fail: removing an academic year, a simulation, a module or a module group could be refused outright once these relations existed, because nothing removed them first. That is fixed as well.
Credits of a module that is not offered are no longer counted - CUR-4160
On the Structure tab of a study or module group, a module that is part of a group but has no offering in the selected academic year — an alternating module taught every other year, for instance — still added its credits to the group total. One rule now decides whether a module counts: it does not when it is terminated, when it has no active offering at all, or, on pages that mark inactive offerings, when it has no offering left in the selected period. The same rule decides whether the module is struck through, so what is shown as not taught and what is counted now agree. The configurable structure page is covered too.
The Changes pages are easier to read and to work with - CUR-4184
The Changes report, the change drawer and the changes list were reworked. The drawer now opens with General, then Revisions, then the revision you selected, with Approve change and Reject change directly under the properties of that revision and Comments before Statuses.
Selecting a revision moves the highlighted row in the report with it, so the two stay in step, and the status history uses the same row layout as the revisions.

Saving a competence or an applicant no longer clears the fields the window does not show - CUR-4092
Editing one field on a competence or applicant record could empty the fields the window does not display: the external identifier, the code, the validity dates, and any custom field configured on the object type but not placed on that window. Those values are now sent back unchanged. The rebuilt Admin tabs described above use the same form and are covered by the same fix.
Definitions can carry custom fields - CUR-4092
A definition groups the versions of a study, module group or module under one identifier. As part of the wider work on versioning, a definition can now carry a custom field of its own — an identifier from a source system, for example — kept on its Admin tab, through the reference API and through the reference CSV import. Before this a definition had nowhere to put one: the Admin tab reported a successful save and the value was dropped.
The Versions table on /definition/{id}/general now also marks the version that is active in the academic year you are viewing, rather than the one active today, which is how the version switcher has always read it.
A cost report could fail for a study whose module groups have no description - CUR-4273
Module group descriptions were read under a name that had changed, so they were always empty, and the report failed outright for a study whose groups have no default description. This has been fixed, and the descriptions are read and shown again.
Documents can be stored on a person, and stay visible when you switch academic year - CUR-4181
Adding a document through a competence or an applicant workflow failed. A document can now be stored on any object. A person's documents are also no longer filtered by academic year, so they stay visible when you switch year, which matches the way person and competence data already works.
Purging a deleted academic year now removes the documents that were uploaded on that year's objects as well. Before, the purge left them behind, and because they no longer belonged to a year they then appeared in every year.
Teacher remarks are shown in the Staff availability report and in the API - CUR-4031, CUR-4033, CUR-4114
On the availability preference page a teacher records when they are available to teach, and can add a remark. Schedulers could not see which teachers had left one, because the report showed only the day and time-block choices. Remarks is now a column in the details of the Staff availability report and on the Schedule preferences tab of a person, sortable like the other columns and shortened in the cell with the full text on hover. A remark is recorded once per person and period, so it is the same on every row for that person in that period. The Staff availability report is now also sorted by name by default, rather than by an internal identifier.
The same remark was added to the availability preference endpoint, so an integration receives what the report shows. It is optional: a preference without a remark leaves the field out rather than sending an empty value.

Integration
Extensions, improvements and fixes to the integrations: the Curriculum API, OOAPI, CSV import, and the data exchange with Data Manager, Core and the broker.
Loading studies, module groups, modules and reference data in bulk - CUR-4194
Loading a large set of objects meant one call per object. Four new endpoints accept a list instead — /import/v2/study/upsert, /import/v2/module-group/upsert, /import/v2/module/upsert and /import/v2/reference/upsert — taking up to 25 objects per request and answering with a result per object next to the totals, so a partial success can be acted on. Each object is created or updated as a whole, and a module group whose structure cannot be applied is no longer stored in part.
The Import permission is now also enough on its own to import any reference type. Four of them, among them the study type and the module type, additionally required administrator rights, so an integration account holding only Import was refused. See developer.timeedit.com for the endpoints.
Group filters can be sent as a structured model - CUR-4246, CUR-4236
A module group can draw its modules from a filter rather than an explicit list. Over the API that filter was one line of expression syntax, which is awkward to build from an integration and cannot be described in the API documentation. It is now a list of filters under groupFilters and moduleFilters, each with a name, an operator and a value, so an integration works against fields instead of assembling syntax. Numbers are recognised as numbers, filters can be combined with and, or and not, and there are now in and not in operators for matching a value against a set.
This is groundwork for the TimeEdit Study Planner, which will resolve a group's modules from the same filter. The structured form is the API's only; in Curriculum the filter is still edited as an expression on the module group, and what is stored has not changed.
An organisation can now be set on a specification and on a qualification - CUR-4072
A study and a module can be linked to a department through their organisation, and processes follow that link to find the related roles. A specification and a qualification had no such field, so a custom field had to stand in for one and processes at that level could not find the department. Both now carry an organisation next to the faculty they already had — organisationId on the object, an organisation reference in the canonical API, and a column in the CSV import and export that takes the organisation's external identifier or its code.
Worth knowing before you fill it in: access rules and role propagation follow the organisation, so setting one on a specification or qualification lets everyone holding a role on that organisation reach it. It only ever widens access, never restricts it, and the same field is used to resolve process notification recipients and the responsible person for a process. Check your role assignments on the organisation before populating the field across a catalogue.
Sending teacher availability to Core no longer stops at the first failure - CUR-4233 (Hotfix)
Availability is sent to Core with Invoke hook, from the report of a process or from a process step. It failed for most rows while part of the data did arrive, which left the export impossible to repeat because Core already held what had been sent. Two separate defects caused it: a successful answer from Core could not be read at all, because it leaves a field empty where a number was expected, and a batch that met a failure stopped the batches behind it. Both are fixed — the remaining batches are now sent, and the failures are reported as one readable line rather than an unreadable error.
Security
Our build process scans continuously for known vulnerabilities. Fixes are prioritised by impact: some go out as an immediate hotfix, others in the current or next sprint. See the central database of vulnerabilities for details.
Implemented security fixes - CUR-4214
Change and feedback data is now returned only to users who may view changes on the object concerned. Every remaining endpoint that reads it, including the report cells that render change content, refuses a user who has been denied access rather than answering with an empty result.
View feedback now includes view changes. A reviewer holding only View feedback sees the Changes tabs, the changes list and the Changes report together with the review discussion; a user holding only View changes sees what changed but not the discussion.
Continued investment in tooling and quality
Ongoing improvements to our automated testing, build pipeline and dependency scanning, so errors and security issues are caught earlier.
See the Curriculum manual for configuration guidance.