Skip to content

Fix 342 Pair Programming Bug Fixes - #345

Open
pdji1602003 wants to merge 16 commits into
devfrom
fix-342-pp_bug_fixes
Open

Fix 342 Pair Programming Bug Fixes#345
pdji1602003 wants to merge 16 commits into
devfrom
fix-342-pp_bug_fixes

Conversation

@pdji1602003

@pdji1602003 pdji1602003 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Main Description

This pull request contains fix to issues found during the last pair programming session, which list as follows:

  1. Moodle URL/settings values are not trimmed before saving: when a Moodle API URL or similar scalar setting is saved with leading/trailing whitespace, CARE persists the value as-is. If the Moodle API URL has a trailing space, Moodle-related requests use the malformed URL and fail.
  2. Moodle user import cannot map campus Moodle role labels: CARE currently relies on fixed role matching during Moodle user import. This works for the CS Moodle role labels, but the campus Moodle returns different role strings, including multilingual Moodle markup.
  3. The Result step in the Import users via Moodle modal feels confusing: After successfully importing users in the Import modal, the Result step only only shows the success message, but also shows form fields and two action buttons at the button, which feel confusing.

const { assertStableEmailTemplateContent } = require("../../utils/helper/templateResolver");

const MAIL_SERVICE_KEY_PREFIX = "system.mailService.";
const PRESERVE_WHITESPACE_SETTING_TYPES = new Set(["edits", "text"]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PRESERVE_WHITESPACE_SETTING_TYPES lists the types to skip, so shouldTrimSetting trims every type that is not in the set. Any setting type added later, and any entry whose type could not be resolved, gets trimmed by default without anyone noticing. Flip it to an allow-list of types that should be trimmed (for example "string", "number", "select", "color"), so a new type has to opt in.

* @returns {Promise<Map<string, string>>}
*/
async function getSettingTypeByKey(Setting, settings, options = {}) {
if (typeof Setting.findAll !== "function") {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both callers of saveSettings pass a real Sequelize model, so Setting.findAll is always a function and this branch never runs. If it ever did run it would return an empty Map, and every setting would fall back to being trimmed, including the "text" and "edits" types this PR wants to protect. Drop the guard, or throw here, rather than silently changing how values are saved.

*/
async bulkCreateUsers(data) {
const users = data["users"];
const roleMap = data["roleMap"];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

roleMap comes from the client, and assignUserRoles in backend/db/models/user.js assigns any role name it gets. A caller can send roleMap: {"Student*in": "admin"} together with an existing user's email and make that account an admin, because userBulkCreate has no isAdmin() check (the other handlers in this class do have one). Please guard the handler with if (!(await this.isAdmin())) throw ... and drop any mapped value that is not in the allowed role list, since the "no admin" filter in RoleMappingStep.vue only applies to the dropdown.

</template>
<!-- Step1: Upload -->
<template #step-1>
<template #step="{ step }">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All five steps now render from one slot in this file, which the lint job already flags at 642 lines (the limit is 300). RoleMappingStep.vue in this PR shows the shape that works here. Please move the preview, confirm and result blocks into their own step components as well, and keep this template down to picking which one to show.

computed: {
careRoleOptions() {
return [
...this.systemRoles.filter((role) => !role.deleted && role.name !== "admin").map((role) => ({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

role.deleted is never set on these objects. sendSystemRoles in backend/webserver/sockets/app.js sends only id and name, so this check removes nothing and soft-deleted roles still appear in the dropdown. Add where: {deleted: false} to that findAll and drop the !role.deleted part here.

value: role.name,
label: role.name.charAt(0).toUpperCase() + role.name.slice(1),
})),
{ value: "", label: "Do not assign additional role" }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option sounds like the user keeps the roles they already have, but that is not what happens. When an existing user is imported again, assignUserRoles in backend/db/models/user.js first deletes all their roles and then adds back only user plus the mapped ones. Please rename it to something like "No extra CARE role" and add a note on this step that old roles are removed.


Moodle role labels can vary by Moodle instance and may include multilingual markup such as ``{mlang de}Lehrende{mlang}{mlang other}Lecturer{mlang}``. CARE hides this markup in the mapping step while keeping the original Moodle role string for the import.

CSV user import also includes a role mapping step. CARE reads the distinct values from the CSV ``roles`` column and asks you to map each value to a CARE role before previewing the import. Multiple roles in one CSV cell should be separated by commas.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CARE docs name the role instead of addressing the reader, so "asks you to map" does not match the rest of this page. Please write "asks the admin to map each value to a CARE role".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants