mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 01:20:07 -05:00
Fix #6508: two console crashes in the import "map users later" workflow.
A) Import "map members": clicking a searched user threw `TypeError: Cannot read properties of undefined (reading '_id')` at `click .js-select-import`. The handler read `Template.currentData()._id`, which is undefined when the click lands on a CHILD of the anchor (the avatar or the name span). Read the WeKan user id from the anchor's `data-id` (event.currentTarget) instead, and guard it — robust regardless of which inner node was clicked. B) Admin Panel / People: saving an IMPORTED (placeholder) user as an active person with an email threw `TypeError: Cannot read properties of undefined (reading '0')`. The profile submit handler read `user.emails[0].verified` (and `.address`), but an imported placeholder user (and some SSO users) have no `emails` array at all. Read the primary email defensively (missing OR empty array) via a `primaryEmail` guard. Tests: tests/importMapUserFixes.test.cjs; tests/importMemberMapping.test.cjs updated for the data-id mapping. Thanks to AmigaAbattoir and xet7 ! Fixes #6508,
This commit is contained in:
1 parent
1697584693
commit
2f07b29336
4 files changed
+69
-13
No files matched your search
@@ -682,11 +682,16 @@ function importMapToUser(wekanId) {
|
||||
}
|
||||
|
||||
Template.importMapMembersAddPopup.events({
|
||||
'click .js-select-import'(event, tpl) {
|
||||
// The clicked search result is a WeKan user document; map to its _id. (Was
|
||||
// `__originalId`, which is never set on the results, so mapping silently did
|
||||
// nothing and the suggestion could not be selected — #6493-adjacent import fix.)
|
||||
importMapToUser(Template.currentData()._id);
|
||||
'click .js-select-import'(event) {
|
||||
// Map to the WeKan user _id carried on the .js-select-import anchor's data-id.
|
||||
// Reading it from the anchor (event.currentTarget) is robust to the click
|
||||
// landing on a child node (the avatar or the name span), where
|
||||
// Template.currentData() is undefined and `._id` threw "Cannot read properties
|
||||
// of undefined (reading '_id')" (#6508). (`data-id` is set to {{_id}} in the
|
||||
// template; the earlier fix from `__originalId` to `_id` was correct but read
|
||||
// the wrong context.)
|
||||
const id = event.currentTarget.getAttribute('data-id');
|
||||
if (id) importMapToUser(id);
|
||||
},
|
||||
// Enter selects the first (highlighted) search result, so a name can be assigned
|
||||
// by keyboard without a mouse click.
|
||||
|
||||
@@ -1376,15 +1376,20 @@ Template.editUserPopup.events({
|
||||
const isChangePassword = password.length > 0;
|
||||
const isChangeUserName = username !== user.username;
|
||||
const isChangeInitials = initials.length > 0;
|
||||
const isChangeEmailVerified = verified !== user.emails[0].verified;
|
||||
|
||||
// If previously email address has not been set, it is undefined,
|
||||
// check for undefined, and allow adding email address.
|
||||
// An imported (placeholder) user, and some SSO users, have NO `emails` array at
|
||||
// all, so `user.emails[0]` threw "Cannot read properties of undefined (reading
|
||||
// '0')" when an admin gave such a user an email in Admin Panel / People (#6508).
|
||||
// Read the primary email defensively (missing array OR empty array).
|
||||
const primaryEmail =
|
||||
Array.isArray(user.emails) && user.emails.length ? user.emails[0] : null;
|
||||
const isChangeEmailVerified =
|
||||
verified !== (primaryEmail ? primaryEmail.verified : undefined);
|
||||
|
||||
// If no email was set before, allow adding one (compare against `false`).
|
||||
const isChangeEmail =
|
||||
email.toLowerCase() !==
|
||||
(typeof user.emails !== 'undefined'
|
||||
? user.emails[0].address.toLowerCase()
|
||||
: false);
|
||||
(primaryEmail ? primaryEmail.address.toLowerCase() : false);
|
||||
|
||||
// Build user teams list
|
||||
let userTeamsList = userTeams.split(",");
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
// Regression for #6508 (import / map imported users later): two console-crash bugs.
|
||||
// A) Import "map members": clicking a search result threw
|
||||
// "Cannot read properties of undefined (reading '_id')" at click .js-select-import,
|
||||
// because it read Template.currentData()._id — undefined when the click lands on a
|
||||
// child node (the avatar or the name span). Read the _id from the anchor's data-id.
|
||||
// B) Admin Panel / People: saving an IMPORTED (placeholder) user as active with an
|
||||
// email threw "Cannot read properties of undefined (reading '0')" because the
|
||||
// submit handler read user.emails[0] and a placeholder user has no `emails` array.
|
||||
// Run: node tests/importMapUserFixes.test.cjs
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let passed = 0;
|
||||
function check(name, fn) { fn(); passed += 1; console.log(' ok -', name); }
|
||||
const read = rel => fs.readFileSync(path.join(__dirname, '..', rel), 'utf8');
|
||||
|
||||
check('A: .js-select-import maps by the anchor data-id, not Template.currentData()', () => {
|
||||
const js = read('client/components/import/import.js');
|
||||
const at = js.indexOf("'click .js-select-import'");
|
||||
assert.ok(at > -1, 'the select-import handler exists');
|
||||
const block = js.slice(at, at + 700);
|
||||
assert.ok(/event\.currentTarget\.getAttribute\('data-id'\)/.test(block),
|
||||
'reads the _id from the anchor data-id');
|
||||
assert.ok(/if \(id\) importMapToUser\(id\)/.test(block), 'guards the id before mapping');
|
||||
assert.ok(!/Template\.currentData\(\)\._id/.test(js),
|
||||
'must not read _id from Template.currentData() (undefined on a child click)');
|
||||
});
|
||||
|
||||
check('B: People submit reads the primary email defensively (no unguarded emails[0])', () => {
|
||||
const js = read('client/components/settings/peopleBody.js');
|
||||
assert.ok(/Array\.isArray\(user\.emails\) && user\.emails\.length \? user\.emails\[0\] : null/.test(js),
|
||||
'primary email is read defensively (missing / empty emails array)');
|
||||
assert.ok(/primaryEmail \? primaryEmail\.verified : undefined/.test(js), 'verified compares against the guarded email');
|
||||
assert.ok(/primaryEmail \? primaryEmail\.address\.toLowerCase\(\) : false/.test(js), 'address compares against the guarded email');
|
||||
// The old unguarded access must be gone.
|
||||
assert.ok(!/verified !== user\.emails\[0\]\.verified/.test(js), 'the unguarded user.emails[0].verified access is removed');
|
||||
});
|
||||
|
||||
console.log(`\nimportMapUserFixes: ${passed} checks passed`);
|
||||
@@ -30,9 +30,12 @@ test('selecting a searched user maps its _id (not the never-set __originalId)',
|
||||
// The click handler maps the clicked user document by _id.
|
||||
const clickIdx = js.indexOf("'click .js-select-import'");
|
||||
assert.ok(clickIdx > -1, 'the select-import click handler exists');
|
||||
const clickBlock = js.slice(clickIdx, clickIdx + 600);
|
||||
const clickBlock = js.slice(clickIdx, clickIdx + 1000);
|
||||
assert.ok(/mapSelectedMember|importMapToUser/.test(clickBlock), 'the click maps the selected member');
|
||||
assert.ok(/Template\.currentData\(\)\._id/.test(js), 'maps the search result by its _id');
|
||||
// #6508: read the _id from the anchor's data-id (robust to a click on a child
|
||||
// node), not Template.currentData() which is undefined there and threw.
|
||||
assert.ok(/getAttribute\('data-id'\)/.test(clickBlock), 'maps the search result by its data-id');
|
||||
assert.ok(!/Template\.currentData\(\)\._id/.test(js), 'must not read _id from Template.currentData() (child-click undefined)');
|
||||
});
|
||||
|
||||
test('Enter selects the first search result (keyboard, no mouse needed)', () => {
|
||||
|
||||
Reference in new issue
Block a user