Refactor and handle legacy login-update-profile.ftl
This commit is contained in:
parent
a6f7f8ff49
commit
027c8f38d8
@ -70,7 +70,7 @@ export type KcContextLike = {
|
|||||||
attributes: Attribute[];
|
attributes: Attribute[];
|
||||||
html5DataAnnotations?: Record<string, string>;
|
html5DataAnnotations?: Record<string, string>;
|
||||||
};
|
};
|
||||||
passwordRequired: boolean;
|
passwordRequired?: boolean;
|
||||||
realm: { registrationEmailAsUsername: boolean };
|
realm: { registrationEmailAsUsername: boolean };
|
||||||
passwordPolicies?: PasswordPolicies;
|
passwordPolicies?: PasswordPolicies;
|
||||||
url: {
|
url: {
|
||||||
@ -122,8 +122,19 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
"documentTitle": undefined
|
"documentTitle": undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const attributesWithPassword = useMemo(() => {
|
const { getErrors } = useGetErrors({
|
||||||
const attributesWithPassword: Attribute[] = [];
|
kcContext,
|
||||||
|
i18n
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialState = useMemo((): internal.State => {
|
||||||
|
// NOTE: We don't use te kcContext.profile.attributes directly because
|
||||||
|
// they don't includes the password and password confirm fields and we want to add them.
|
||||||
|
// Also, we want to polyfill the attributes for older Keycloak version before User Profile was introduced.
|
||||||
|
// Finally we want to patch the changes made by Keycloak on the attributes format so we have an homogeneous
|
||||||
|
// attributes format to work with.
|
||||||
|
const syntheticAttributes = (() => {
|
||||||
|
const syntheticAttributes: Attribute[] = [];
|
||||||
|
|
||||||
const attributes = (() => {
|
const attributes = (() => {
|
||||||
retrocompat_patch: {
|
retrocompat_patch: {
|
||||||
@ -131,8 +142,9 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
break retrocompat_patch;
|
break retrocompat_patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
kcContext.profile = {
|
if ("register" in kcContext && kcContext.register instanceof Object && "formData" in kcContext.register) {
|
||||||
"attributes": (["firstName", "lastName", "email", "username"] as const)
|
//NOTE: Handle legacy register.ftl page
|
||||||
|
return (["firstName", "lastName", "email", "username"] as const)
|
||||||
.filter(name => (name !== "username" ? true : !kcContext.realm.registrationEmailAsUsername))
|
.filter(name => (name !== "username" ? true : !kcContext.realm.registrationEmailAsUsername))
|
||||||
.map(name =>
|
.map(name =>
|
||||||
id<Attribute>({
|
id<Attribute>({
|
||||||
@ -155,16 +167,41 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
})
|
})
|
||||||
),
|
);
|
||||||
"html5DataAnnotations": {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return kcContext.profile.attributes;
|
if ("user" in kcContext && kcContext.user instanceof Object) {
|
||||||
})();
|
//NOTE: Handle legacy login-update-profile.ftl
|
||||||
|
return (["username", "email", "firstName", "lastName"] as const)
|
||||||
|
.filter(name => (name !== "username" ? true : (kcContext as any).user.editUsernameAllowed))
|
||||||
|
.map(name =>
|
||||||
|
id<Attribute>({
|
||||||
|
"name": name,
|
||||||
|
"displayName": id<`\${${MessageKey}}`>(`\${${name}}`),
|
||||||
|
"required": true,
|
||||||
|
"value": (kcContext as any).user[name] ?? "",
|
||||||
|
"html5DataAnnotations": {},
|
||||||
|
"readOnly": false,
|
||||||
|
"validators": {},
|
||||||
|
"annotations": {},
|
||||||
|
"autocomplete": (() => {
|
||||||
|
switch (name) {
|
||||||
|
case "email":
|
||||||
|
return "email";
|
||||||
|
case "username":
|
||||||
|
return "username";
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (const attribute_pre_group_patch of attributes) {
|
assert(false, "Unable to mock user profile from the current kcContext");
|
||||||
const attribute = (() => {
|
}
|
||||||
|
|
||||||
|
return kcContext.profile.attributes.map(attribute_pre_group_patch => {
|
||||||
if (typeof attribute_pre_group_patch.group === "string" && attribute_pre_group_patch.group !== "") {
|
if (typeof attribute_pre_group_patch.group === "string" && attribute_pre_group_patch.group !== "") {
|
||||||
const { group, groupDisplayHeader, groupDisplayDescription, groupAnnotations, ...rest } =
|
const { group, groupDisplayHeader, groupDisplayDescription, groupAnnotations, ...rest } =
|
||||||
attribute_pre_group_patch as Attribute & {
|
attribute_pre_group_patch as Attribute & {
|
||||||
@ -186,9 +223,11 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
return attribute_pre_group_patch;
|
return attribute_pre_group_patch;
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
attributesWithPassword.push(attribute);
|
for (const attribute of attributes) {
|
||||||
|
syntheticAttributes.push(attribute);
|
||||||
|
|
||||||
add_password_and_password_confirm: {
|
add_password_and_password_confirm: {
|
||||||
if (!kcContext.passwordRequired) {
|
if (!kcContext.passwordRequired) {
|
||||||
@ -201,7 +240,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
break add_password_and_password_confirm;
|
break add_password_and_password_confirm;
|
||||||
}
|
}
|
||||||
|
|
||||||
attributesWithPassword.push(
|
syntheticAttributes.push(
|
||||||
{
|
{
|
||||||
"name": "password",
|
"name": "password",
|
||||||
"displayName": id<`\${${MessageKey}}`>("${password}"),
|
"displayName": id<`\${${MessageKey}}`>("${password}"),
|
||||||
@ -230,16 +269,77 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributesWithPassword;
|
return syntheticAttributes;
|
||||||
}, []);
|
})();
|
||||||
|
|
||||||
const { getErrors } = useGetErrors({
|
const initialFormFieldState = (() => {
|
||||||
kcContext,
|
const out: { attribute: Attribute; valueOrValues: string | string[] }[] = [];
|
||||||
i18n
|
|
||||||
|
for (const attribute of syntheticAttributes) {
|
||||||
|
handle_multi_valued_attribute: {
|
||||||
|
if (!attribute.multivalued) {
|
||||||
|
break handle_multi_valued_attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = attribute.values ?? [""];
|
||||||
|
|
||||||
|
apply_validator_min_range: {
|
||||||
|
if (attribute.annotations.inputType?.startsWith("multiselect")) {
|
||||||
|
break apply_validator_min_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validator = attribute.validators.multivalued;
|
||||||
|
|
||||||
|
if (validator === undefined) {
|
||||||
|
break apply_validator_min_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { min: minStr } = validator;
|
||||||
|
|
||||||
|
if (minStr === undefined) {
|
||||||
|
break apply_validator_min_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
const min = parseInt(minStr);
|
||||||
|
|
||||||
|
for (let index = values.length; index < min; index++) {
|
||||||
|
values.push("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
attribute,
|
||||||
|
"valueOrValues": values
|
||||||
});
|
});
|
||||||
|
|
||||||
const [state, dispatchFormAction] = useReducer(
|
continue;
|
||||||
function reducer(state: internal.State, params: FormAction): internal.State {
|
}
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
attribute,
|
||||||
|
"valueOrValues": attribute.value ?? ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const initialState: internal.State = {
|
||||||
|
"formFieldStates": initialFormFieldState.map(({ attribute, valueOrValues }) => ({
|
||||||
|
attribute,
|
||||||
|
"errors": getErrors({
|
||||||
|
"attributeName": attribute.name,
|
||||||
|
"formFieldStates": initialFormFieldState
|
||||||
|
}),
|
||||||
|
"hasLostFocusAtLeastOnce": valueOrValues instanceof Array ? valueOrValues.map(() => false) : false,
|
||||||
|
"valueOrValues": valueOrValues
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
return initialState;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [state, dispatchFormAction] = useReducer(function reducer(state: internal.State, params: FormAction): internal.State {
|
||||||
const formFieldState = state.formFieldStates.find(({ attribute }) => attribute.name === params.name);
|
const formFieldState = state.formFieldStates.find(({ attribute }) => attribute.name === params.name);
|
||||||
|
|
||||||
assert(formFieldState !== undefined);
|
assert(formFieldState !== undefined);
|
||||||
@ -302,75 +402,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
},
|
}, initialState);
|
||||||
useMemo(function getInitialState(): internal.State {
|
|
||||||
const initialFormFieldState = (() => {
|
|
||||||
const out: { attribute: Attribute; valueOrValues: string | string[] }[] = [];
|
|
||||||
|
|
||||||
for (const attribute of attributesWithPassword) {
|
|
||||||
handle_multi_valued_attribute: {
|
|
||||||
if (!attribute.multivalued) {
|
|
||||||
break handle_multi_valued_attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = attribute.values ?? [""];
|
|
||||||
|
|
||||||
apply_validator_min_range: {
|
|
||||||
if (attribute.annotations.inputType?.startsWith("multiselect")) {
|
|
||||||
break apply_validator_min_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validator = attribute.validators.multivalued;
|
|
||||||
|
|
||||||
if (validator === undefined) {
|
|
||||||
break apply_validator_min_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { min: minStr } = validator;
|
|
||||||
|
|
||||||
if (minStr === undefined) {
|
|
||||||
break apply_validator_min_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
const min = parseInt(minStr);
|
|
||||||
|
|
||||||
for (let index = values.length; index < min; index++) {
|
|
||||||
values.push("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push({
|
|
||||||
attribute,
|
|
||||||
"valueOrValues": values
|
|
||||||
});
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push({
|
|
||||||
attribute,
|
|
||||||
"valueOrValues": attribute.value ?? ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const initialState: internal.State = {
|
|
||||||
"formFieldStates": initialFormFieldState.map(({ attribute, valueOrValues }) => ({
|
|
||||||
attribute,
|
|
||||||
"errors": getErrors({
|
|
||||||
"attributeName": attribute.name,
|
|
||||||
"formFieldStates": initialFormFieldState
|
|
||||||
}),
|
|
||||||
"hasLostFocusAtLeastOnce": valueOrValues instanceof Array ? valueOrValues.map(() => false) : false,
|
|
||||||
"valueOrValues": valueOrValues
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
return initialState;
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const formState: FormState = useMemo(
|
const formState: FormState = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user