diff --git a/.changeset/fifty-lamps-fail.md b/.changeset/fifty-lamps-fail.md new file mode 100644 index 00000000000..095e9d7071d --- /dev/null +++ b/.changeset/fifty-lamps-fail.md @@ -0,0 +1,8 @@ +--- +"@wso2is/common.branding.v1": minor +"@wso2is/admin.branding.v1": minor +"@wso2is/identity-apps-core": minor +"@wso2is/i18n": minor +--- + +Add the username recovery multiple channel support with branding. diff --git a/.changeset/real-carpets-press.md b/.changeset/real-carpets-press.md deleted file mode 100644 index b45d8c1f1b7..00000000000 --- a/.changeset/real-carpets-press.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@wso2is/myaccount": minor -"@wso2is/core": patch -"@wso2is/i18n": patch ---- - -Add multi-valued email, mobile support to my account diff --git a/features/admin.branding.v1/components/preview/branding-preference-preview.tsx b/features/admin.branding.v1/components/preview/branding-preference-preview.tsx index 56253e0b5f2..1b6cc1d8b4a 100644 --- a/features/admin.branding.v1/components/preview/branding-preference-preview.tsx +++ b/features/admin.branding.v1/components/preview/branding-preference-preview.tsx @@ -270,7 +270,10 @@ export const BrandingPreferencePreview: FunctionComponent { diff --git a/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-channel-selection-fragment.tsx b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-channel-selection-fragment.tsx new file mode 100644 index 00000000000..75de20da30b --- /dev/null +++ b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-channel-selection-fragment.tsx @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IdentifiableComponentInterface } from "@wso2is/core/models"; +import React, { FunctionComponent, ReactElement } from "react"; +import { CustomTextPreferenceConstants } from "../../../../constants/custom-text-preference-constants"; +import useBrandingPreference from "../../../../hooks/use-branding-preference"; + +/** + * Proptypes for the username-recovery-channel-selection fragment of login screen skeleton. + */ +export type UsernameRecoveryChannelSelectionFragmentInterface = IdentifiableComponentInterface; + +/** + * Username recovery channel selection fragment component for the branding preview of Username Recovery box. + * + * @param props - Props injected to the component. + * @returns Username recovery fragment component. + */ +const UsernameRecoveryChannelSelectionFragment: FunctionComponent = ( + props: UsernameRecoveryChannelSelectionFragmentInterface +): ReactElement => { + const { ["data-componentid"]: componentId = "username-recovery-channel-selection" } = props; + + const { i18n } = useBrandingPreference(); + + return ( +
+

{ i18n(CustomTextPreferenceConstants. + TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_CHANNEL_SELECTION.HEADING, "Recovery Username") } +

+

+ { i18n( + CustomTextPreferenceConstants.TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_CHANNEL_SELECTION.BODY, + "Select a recovery option." + ) } +

+
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+ +
+ +
+ +
+ + +
+
+
+ ); +}; + +export default UsernameRecoveryChannelSelectionFragment; diff --git a/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-claims-fragment.tsx b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-claims-fragment.tsx new file mode 100644 index 00000000000..de02911cb7b --- /dev/null +++ b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-claims-fragment.tsx @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IdentifiableComponentInterface } from "@wso2is/core/models"; +import React, { FunctionComponent, ReactElement } from "react"; +import { CustomTextPreferenceConstants } from "../../../../constants/custom-text-preference-constants"; +import useBrandingPreference from "../../../../hooks/use-branding-preference"; + +/** + * Proptypes for the username-recovery-claims fragment of login screen skeleton. + */ +export type UsernameRecoveryClaimsFragmentInterface = IdentifiableComponentInterface; + +/** + * Username recovery claim fragment component for the branding preview of Username Recovery box. + * + * @param props - Props injected to the component. + * @returns Username recovery fragment component. + */ +const UsernameRecoveryClaimsFragment: FunctionComponent = ( + props: UsernameRecoveryClaimsFragmentInterface +): ReactElement => { + const { ["data-componentid"]: componentId } = props; + + const { i18n } = useBrandingPreference(); + + return ( +
+

+ { i18n(CustomTextPreferenceConstants. + TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_CLAIM.HEADING, "Username Recovery") } +

+

+ { i18n( + CustomTextPreferenceConstants.TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_CLAIM.BODY, + "Enter below details to recover your username." + ) } +

+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+
+ +
+ +
+
+
+ ); +}; + +/** + * Default proptypes for the component. + */ +UsernameRecoveryClaimsFragment.defaultProps = { + "data-componentid": "username-recovery-claims-fragment" +}; + +export default UsernameRecoveryClaimsFragment; diff --git a/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-success/username-recovery-success-email-fragment.tsx b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-success/username-recovery-success-email-fragment.tsx new file mode 100644 index 00000000000..bffa7c05cd3 --- /dev/null +++ b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-success/username-recovery-success-email-fragment.tsx @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AppState } from "@wso2is/admin.core.v1/store"; +import { BrandingPreferenceInterface } from "@wso2is/common.branding.v1/models"; +import { IdentifiableComponentInterface } from "@wso2is/core/models"; +import React, { FunctionComponent, ReactElement } from "react"; +import { useSelector } from "react-redux"; +import { CustomTextPreferenceConstants } from "../../../../../constants/custom-text-preference-constants"; +import useBrandingPreference from "../../../../../hooks/use-branding-preference"; + +/** + * Proptypes for the username-recovery-success-email fragment of login screen skeleton. + */ +interface UsernameRecoverySuccessEmailFragmentInterface extends IdentifiableComponentInterface { + brandingPreference: BrandingPreferenceInterface; +} + +/** + * Username recovery success email fragment component for the branding preview of Username Recovery box. + * + * @param props - Props injected to the component. + * @returns Username recovery fragment component. + */ +const UsernameRecoverySuccessEmailFragment: FunctionComponent = ( + props: UsernameRecoverySuccessEmailFragmentInterface +): ReactElement => { + const { + brandingPreference, + ["data-componentid"]: componentId = "username-recovery-success-email" + } = props; + + const { i18n } = useBrandingPreference(); + const supportEmail: string = useSelector((state: AppState) => { + return state.config.deployment.extensions?.supportEmail as string; + }); + + return ( +
+

{ i18n(CustomTextPreferenceConstants. + TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_SUCCESS_EMAIL.HEADING, "Check Your Email") } +

+

+ { i18n( + CustomTextPreferenceConstants.TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_SUCCESS_EMAIL.BODY, + "Username recovery details have been sent to the email provided. If the email is not " + + "received,try again using different information." + ) } +
+
+
+ + + { i18n( + CustomTextPreferenceConstants.TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_SUCCESS_EMAIL.BUTTON, + "Back to application" + ) } + +

+ For further assistance, write to +
+ + + { brandingPreference.organizationDetails.supportEmail || supportEmail } + + +

+

+
+ ); +}; + +export default UsernameRecoverySuccessEmailFragment; diff --git a/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-success/username-recovery-success-sms-fragement.tsx b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-success/username-recovery-success-sms-fragement.tsx new file mode 100644 index 00000000000..8ac1badd202 --- /dev/null +++ b/features/admin.branding.v1/components/preview/sign-in-box/fragments/username-recovery-success/username-recovery-success-sms-fragement.tsx @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AppState } from "@wso2is/admin.core.v1/store"; +import { BrandingPreferenceInterface } from "@wso2is/common.branding.v1/models"; +import { IdentifiableComponentInterface } from "@wso2is/core/models"; +import React, { FunctionComponent, ReactElement } from "react"; +import { useSelector } from "react-redux"; +import { CustomTextPreferenceConstants } from "../../../../../constants/custom-text-preference-constants"; +import useBrandingPreference from "../../../../../hooks/use-branding-preference"; + +/** + * Proptypes for the username-recovery-success-sms fragment of login screen skeleton. + */ +interface UsernameRecoverySuccessSmsFragmentInterface extends IdentifiableComponentInterface { + brandingPreference: BrandingPreferenceInterface; +} + +/** + * Username recovery success sms fragment component for the branding preview of Username Recovery box. + * + * @param props - Props injected to the component. + * @returns Username recovery fragment component. + */ +const UsernameRecoverySuccessSmsFragment: FunctionComponent = ( + props: UsernameRecoverySuccessSmsFragmentInterface +): ReactElement => { + const { + brandingPreference, + ["data-componentid"]: componentId = "username-recovery-success-email" + } = props; + + const { i18n } = useBrandingPreference(); + const supportEmail: string = useSelector((state: AppState) => { + return state.config.deployment.extensions?.supportEmail as string; + }); + + return ( +
+

{ i18n(CustomTextPreferenceConstants. + TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_SUCCESS_SMS.HEADING, "Check Your Mobile") } +

+

+ { i18n( + CustomTextPreferenceConstants.TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_SUCCESS_SMS.BODY, + "Username recovery details have been sent to the mobile number provided. If the SMS is not" + + "received, try again using different information." + ) } +
+
+
+ + + { i18n( + CustomTextPreferenceConstants.TEXT_BUNDLE_KEYS.USERNAME_RECOVERY_SUCCESS_SMS.BUTTON, + "Back to application" + ) } + +

+

+ For further assistance, write to +
+ + + { brandingPreference.organizationDetails.supportEmail || supportEmail } + + +

+
+ ); +}; + +export default UsernameRecoverySuccessSmsFragment; diff --git a/features/admin.branding.v1/components/preview/sign-in-box/sign-in-box.tsx b/features/admin.branding.v1/components/preview/sign-in-box/sign-in-box.tsx index 63a9b92c2b4..790132cc4b6 100644 --- a/features/admin.branding.v1/components/preview/sign-in-box/sign-in-box.tsx +++ b/features/admin.branding.v1/components/preview/sign-in-box/sign-in-box.tsx @@ -38,6 +38,13 @@ import PasswordResetSuccessFragment from "./fragments/password-reset-success-fra import SignUpFragment from "./fragments/sign-up-fragment"; import SMSOTPFragment from "./fragments/sms-otp-fragment"; import TOTPFragment from "./fragments/totp-fragment"; +import UsernameRecoveryChannelSelectionFragment from + "./fragments/username-recovery-channel-selection-fragment"; +import UsernameRecoveryClaimsFragment from "./fragments/username-recovery-claims-fragment"; +import UsernameRecoverySuccessEmailFragment from + "./fragments/username-recovery-success/username-recovery-success-email-fragment"; +import UsernameRecoverySuccessSmsFragment from + "./fragments/username-recovery-success/username-recovery-success-sms-fragement"; import useBrandingPreference from "../../../hooks/use-branding-preference"; /** @@ -61,7 +68,8 @@ const SignInBox: FunctionComponent = ( ): ReactElement => { const { - ["data-componentid"]: componentId + brandingPreference, + ["data-componentid"]: componentId = "login-screen-skeleton-login-box" } = props; const { selectedScreen, selectedScreenVariation } = useBrandingPreference(); @@ -95,6 +103,17 @@ const SignInBox: FunctionComponent = ( return ; } else if (selectedScreen === PreviewScreenType.EMAIL_LINK_EXPIRY) { return ; + } else if (selectedScreen === PreviewScreenType.USERNAME_RECOVERY_CLAIM) { + return ; + } else if (selectedScreen === PreviewScreenType.USERNAME_RECOVERY_CHANNEL_SELECTION) { + return ; + } else if (selectedScreen === PreviewScreenType.USERNAME_RECOVERY_SUCCESS) { + if (selectedScreenVariation === PreviewScreenVariationType.EMAIL || + selectedScreenVariation === PreviewScreenVariationType.BASE) { + return ; + } else if (selectedScreenVariation === PreviewScreenVariationType.SMS) { + return ; + } } }; @@ -105,11 +124,4 @@ const SignInBox: FunctionComponent = ( ); }; -/** - * Default props for the component. - */ -SignInBox.defaultProps = { - "data-componentid": "login-screen-skeleton-login-box" -}; - export default SignInBox; diff --git a/features/admin.branding.v1/components/screen-dropdown.tsx b/features/admin.branding.v1/components/screen-dropdown.tsx index dee0c95b5ac..0d051174ad9 100644 --- a/features/admin.branding.v1/components/screen-dropdown.tsx +++ b/features/admin.branding.v1/components/screen-dropdown.tsx @@ -16,11 +16,14 @@ * under the License. */ +import { AppState } from "@wso2is/admin.core.v1/store"; import { PreviewScreenType } from "@wso2is/common.branding.v1/models/branding-preferences"; import { IdentifiableComponentInterface } from "@wso2is/core/models"; import React, { FunctionComponent, ReactElement, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useSelector } from "react-redux"; import { DropdownProps, Form, Select } from "semantic-ui-react"; +import { BRANDING_PREVIEW_SCREEN_ID_PREFIX } from "../constants/preview-screen-constants"; /** * Prop types for the language dropdown component. @@ -66,6 +69,9 @@ const ScreenDropdown: FunctionComponent = ( onChange(defaultScreen); }, [ defaultScreen ]); + const disabledBrandingFeatures: string[] = useSelector((state: AppState) => + state?.config?.ui?.features?.branding?.disabledFeatures) || []; + const supportedScreens: { key: string; text: string; @@ -75,7 +81,8 @@ const ScreenDropdown: FunctionComponent = ( return []; } - return screens.map((screen: string) => { + return screens.filter((screen: string) => !disabledBrandingFeatures. + includes(BRANDING_PREVIEW_SCREEN_ID_PREFIX+screen)).map((screen: string) => { return { key: screen, text: t(`branding:screens.${ screen }`), diff --git a/features/admin.branding.v1/constants/custom-text-preference-constants.ts b/features/admin.branding.v1/constants/custom-text-preference-constants.ts index 71f4d9f33e1..5c5da78472e 100644 --- a/features/admin.branding.v1/constants/custom-text-preference-constants.ts +++ b/features/admin.branding.v1/constants/custom-text-preference-constants.ts @@ -47,55 +47,55 @@ export class CustomTextPreferenceConstants { CUSTOM_TEXT_PREFERENCE_FETCH_INVALID_STATUS_CODE_ERROR: IdentityAppsError; CUSTOM_TEXT_PREFERENCE_UPDATE_ERROR: IdentityAppsError; CUSTOM_TEXT_PREFERENCE_UPDATE_INVALID_STATUS_CODE_ERROR: IdentityAppsError; - } = { - CUSTOM_TEXT_PREFERENCE_DELETE_ERROR: new IdentityAppsError( - CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_DELETE_ERROR_CODE, - "An error occurred while deleting the Text Customizations for the requested resource.", - "Error while deleting Text Customizations", - null - ), - CUSTOM_TEXT_PREFERENCE_DELETE_INVALID_STATUS_CODE_ERROR: new IdentityAppsError( - CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_DELETE_INVALID_STATUS_CODE_ERROR_CODE, - "Received an invalid status code while deleting the Text Customizations for the requested resource.", - "Invalid Error Code while deleting Text Customizations", - null - ), - CUSTOM_TEXT_PREFERENCE_FETCH_ERROR: new IdentityAppsError( - CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_FETCH_ERROR_CODE, - "An error occurred while fetching the Text Customizations for the requested resource.", - "Error while fetching Text Customizations", - null - ), - CUSTOM_TEXT_PREFERENCE_FETCH_INVALID_STATUS_CODE_ERROR: new IdentityAppsError( - CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_FETCH_INVALID_STATUS_CODE_ERROR_CODE, - "Received an invalid status code while fetching the Text Customizations for the requested resource.", - "Invalid Error Code while fetching Text Customizations", - null - ), - CUSTOM_TEXT_PREFERENCE_UPDATE_ERROR: new IdentityAppsError( - CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_UPDATE_ERROR_CODE, - "An error occurred while updating the Text Customizations for the requested resource.", - "Error while updating Text Customizations", - null - ), - CUSTOM_TEXT_PREFERENCE_UPDATE_INVALID_STATUS_CODE_ERROR: new IdentityAppsError( - CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_UPDATE_INVALID_STATUS_CODE_ERROR_CODE, - "Received an invalid status code while updating the Text Customizations for the requested resource.", - "Invalid Error Code while updating Text Customizations", - null - ) - }; +} = { + CUSTOM_TEXT_PREFERENCE_DELETE_ERROR: new IdentityAppsError( + CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_DELETE_ERROR_CODE, + "An error occurred while deleting the Text Customizations for the requested resource.", + "Error while deleting Text Customizations", + null + ), + CUSTOM_TEXT_PREFERENCE_DELETE_INVALID_STATUS_CODE_ERROR: new IdentityAppsError( + CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_DELETE_INVALID_STATUS_CODE_ERROR_CODE, + "Received an invalid status code while deleting the Text Customizations for the requested resource.", + "Invalid Error Code while deleting Text Customizations", + null + ), + CUSTOM_TEXT_PREFERENCE_FETCH_ERROR: new IdentityAppsError( + CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_FETCH_ERROR_CODE, + "An error occurred while fetching the Text Customizations for the requested resource.", + "Error while fetching Text Customizations", + null + ), + CUSTOM_TEXT_PREFERENCE_FETCH_INVALID_STATUS_CODE_ERROR: new IdentityAppsError( + CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_FETCH_INVALID_STATUS_CODE_ERROR_CODE, + "Received an invalid status code while fetching the Text Customizations for the requested resource.", + "Invalid Error Code while fetching Text Customizations", + null + ), + CUSTOM_TEXT_PREFERENCE_UPDATE_ERROR: new IdentityAppsError( + CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_UPDATE_ERROR_CODE, + "An error occurred while updating the Text Customizations for the requested resource.", + "Error while updating Text Customizations", + null + ), + CUSTOM_TEXT_PREFERENCE_UPDATE_INVALID_STATUS_CODE_ERROR: new IdentityAppsError( + CustomTextPreferenceConstants.CUSTOM_TEXT_PREFERENCE_UPDATE_INVALID_STATUS_CODE_ERROR_CODE, + "Received an invalid status code while updating the Text Customizations for the requested resource.", + "Invalid Error Code while updating Text Customizations", + null + ) +}; /** * Text customization form element constraints. */ public static readonly FORM_FIELD_CONSTRAINTS: { - MAX_LENGTH: number; - MIN_LENGTH: number; - } = { - MAX_LENGTH: 1024, - MIN_LENGTH: 0 - }; + MAX_LENGTH: number; + MIN_LENGTH: number; + } = { + MAX_LENGTH: 1024, + MIN_LENGTH: 0 + }; public static readonly FORM_ID: string = "branding-preference-custom-text-form"; @@ -116,6 +116,42 @@ export class CustomTextPreferenceConstants { } } }; + USERNAME_RECOVERY_CLAIM: { + HEADING: string; + BODY: string; + IDENTIFIER: { + INPUT: { + LABEL: string; + PLACEHOLDER: string; + } + }, + BUTTON: { + NEXT: string; + CANCEL: string; + } + }; + USERNAME_RECOVERY_CHANNEL_SELECTION: { + HEADING: string; + BODY: string; + RADIO_BUTTON: { + EMAIL: string; + SMS: string; + }, + BUTTON: { + NEXT: string; + CANCEL: string; + } + }; + USERNAME_RECOVERY_SUCCESS_EMAIL : { + HEADING: string; + BODY: string; + BUTTON: string; + }; + USERNAME_RECOVERY_SUCCESS_SMS : { + HEADING: string; + BODY: string; + BUTTON: string; + }; PASSWORD_RECOVERY: { HEADING: string; BODY: string; @@ -220,6 +256,42 @@ export class CustomTextPreferenceConstants { TERMS_OF_SERVICE: "terms.of.service", TOTP: { HEADING: "totp.heading" + }, + USERNAME_RECOVERY_CHANNEL_SELECTION: { + BODY: "username.recovery.channel.selection.body", + BUTTON: { + CANCEL: "username.recovery.channel.selection.cancel.button", + NEXT: "username.recovery.channel.selection.next.button" + }, + HEADING: "username.recovery.channel.selection.heading", + RADIO_BUTTON: { + EMAIL: "send.username.via.email", + SMS: "send.username.via.sms" + } + }, + USERNAME_RECOVERY_CLAIM: { + BODY: "username.recovery.body", + BUTTON: { + CANCEL: "username.recovery.cancel.button", + NEXT: "username.recovery.next.button" + }, + HEADING: "username.recovery.heading", + IDENTIFIER: { + INPUT: { + LABEL: "contact", + PLACEHOLDER: "contact" + } + } + }, + USERNAME_RECOVERY_SUCCESS_EMAIL: { + BODY: "username.recovery.email.success.body", + BUTTON: "username.recovery.success.action", + HEADING: "username.recovery.email.success.heading" + }, + USERNAME_RECOVERY_SUCCESS_SMS: { + BODY: "username.recovery.sms.success.body", + BUTTON: "username.recovery.success.action", + HEADING: "username.recovery.sms.success.heading" } }; } diff --git a/features/admin.branding.v1/constants/preview-screen-constants.ts b/features/admin.branding.v1/constants/preview-screen-constants.ts new file mode 100644 index 00000000000..611a6a5aa86 --- /dev/null +++ b/features/admin.branding.v1/constants/preview-screen-constants.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Feature keys. +export const BRANDING_PREVIEW_SCREEN_ID_PREFIX: string = "branding.preview.screens."; diff --git a/features/admin.branding.v1/models/custom-text-preference.ts b/features/admin.branding.v1/models/custom-text-preference.ts index b0d847f9835..dd38d816af3 100644 --- a/features/admin.branding.v1/models/custom-text-preference.ts +++ b/features/admin.branding.v1/models/custom-text-preference.ts @@ -126,5 +126,8 @@ export const BASE_DISPLAY_VARIATION :Record +# +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.heading=Recover Username +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.body=Enter below details to recover your username. +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +contact=Email or mobile +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.next.button=Next +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.cancel.button=Cancel +# + +# +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.heading=Recover Username +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.body=Select a recovery option +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +send.username.via.email=Send Username via Email +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +send.username.via.sms=Send Username via SMS +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.next.button=Next +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.cancel.button=Cancel +# + +# +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=false,VARIATIONS="email" +username.recovery.email.success.heading=Check Your Email +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=false,VARIATIONS="sms" +username.recovery.sms.success.heading=Check Your Mobile +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=true,VARIATIONS="email" +username.recovery.email.success.body=Username recovery details have been sent to the email provided. If the email is not received, try again using different information. +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=true,VARIATIONS="sms" +username.recovery.sms.success.body=Username recovery details have been sent to the mobile number provided. If the SMS is not received, try again using different information. +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=false +username.recovery.success.action=Back to application +# + Wso2.identity.server=WSO2 Identity Server All.rights.reserved=All Rights Reserved. Inc=Inc @@ -130,12 +171,14 @@ Insufficient.info.to.find.user=Provided information is insufficient to identify No.recovery.supported.claims.found=No recovery supported claims found Unknown.password.recovery.option=Unknown Password Recovery Option Username.cannot.be.empty=Username cannot be empty. +Contact.cannot.be.empty=Contact cannot be empty. +Invalid.contact=Invalid contact +Invalid.username.recovery.stage=Invalid username recovery stage. Account.confirmation.sent.to.your.email=Account confirmation has been sent to your email. Create.an.account=Create An Account Enter.required.fields.to.complete.registration=Enter required fields to complete registration of- Next=Next Already.have.an.account=Already have an account? -Username.recovery.information.sent.to.your.email=Username recovery information has been sent to your email. Try again with different attributes if the email is not received. Registered.user.not.found.in.session=Registered user not found in session. No.security.questions.found.to.recover.password.contact.system.administrator=No Security Questions Found to recover password. Please contact your system Administrator No.recovery.options.found=No Recovery Option Found to recover password. Please contact your system Administrator diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_de_DE.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_de_DE.properties index b8f3f47c937..40853a0593a 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_de_DE.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_de_DE.properties @@ -59,6 +59,31 @@ password.reset.success.action=Zurück zur Registrierung email.link.expiry.message=Dieser Link ist abgelaufen. # +# +username.recovery.heading=Benutzernamen wiederherstellen +username.recovery.body=Geben Sie die folgenden Details ein, um Ihren Benutzernamen wiederherzustellen. +contact=E-Mail oder Handy +username.recovery.next.button=Nächste +username.recovery.cancel.button=Stornieren +# + +# +username.recovery.channel.selection.heading=Benutzernamen wiederherstellen +username.recovery.channel.selection.body=Wählen Sie eine Wiederherstellungsoption +send.username.via.email=Benutzernamen per E-Mail senden +send.username.via.sms=Benutzernamen per SMS senden +username.recovery.channel.selection.next.button=Nächste +username.recovery.channel.selection.cancel.button=Stornieren +# + +# +username.recovery.email.success.header=Überprüfen Sie Ihre E-Mail +username.recovery.sms.success.header=Überprüfen Sie Ihr Mobiltelefon +username.recovery.email.success.body=Details zur Wiederherstellung des Benutzernamens wurden an die angegebene E-Mail-Adresse gesendet. Wenn Sie die E-Mail nicht erhalten, versuchen Sie es erneut mit anderen Informationen. +username.recovery.sms.success.body=Details zur Wiederherstellung des Benutzernamens wurden an die angegebene Mobiltelefonnummer gesendet. Wenn die SMS nicht empfangen wird, versuchen Sie es erneut mit anderen Informationen. +username.recovery.success.action=Zurück zur Bewerbung +# + Wso2.identity.server=WSO2 Identity Server All.rights.reserved=Alle Rechte vorbehalten. Inc=Inc. @@ -107,12 +132,14 @@ Insufficient.info.to.find.user=Bereitgestellte Informationen sind nicht ausreic No.recovery.supported.claims.found=Keine von der Wiederherstellung unterstützten Ansprüche gefunden Unknown.password.recovery.option=Option zur Wiederherstellung unbekannter Passwörter Username.cannot.be.empty=Der Benutzername darf nicht leer sein. +Contact.cannot.be.empty=Der Kontakt darf nicht leer sein. +Invalid.contact=Ungültiger Kontakt +Invalid.username.recovery.stage=Ugyldigt gendannelsestrin for brugernavn. Account.confirmation.sent.to.your.email=Die Kontobestätigung wurde an Ihre E -Mail gesendet. Create.an.account=Ein Konto erstellen Enter.required.fields.to.complete.registration=Füllen Sie die erforderlichen Felder aus, um die Registrierung abzuschliessen Next=Nächste Already.have.an.account=Haben Sie bereits ein Konto? -Username.recovery.information.sent.to.your.email=Informationen zur Wiederherstellung von Benutzername wurden an Ihre E-Mail gesendet. Versuchen Sie es erneut mit verschiedenen Attributen, wenn die E-Mail nicht empfangen wird. Registered.user.not.found.in.session=Registrierter Benutzer wurde in Sitzung nicht gefunden. No.security.questions.found.to.recover.password.contact.system.administrator=Keine Sicherheitsfragen zur Wiederherstellung des Passworts wurde gefunden. Bitte wenden Sie sich an Ihren Systemadministrator No.recovery.options.found=Keine Option zur Wiederherstellung des Passworts wurde gefunden. Bitte wenden Sie sich an Ihren Systemadministrator diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_es_ES.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_es_ES.properties index a4121dbee6e..c736297ff0e 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_es_ES.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_es_ES.properties @@ -59,6 +59,31 @@ password.reset.success.action=Volver a la aplicación email.link.expiry.message=Este enlace ha expirado. # +# +username.recovery.heading=Recuperar nombre de usuario +username.recovery.body=Ingrese los detalles a continuación para recuperar su nombre de usuario. +contact=Correo electrónico o móvil +username.recovery.next.button=Próxima +username.recovery.cancel.button=Cancelar +# + +# +username.recovery.channel.selection.heading=Recuperar nombre de usuario +username.recovery.channel.selection.body=Seleccione una opción de recuperación +send.username.via.email=Enviar nombre de usuario por correo electrónico +send.username.via.sms=Enviar nombre de usuario por SMS +username.recovery.channel.selection.next.button=Próxima +username.recovery.channel.selection.cancel.button=Cancelar +# + +# +username.recovery.email.success.header=Revisa tu correo electrónico +username.recovery.sms.success.header=Revisa tu móvil +username.recovery.email.success.body=Los detalles de recuperación del nombre de usuario se enviaron al correo electrónico proporcionado. Si no se recibe el correo electrónico, inténtelo nuevamente usando información diferente. +username.recovery.sms.success.body=Los detalles de recuperación del nombre de usuario se enviaron al número de teléfono móvil proporcionado. Si no se recibe el SMS, inténtelo nuevamente usando información diferente. +username.recovery.success.action=Volver a la aplicación +# + Wso2.identity.server=WSO2 Identity Server All.rights.reserved=Todos los derechos reservados. Inc=Inc @@ -107,12 +132,14 @@ Insufficient.info.to.find.user=La información proporcionada es insuficiente par No.recovery.supported.claims.found=No se encontraron reclamos respaldados por recuperación Unknown.password.recovery.option=Opción de recuperación de contraseña desconocida Username.cannot.be.empty=El nombre de usuario no puede estar vacío. +Contact.cannot.be.empty=El contacto no puede estar vacío +Invalid.contact=Contacto inválido +Invalid.username.recovery.stage=Etapa de recuperación de nombre de usuario no válida. Account.confirmation.sent.to.your.email=La confirmación de la cuenta se ha enviado a su correo electrónico. Create.an.account=Crea una cuenta Enter.required.fields.to.complete.registration=Ingrese los campos requeridos para completar el registro de Next=Siguiente Already.have.an.account=¿Ya tienes una cuenta? -Username.recovery.information.sent.to.your.email=La información de recuperación del nombre de usuario se ha enviado a su correo electrónico. Vuelva a intentarlo con diferentes atributos si no se recibe el correo electrónico. Registered.user.not.found.in.session=Usuario registrado no encontrado en la sesión. No.security.questions.found.to.recover.password.contact.system.administrator=No se encuentran preguntas de seguridad para recuperar la contraseña. Por favor, póngase en contacto con el administrador del sistema No.recovery.options.found=No se encuentra ninguna opción de recuperación para recuperar la contraseña. Por favor, póngase en contacto con el administrador del sistema diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_fr_FR.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_fr_FR.properties index c39e8052632..b5f4d2fd439 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_fr_FR.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_fr_FR.properties @@ -59,6 +59,31 @@ password.reset.success.action=Retour à la candidature email.link.expiry.message=Ce lien a expiré # +# +username.recovery.heading=Récupérer le nom d'utilisateur +username.recovery.body=Entrez les détails ci-dessous pour récupérer votre nom d'utilisateur. +contact=E-mail ou mobile +username.recovery.next.button=Suivante +username.recovery.cancel.button=Annuler +# + +# +username.recovery.channel.selection.heading=Récupérer le nom d'utilisateur +username.recovery.channel.selection.body=Sélectionnez une option de récupération +send.username.via.email=Envoyer le nom d'utilisateur par e-mail +send.username.via.sms=Envoyer le nom d'utilisateur par SMS +username.recovery.channel.selection.next.button=Suivante +username.recovery.channel.selection.cancel.button=Annuler +# + +# +username.recovery.email.success.header=Vérifiez votre courrier électronique +username.recovery.sms.success.header=Vérifiez votre mobile +username.recovery.email.success.body=Les détails de récupération du nom d'utilisateur ont été envoyés à l'adresse e-mail fournie. Si l'e-mail n'est pas reçu, réessayez en utilisant des informations différentes. +username.recovery.sms.success.body=Les détails de récupération du nom d'utilisateur ont été envoyés au numéro de mobile fourni. Si le SMS n’est pas reçu, réessayez en utilisant des informations différentes. +username.recovery.success.action=Retour à la candidature +# + Wso2.identity.server=WSO2 Identity Server All.rights.reserved=Tous droits réservés. Inc=Inc @@ -103,12 +128,14 @@ No.valid.user.found=Aucun utilisateur valide trouvé No.recovery.supported.claims.found= Pas d'informations de récupération trouvées Unknown.password.recovery.option=Option de récupération du mot de passe inconnue Username.cannot.be.empty=Le nom d utilisateur ne peut pas être vide. +Contact.cannot.be.empty=Le contact ne peut pas être vide +Invalid.contact=Contact invalide +Invalid.username.recovery.stage=Étape de récupération du nom d'utilisateur non valide. Account.confirmation.sent.to.your.email=Confirmation du compte envoyée sur votre adresse e-mail. Create.an.account=Créez un compte Enter.required.fields.to.complete.registration=Saisir les champs requis pour terminer l'enregistrement de Next=Suivant Already.have.an.account=Avez-vous déjà un compte? -Username.recovery.information.sent.to.your.email=Les informations de récupération de le nom d'utilisateur ont été envoyées à votre adresse mail. Essayer à nouveau avec des attributs différents si le mail n'est pas reçu. Registered.user.not.found.in.session=Utilisateur enregistré introuvable dans la session. No.security.questions.found.to.recover.password.contact.system.administrator=Pas de questions de sécurité trouvées pour récupérer le mot de passe. Veuillez vous adresser à votre administrateur système Username.is.missing=Nom d utilisateur manquant. diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_ja_JP.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_ja_JP.properties index 8bea76119fa..a8f6121386c 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_ja_JP.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_ja_JP.properties @@ -59,6 +59,31 @@ password.reset.success.action=アプリケーションに戻る email.link.expiry.message=このリンクは有効期限が切れています。 # +# +username.recovery.heading=ユーザー名を回復する +username.recovery.body=ユーザー名を回復するには、以下の詳細を入力してください。 +contact=電子メールまたは携帯電話 +username.recovery.next.button=次 +username.recovery.cancel.button=キャンセル +# + +# +username.recovery.channel.selection.heading=ユーザー名を回復する +username.recovery.channel.selection.body=回復オプションを選択してください +send.username.via.email=電子メールでユーザー名を送信 +send.username.via.sms=SMS 経由でユーザー名を送信 +username.recovery.channel.selection.next.button=次 +username.recovery.channel.selection.cancel.button=キャンセル +# + +# +username.recovery.email.success.header=メールを確認してください +username.recovery.sms.success.header=携帯電話を確認してください +username.recovery.email.success.body=ユーザー名の回復の詳細が、指定された電子メールに送信されました。メールが届かない場合は、別の情報を使用して再度お試しください。 +username.recovery.sms.success.body=ユーザー名の回復の詳細が、指定された携帯電話番号に送信されました。 SMS が受信されない場合は、別の情報を使用して再試行してください。 +username.recovery.success.action=アプリケーションに戻る +# + Wso2.identity.server=WSO2 アイデンティティサーバ All.rights.reserved=無断転載を禁じます。 Inc=Inc @@ -105,12 +130,14 @@ Insufficient.info.to.find.user=提供された情報はユーザーを特定す No.recovery.supported.claims.found=復元サポート対象クレームが見つかりません Unknown.password.recovery.option=不明パスワード復旧オプション Username.cannot.be.empty=ユーザー名は入力必須です。 +Contact.cannot.be.empty=連絡先を空にすることはできません +Invalid.contact=無効な連絡先 +Invalid.username.recovery.stage=ユーザー名の回復ステージが無効です。 Account.confirmation.sent.to.your.email=アカウント確認メールが送信されました。 Create.an.account=アカウントの作成 Enter.required.fields.to.complete.registration=必須項目を入力して-の登録を完了してください Next=次 Already.have.an.account=すでにアカウントをお持ちですか? -Username.recovery.information.sent.to.your.email=ユーザー名の再設定情報がメールアドレスに送信されました。メールが届かない場合は、別の属性でもう一度試してください。 Registered.user.not.found.in.session=セッションに登録ユーザーが見つかりません。 No.security.questions.found.to.recover.password.contact.system.administrator=パスワードを復旧するためのセキュリティの質問が見つかりません。システム管理者にお問い合わせください No.recovery.options.found=パスワードを復旧するための回復オプションが見つかりません。システム管理者にお問い合わせください diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_BR.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_BR.properties index 1b69fba3d1b..e1dba777d8b 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_BR.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_BR.properties @@ -59,6 +59,31 @@ password.reset.success.action=Voltar ao aplicativo email.link.expiry.message=Este link expirou. # +# +username.recovery.heading=Recuperar nome de usuário +username.recovery.body=Insira os detalhes abaixo para recuperar seu nome de usuário. +contact=E-mail ou celular +username.recovery.next.button=Próxima +username.recovery.cancel.button=Cancelar +# + +# +username.recovery.channel.selection.heading=Recuperar nome de usuário +username.recovery.channel.selection.body=Selecione uma opção de recuperação +send.username.via.email=Enviar nome de usuário por e-mail +send.username.via.sms=Enviar nome de usuário via SMS +username.recovery.channel.selection.next.button=Próxima +username.recovery.channel.selection.cancel.button=Cancelar +# + +# +username.recovery.email.success.header=Verifique seu e-mail +username.recovery.sms.success.header=Verifique seu celular +username.recovery.email.success.body=Os detalhes de recuperação do nome de usuário foram enviados para o e-mail fornecido. Se o e-mail não for recebido, tente novamente usando informações diferentes. +username.recovery.sms.success.body=Os detalhes de recuperação do nome de usuário foram enviados para o número de celular fornecido. Se o SMS não for recebido, tente novamente usando informações diferentes. +username.recovery.success.action=Voltar ao aplicativo +# + Wso2.identity.server=WSO2 Identity Server All.rights.reserved=Todos os direitos reservados. Inc=Inc @@ -107,12 +132,14 @@ Insufficient.info.to.find.user=As informações fornecidas são insuficientes pa No.recovery.supported.claims.found=Nenhuma claim de recuperação compatível foi encontrada Unknown.password.recovery.option=Opção de recuperação de senha desconhecida Username.cannot.be.empty=O nome de usuário não pode estar vazio. +Contact.cannot.be.empty=O contato não pode ficar vazio +Invalid.contact=Contato inválido +Invalid.username.recovery.stage=Estágio de recuperação de nome de usuário inválido Account.confirmation.sent.to.your.email=A confirmação da conta foi enviada para o seu e-mail. Create.an.account=Crie uma conta Enter.required.fields.to.complete.registration=Insira os campos necessários para concluir o registro de- Next=Próximo Already.have.an.account=Já tem uma conta? -Username.recovery.information.sent.to.your.email=As informações de recuperação de nome de usuário foram enviadas para o seu e-mail. Tente novamente com atributos diferentes se o email não for recebido. Registered.user.not.found.in.session=Usuário registrado não encontrado na sessão. No.security.questions.found.to.recover.password.contact.system.administrator=Nenhuma pergunta de segurança encontrada para recuperar a senha. Por favor, entre em contato com o administrador do seu sistema No.recovery.options.found=Nenhuma pergunta de segurança encontrada para recuperar a senha. Por favor, entre em contato com o administrador do seu sistema diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_PT.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_PT.properties index c5cb883ba47..002a9f74954 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_PT.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_pt_PT.properties @@ -59,6 +59,48 @@ password.reset.success.action=De volta ao aplicativo email.link.expiry.message=Este link expirou. # + +# +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.heading=Recuperar nome de utilizador +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.body=Introduza os detalhes abaixo para recuperar o seu nome de utilizador. +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +contact=E-mail ou telemóvel +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.next.button=Seguinte +# EDITABLE=true,SCREEN="username-recovery-claim",MULTI_LINE=false +username.recovery.cancel.button=Cancelar +# + +# +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.heading=Recuperar nome de utilizador +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.body=Selecione uma opção de recuperação +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +send.username.via.email=Enviar nome de utilizador por e-mail +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +send.username.via.sms=Enviar nome de utilizador via SMS +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.next.button=Seguinte +# EDITABLE=true,SCREEN="username-recovery-channel-selection",MULTI_LINE=false +username.recovery.channel.selection.cancel.button=Cancelar +# + +# +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=false +username.recovery.email.success.header=Verifique o seu e-mail +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=false +username.recovery.sms.success.header=Verifique o seu telemóvel +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=true +username.recovery.email.success.body=Os detalhes de recuperação do nome de utilizador foram enviados para o e-mail fornecido. Se o e-mail não for recebido, tente novamente utilizando informações diferentes. +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=true +username.recovery.sms.success.body=Os detalhes de recuperação do nome de utilizador foram enviados para o número de telemóvel fornecido. Se o SMS não for recebido, tente novamente utilizando informações diferentes. +# EDITABLE=true,SCREEN="username-recovery-success",MULTI_LINE=false +username.recovery.success.action=Voltar ao aplicativo +# + Wso2.identity.server=WSO2 Identity Server All.rights.reserved=Todos os direitos reservados. Inc=Inc @@ -107,12 +149,14 @@ Insufficient.info.to.find.user=As informações fornecidas são insuficientes pa No.recovery.supported.claims.found=Nenhuma reivindicação apoiada por recuperação encontrada Unknown.password.recovery.option=Opção de recuperação de senha desconhecida Username.cannot.be.empty=O nome de usuário não pode estar vazio. +Contact.cannot.be.empty=O contato não pode ficar vazio +Invalid.contact=Contato inválido +Invalid.username.recovery.stage=Estágio de recuperação de nome de usuário inválido. Account.confirmation.sent.to.your.email=A confirmação da conta foi enviada para o seu e-mail. Create.an.account=Crie a sua conta aqui Enter.required.fields.to.complete.registration=Insira os campos necessários para concluir o registro de- Next=Próximo Already.have.an.account=já tem uma conta? -Username.recovery.information.sent.to.your.email=As informações de recuperação de nome de usuário foram enviadas para o seu e-mail. Tente novamente com atributos diferentes se o email não for recebido. Registered.user.not.found.in.session=Usuário registrado não encontrado na sessão. No.security.questions.found.to.recover.password.contact.system.administrator=Nenhuma pergunta de segurança encontrada para recuperar a senha. Entre em contato com o administrador do seu sistema No.recovery.options.found=Nenhuma opção de recuperação encontrada para recuperar a senha. Entre em contato com o administrador do seu sistema diff --git a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_zh_CN.properties b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_zh_CN.properties index 75ad88162e1..fd92cc44855 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_zh_CN.properties +++ b/identity-apps-core/apps/recovery-portal/src/main/resources/org/wso2/carbon/identity/mgt/recovery/endpoint/i18n/Resources_zh_CN.properties @@ -59,6 +59,31 @@ password.reset.success.action=返回应用程序 email.link.expiry.message=此链接已过期。 # +# +username.recovery.heading=恢复用户名 +username.recovery.body=输入以下详细信息以恢复您的用户名。 +contact=电子邮件或手机 +username.recovery.next.button=下一个 +username.recovery.cancel.button=取消 +# + +# +username.recovery.channel.selection.heading=恢复用户名 +username.recovery.channel.selection.body=选择恢复选项 +send.username.via.email=通过电子邮件发送用户名 +send.username.via.sms=通过短信发送用户名 +username.recovery.channel.selection.next.button=下一个 +username.recovery.channel.selection.cancel.button=取消 +# + +# +username.recovery.email.success.header=检查您的电子邮件 +username.recovery.sms.success.header=检查您的手机 +username.recovery.email.success.body=用户名恢复详细信息已发送至提供的电子邮件。如果未收到电子邮件,请使用不同的信息重试。 +username.recovery.sms.success.body=用户名恢复详细信息已发送至提供的手机号码。如果未收到短信,请使用不同的信息重试。 +username.recovery.success.action=返回应用程序 +# + Wso2.identity.server=WSO2身份服务器 All.rights.reserved=版权所有。 Inc=Inc @@ -78,6 +103,7 @@ Enter.detail.to.recover.uname=输入以下详细信息以恢复您的用户名 First.name=名 Last.name=姓 Email=电子邮件 +Contact=电子邮件或手机 Tenant.domain=租户域 Create.account=完成注册 Enter.fields.to.cmplete.reg=在我们完成之前,只有一些细节。 @@ -105,12 +131,14 @@ Insufficient.info.to.find.user=如果信息不足以识别用户 No.recovery.supported.claims.found=未找到恢复支持的索赔 Unknown.password.recovery.option=未知密码恢复选项 Username.cannot.be.empty=用户名不能为空。 +Contact.cannot.be.empty=联系方式不能为空 +Invalid.contact=联系方式无效 +Invalid.username.recovery.stage=无效用户名恢复阶段. Account.confirmation.sent.to.your.email=帐户确认已发送到您的电子邮件中。 Create.an.account=创建一个帐户 Enter.required.fields.to.complete.registration=输入所需字段以完成注册 - Next=下一个 Already.have.an.account=已经有一个帐户? -Username.recovery.information.sent.to.your.email=用户名恢复信息已发送到您的电子邮件中。如果未收到电子邮件,请再次尝试使用不同的属性。 Registered.user.not.found.in.session=在会话中找不到注册用户。 No.security.questions.found.to.recover.password.contact.system.administrator=找不到恢复密码的安全问题。请联系您的系统管理员 No.recovery.options.found=找不到恢复密码的恢复选项。请联系您的系统管理员 diff --git a/identity-apps-core/apps/recovery-portal/src/main/webapp/recovery.jsp b/identity-apps-core/apps/recovery-portal/src/main/webapp/recovery.jsp index d0fd508e4d2..b65d5b0b560 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/webapp/recovery.jsp +++ b/identity-apps-core/apps/recovery-portal/src/main/webapp/recovery.jsp @@ -48,6 +48,49 @@ <%-- Include tenant context --%> +<%! + /** + * UsernameRecoveryStage represents the two steps of recovery in the UsernameRecovery V2. + */ + public enum UsernameRecoveryStage { + INITIATE("INITIATE"), + NOTIFY("NOTIFY"); + + private final String value; + + UsernameRecoveryStage(String value) { + + this.value = value; + } + + /** + * Returns the string value of the enum. + */ + public String getValue() { + + return value; + } + + /** + * Override the toString method of the object class. + */ + @Override + public String toString() { + + return value; + } + + /** + * Compares enum with string based on it's value. + * @return boolean whether the passed string equals the value of the enum. + */ + public boolean equalsValue(String otherValue) { + + return this.value.equals(otherValue); + } + } +%> + <% File usernameResolverFile = new File(getServletContext().getRealPath("extensions/username-resolver.jsp")); if (usernameResolverFile.exists()) { @@ -129,57 +172,151 @@ } if (isUsernameRecovery) { - // Username Recovery Scenario + // Username recovery scenario. + String recoveryStage = request.getAttribute("recoveryStage") != null + ? (String) request.getAttribute("recoveryStage") + : Encode.forJava(request.getParameter("recoveryStage")); + if (StringUtils.isBlank(tenantDomain)) { tenantDomain = IdentityManagementEndpointConstants.SUPER_TENANT; } - List claims; - UsernameRecoveryApi usernameRecoveryApi = new UsernameRecoveryApi(); - try { - claims = usernameRecoveryApi.getClaimsForUsernameRecovery(tenantDomain, true); - } catch (ApiException e) { - IdentityManagementEndpointUtil.addErrorInformation(request, e); - if (!StringUtils.isBlank(username)) { - request.setAttribute("username", username); + if (UsernameRecoveryStage.INITIATE.equalsValue(recoveryStage)) { + + // Separate the contact to mobile or email. + String contact = Encode.forJava(request.getParameter("contact")); + if (contact.matches(IdentityManagementEndpointConstants.UserInfoRecovery.MOBILE_CLAIM_REGEX)) { + request.setAttribute(IdentityManagementEndpointConstants.ClaimURIs.MOBILE_CLAIM, contact); + } else { + request.setAttribute(IdentityManagementEndpointConstants.ClaimURIs.EMAIL_CLAIM, contact); } - request.getRequestDispatcher("error.jsp").forward(request, response); - return; - } + - List claimDTOList = new ArrayList(); + List claims; + UsernameRecoveryApi usernameRecoveryApi = new UsernameRecoveryApi(); + RecoveryApiV2 recoveryApiV2 = new RecoveryApiV2(); + RecoveryInitRequest recoveryInitRequest = new RecoveryInitRequest(); - for (Claim claimDTO : claims) { - if (StringUtils.isNotBlank(request.getParameter(claimDTO.getUri()))) { - UserClaim userClaim = new UserClaim(); - userClaim.setUri(claimDTO.getUri()); - userClaim.setValue(request.getParameter(claimDTO.getUri()).trim()); - claimDTOList.add(userClaim); + try { + claims = usernameRecoveryApi.getClaimsForUsernameRecovery(tenantDomain, true); + } catch (ApiException e) { + IdentityManagementEndpointUtil.addErrorInformation(request, e); + if (!StringUtils.isBlank(username)) { + request.setAttribute("username", username); + } + request.getRequestDispatcher("error.jsp").forward(request, response); + return; } - } - try { - Map requestHeaders = new HashedMap(); - if (request.getParameter("g-recaptcha-response") != null) { - requestHeaders.put("g-recaptcha-response", request.getParameter("g-recaptcha-response")); + List claimDTOList = new ArrayList(); + + for (Claim claimDTO : claims) { + + // Check if the claim is present in the request attributes or parameters. + String claimValue = (String) request.getAttribute(claimDTO.getUri()); + + if (claimValue == null && request.getParameter(claimDTO.getUri()) != null) { + claimValue = Encode.forJava(request.getParameter(claimDTO.getUri()).toString().trim()); + } + + // If the claim value is present (either from parameters or attributes), add it. + if (StringUtils.isNotBlank(claimValue)) { + UserClaim userClaim = new UserClaim(); + userClaim.setUri(claimDTO.getUri()); + userClaim.setValue(claimValue); + claimDTOList.add(userClaim); + } } + recoveryInitRequest.claims(claimDTOList); - usernameRecoveryApi.recoverUsernamePost(claimDTOList, tenantDomain, null, requestHeaders); - request.setAttribute("callback", callback); - request.setAttribute("tenantDomain", tenantDomain); - request.getRequestDispatcher("username-recovery-complete.jsp").forward(request, response); - } catch (ApiException e) { - if (e.getCode() == 204) { - request.setAttribute("error", true); - request.setAttribute("errorMsg", IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, - "No.valid.user.found")); + try { + Map requestHeaders = new HashedMap(); + if (request.getParameter("g-recaptcha-response") != null) { + requestHeaders.put("g-recaptcha-response", Encode.forJava(request.getParameter("g-recaptcha-response"))); + } + + request.setAttribute("callback", callback); + request.setAttribute("tenantDomain", tenantDomain); + request.setAttribute("isUserFound", true); + + List initiateUsernameRecoveryResponse = + recoveryApiV2.initiateUsernameRecovery(recoveryInitRequest, tenantDomain, requestHeaders); + + if (initiateUsernameRecoveryResponse == null || initiateUsernameRecoveryResponse.isEmpty()) { + request.setAttribute("isUserFound", false); + request.getRequestDispatcher("channelselection.do").forward(request, response); + return; + } + + request.setAttribute("recoveryCode", initiateUsernameRecoveryResponse.get(0).getChannelInfo().getRecoveryCode()); + + List firstMatchChannels = initiateUsernameRecoveryResponse.get(0).getChannelInfo().getChannels(); + request.setAttribute("channels", firstMatchChannels); + + request.getRequestDispatcher("channelselection.do").forward(request, response); + return; + + } catch (ApiException e) { + IdentityManagementEndpointUtil.addErrorInformation(request, e); request.getRequestDispatcher("recoveraccountrouter.do").forward(request, response); return; } + + } else if (UsernameRecoveryStage.NOTIFY.equalsValue(recoveryStage)) { + RecoveryApiV2 recoveryApiV2 = new RecoveryApiV2(); + String recoveryCode = request.getAttribute("recoveryCode") != null + ? (String) request.getAttribute("recoveryCode") + : Encode.forJava(request.getParameter("recoveryCode")); + + String usernameRecoveryOption = request.getAttribute("usernameRecoveryOption") != null + ? (String) request.getAttribute("usernameRecoveryOption") + : Encode.forJava(request.getParameter("usernameRecoveryOption")); + + String isUserFoundParam = request.getParameter("isUserFound"); + Boolean isUserFound = (isUserFoundParam != null) + ? Boolean.parseBoolean(isUserFoundParam) + : IdentityManagementEndpointUtil.getBooleanValue(request.getAttribute("isUserFound")); + + String recoveryChannelType = null; + String recoveryChannelId = null; + + // Extract the recovery channel name and id from the recovery option. + if (usernameRecoveryOption != null) { + String[] parts = usernameRecoveryOption.split(":"); + recoveryChannelId = parts[0]; + recoveryChannelType = parts[1]; + } + + request.setAttribute("recoveryChannelType", recoveryChannelType); + + // Sending the notification if we only found a user. + if (isUserFound){ + RecoveryRequest recoveryRequest = new RecoveryRequest(); + recoveryRequest.setRecoveryCode(recoveryCode); + recoveryRequest.setChannelId(recoveryChannelId); + + Map requestHeaders = new HashedMap(); + if (request.getParameter("g-recaptcha-response") != null) { + requestHeaders.put("g-recaptcha-response", Encode.forJava(request.getParameter("g-recaptcha-response"))); + } - IdentityManagementEndpointUtil.addErrorInformation(request, e); - request.getRequestDispatcher("recoveraccountrouter.do").forward(request, response); + try{ + recoveryApiV2.recoverUsername(recoveryRequest, tenantDomain, requestHeaders); + + } catch (ApiException e) { + IdentityManagementEndpointUtil.addErrorInformation(request, e); + request.getRequestDispatcher("recoveraccountrouter.do").forward(request, response); + return; + } + } + + request.getRequestDispatcher("username-recovery-complete.jsp").forward(request, response); return; + + } else { + request.setAttribute("errorMsg", IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, + "Invalid.username.recovery.stage")); + request.getRequestDispatcher("error.jsp").forward(request, response); } } else if (isPasswordRecoveryWithClaimsNotify) { diff --git a/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-channel.jsp b/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-channel.jsp new file mode 100644 index 00000000000..6a297158896 --- /dev/null +++ b/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-channel.jsp @@ -0,0 +1,298 @@ +<%-- + ~ Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + ~ + ~ WSO2 LLC. licenses this file to you under the Apache License, + ~ Version 2.0 (the "License"); you may not use this file except + ~ in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. +--%> + +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + +<%@ page import="org.apache.commons.lang.StringUtils" %> +<%@ page import="org.owasp.encoder.Encode" %> +<%@ page import="org.wso2.carbon.identity.base.IdentityRuntimeException" %> +<%@ page import="org.wso2.carbon.identity.captcha.util.CaptchaUtil" %> +<%@ page import="org.wso2.carbon.identity.core.util.IdentityTenantUtil" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.IdentityManagementEndpointConstants" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.IdentityManagementEndpointUtil" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.ApiException" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.api.ReCaptchaApi" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.api.UsernameRecoveryApi" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.model.Claim" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.model.ReCaptchaProperties" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.model.recovery.v2.*" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.PreferenceRetrievalClient" %> +<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.client.PreferenceRetrievalClientException" %> +<%@ page import="java.io.File" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.util.HashMap" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Map" %> +<%@ taglib prefix="layout" uri="org.wso2.identity.apps.taglibs.layout.controller" %> + +<%-- Localization --%> + + +<%-- Include tenant context --%> + + +<% + // Add the user-recovery-channel-selection screen to the list to retrieve text branding customizations. + screenNames.add("username-recovery-channel-selection"); +%> + +<%-- Branding Preferences --%> + + +<%! + /** + * Retrieves the ID of a specified recovery channel by its name from a list of channels. + * + * @param channels A list of {@link RecoveryChannel} objects representing available recovery channels. + * @param channelName The name of the recovery channel to search for (e.g., "EMAIL" or "SMS"). + * @return The ID of the matching channel if found in the channels list, + * "1" for "EMAIL" and "2" for "SMS" if channels list is null, or null if no match is found. + */ + public static String getChannelIdFromChannelName(List channels, String channelName){ + + // Check if channels are null. + if (channels == null){ + if (StringUtils.equals(channelName, "EMAIL")){ + return "1"; + } else if (StringUtils.equals(channelName, "SMS")){ + return "2"; + } + } + + // If channels are not null, loop throught them. + for (RecoveryChannel channel : channels) { + if (channel.getType().equals(channelName)) { + return channel.getId(); + } + } + + // Return null if no matching channel found. + return null; + } +%> + +<% + if (!Boolean.parseBoolean(application.getInitParameter( + IdentityManagementEndpointConstants.ConfigConstants.ENABLE_EMAIL_NOTIFICATION))) { + response.sendError(HttpServletResponse.SC_FOUND); + return; + } + + boolean error = IdentityManagementEndpointUtil.getBooleanValue(request.getAttribute("error")); + String errorMsg = IdentityManagementEndpointUtil.getStringValue(request.getAttribute("errorMsg")); + List channels = null; + if (request.getAttribute("channels") != null) { + channels = (List) request.getAttribute("channels"); + } + + String recoveryCode = IdentityManagementEndpointUtil.getStringValue(request.getAttribute("recoveryCode")); + Boolean isUserFound = IdentityManagementEndpointUtil.getBooleanValue(request.getAttribute("isUserFound")); + + String EMAIL = "EMAIL"; + String SMS = "SMS"; + String selectedOption = EMAIL; + + Boolean isEmailEnabled; + Boolean isSMSEnabled; + + // Getting the configs. + try { + PreferenceRetrievalClient preferenceRetrievalClient = new PreferenceRetrievalClient(); + isEmailEnabled = preferenceRetrievalClient.checkEmailBasedUsernameRecovery(tenantDomain); + isSMSEnabled = preferenceRetrievalClient.checkSMSBasedUsernameRecovery(tenantDomain); + } catch (PreferenceRetrievalClientException e) { + IdentityManagementEndpointUtil.addErrorInformation(request, e); + request.getRequestDispatcher("error.jsp").forward(request, response); + return; + } + + // Checking for multiple channels enabled. + Boolean isMultiChannelsEnabled = isEmailEnabled && isSMSEnabled; + + // Skipping the channel selection once we don't have channels or only have one channel. + if (channels != null && channels.size() == 1) { + request.setAttribute("usernameRecoveryOption", channels.get(0).getId() + ":" + channels.get(0).getType()); + request.setAttribute("recoveryStage", "NOTIFY"); + request.getRequestDispatcher("verify.do").forward(request, response); + return; + } else if (!isMultiChannelsEnabled) { + String recoveryOption = isEmailEnabled ? EMAIL : SMS; + request.setAttribute("usernameRecoveryOption", getChannelIdFromChannelName(channels, recoveryOption) + ":" + recoveryOption); + request.setAttribute("recoveryStage", "NOTIFY"); + request.getRequestDispatcher("verify.do").forward(request, response); + } + +%> + +<%-- Data for the layout from the page --%> +<% + layoutData.put("containerSize", "large"); +%> + + + + + <%-- header --%> + <% + File headerFile = new File(getServletContext().getRealPath("extensions/header.jsp")); + if (headerFile.exists()) { + %> + + <% } else { %> + + <% } %> + + + + + + <%-- product-title --%> + <% + File productTitleFile = new File(getServletContext().getRealPath("extensions/product-title.jsp")); + if (productTitleFile.exists()) { + %> + + <% } else { %> + + <% } %> + + +
+

<%=i18n(recoveryResourceBundle, customText, "username.recovery.channel.selection.heading")%>

+ <% if (error) { %> +
+ <%= IdentityManagementEndpointUtil.i18nBase64(recoveryResourceBundle, errorMsg) %> +
+ <% } %> + + + <%=i18n(recoveryResourceBundle, customText, "username.recovery.channel.selection.body")%> + + + +
+
+ <% + String callback = IdentityManagementEndpointUtil.getStringValue(request.getAttribute("callback")); + + if (StringUtils.isBlank(callback)) { + callback = IdentityManagementEndpointUtil.getUserPortalUrl( + application.getInitParameter(IdentityManagementEndpointConstants.ConfigConstants.USER_PORTAL_URL), tenantDomain); + } + + if (callback != null) { + %> +
+ +
+
+ "/> +
+ + <% } %> + +
+
+ " + <%=EMAIL.equals(selectedOption)?"checked":""%>/> + +
+
+
+
+ " + <%=SMS.equals(selectedOption)?"checked":""%>/> + +
+
+ + + + > + > + + +
+ +
+ +
+
+
+
+ + <%-- product-footer --%> + <% + File productFooterFile = new File(getServletContext().getRealPath("extensions/product-footer.jsp")); + if (productFooterFile.exists()) { + %> + + <% } else { %> + + <% } %> + + + + +
+ + <%-- footer --%> + <% + File footerFile = new File(getServletContext().getRealPath("extensions/footer.jsp")); + if (footerFile.exists()) { + %> + + <% } else { %> + + <% } %> + + + + diff --git a/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-complete.jsp b/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-complete.jsp index 223da696a86..c4241982d39 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-complete.jsp +++ b/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery-complete.jsp @@ -32,12 +32,30 @@ <%-- Include tenant context --%> +<% + // Add the user-recovery-success screen to the list to retrieve text branding customizations. + screenNames.add("username-recovery-success"); +%> + <%-- Branding Preferences --%> <% - String callback = (String) request.getAttribute("callback"); + String EMAIL = "EMAIL"; + String callback = request.getParameter("callback"); String username = request.getParameter("username"); + String recoveryChannelType = IdentityManagementEndpointUtil.getStringValue(request.getAttribute("recoveryChannelType")); + + String successMessageTitle; + String successMessageDescrition; + + if (StringUtils.equals(recoveryChannelType, EMAIL)){ + successMessageTitle = "username.recovery.email.success.heading"; + successMessageDescrition = "username.recovery.email.success.body"; + } else { + successMessageTitle = "username.recovery.sms.success.heading"; + successMessageDescrition = "username.recovery.sms.success.body"; + } %> <%-- Data for the layout from the page --%> @@ -74,10 +92,10 @@

- <%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "check.your.email")%> + <%=i18n(recoveryResourceBundle, customText, successMessageTitle)%>

- <%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "Username.recovery.information.sent.to.your.email")%> + <%=i18n(recoveryResourceBundle, customText, successMessageDescrition)%>

<% if(StringUtils.isNotBlank(callback)) { @@ -85,7 +103,7 @@

- <%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle,"Back.to.application")%> + <%=i18n(recoveryResourceBundle, customText, "username.recovery.success.action")%> <% } %>

@@ -98,7 +116,6 @@ <%= StringEscapeUtils.escapeHtml4(supportEmail) %> - .

diff --git a/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery.jsp b/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery.jsp index ca04aabb402..0a6961048d5 100644 --- a/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery.jsp +++ b/identity-apps-core/apps/recovery-portal/src/main/webapp/username-recovery.jsp @@ -43,6 +43,11 @@ <%-- Include tenant context --%> +<% + // Add the user-recovery-claim screen to the list to retrieve text branding customizations. + screenNames.add("username-recovery-claim"); +%> + <%-- Branding Preferences --%> @@ -84,6 +89,7 @@ boolean isFirstNameInClaims = false; boolean isLastNameInClaims = false; boolean isEmailInClaims = false; + boolean isMobileInClaims = false; List claims; UsernameRecoveryApi usernameRecoveryApi = new UsernameRecoveryApi(); try { @@ -106,6 +112,8 @@ return; } + String mobileClaimRegex = null; + String emailClaimRegex = null; for (Claim claim : claims) { if (StringUtils.equals(claim.getUri(), IdentityManagementEndpointConstants.ClaimURIs.FIRST_NAME_CLAIM)) { @@ -117,6 +125,12 @@ if (StringUtils.equals(claim.getUri(), IdentityManagementEndpointConstants.ClaimURIs.EMAIL_CLAIM)) { isEmailInClaims = true; + emailClaimRegex = Encode.forJava(claim.getValidationRegex()); + } + if (StringUtils.equals(claim.getUri(), + IdentityManagementEndpointConstants.ClaimURIs.MOBILE_CLAIM)) { + isMobileInClaims = true; + mobileClaimRegex = Encode.forJava(claim.getValidationRegex()); } } @@ -171,7 +185,7 @@
-

<%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "Recover.username")%>

+

<%=i18n(recoveryResourceBundle, customText, "username.recovery.heading")%>

<% if (error) { %>
<%= IdentityManagementEndpointUtil.i18nBase64(recoveryResourceBundle, errorMsg) %> @@ -179,7 +193,7 @@ <% } %> - <%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "Enter.detail.to.recover.uname")%> + <%=i18n(recoveryResourceBundle, customText, "username.recovery.body")%> @@ -226,13 +240,13 @@ <% } - if (isEmailInClaims) { %> -
- - + if (isEmailInClaims || isMobileInClaims) { %> +
+ + *" + required class="form-control" />
<% } %> @@ -249,7 +263,8 @@
<% } %> - + + <% for (Claim claim : claims) { if (claim.getRequired() && @@ -258,7 +273,9 @@ !StringUtils.equals(claim.getUri(), IdentityManagementEndpointConstants.ClaimURIs.LAST_NAME_CLAIM) && !StringUtils.equals(claim.getUri(), - IdentityManagementEndpointConstants.ClaimURIs.EMAIL_CLAIM)) { + IdentityManagementEndpointConstants.ClaimURIs.EMAIL_CLAIM) && + !StringUtils.equals(claim.getUri(), + IdentityManagementEndpointConstants.ClaimURIs.MOBILE_CLAIM)) { %>
@@ -291,15 +308,16 @@ -
- - <%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "Cancel")%> - - + +
+
@@ -341,7 +359,6 @@ } $(document).ready(function () { - $("#recoverDetailsForm").submit(function (e) { <% if (reCaptchaEnabled) { @@ -349,7 +366,6 @@ if (!grecaptcha.getResponse()) { e.preventDefault(); grecaptcha.execute(); - return true; } <% @@ -376,6 +392,24 @@ } <% } %> + // Contact input validation. + const contact = $("#contact").val(); + const mobileClaimRegex = new RegExp("<%=mobileClaimRegex%>"); + const emailClaimRegex = new RegExp("<%=emailClaimRegex%>"); + + if (contact === "") { + errorMessage.text("<%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "Contact.cannot.be.empty")%>"); + errorMessage.show(); + $("html, body").animate({scrollTop: errorMessage.offset().top}, "slow"); + submitButton.removeClass("loading").attr("disabled", false); + return false; + } else if (!contact.match(mobileClaimRegex) && !contact.match(emailClaimRegex)) { + errorMessage.text("<%=IdentityManagementEndpointUtil.i18n(recoveryResourceBundle, "Invalid.contact")%>"); + errorMessage.show(); + $("html, body").animate({scrollTop: errorMessage.offset().top}, "slow"); + submitButton.removeClass("loading").attr("disabled", false); + return false; + } return true; }); }); diff --git a/identity-apps-core/features/org.wso2.identity.apps.recovery.portal.server.feature/resources/web.xml.j2 b/identity-apps-core/features/org.wso2.identity.apps.recovery.portal.server.feature/resources/web.xml.j2 index a919d25ef1b..3a0920d1991 100644 --- a/identity-apps-core/features/org.wso2.identity.apps.recovery.portal.server.feature/resources/web.xml.j2 +++ b/identity-apps-core/features/org.wso2.identity.apps.recovery.portal.server.feature/resources/web.xml.j2 @@ -193,6 +193,7 @@ {"name": "confirmregistration.do", "jsp": "/self-registration-with-verification-confirm.jsp", "url": "/confirmregistration.do"}, {"name": "completeregistration.do", "jsp": "/self-registration-complete.jsp", "url": "/completeregistration.do"}, {"name": "verify.do", "jsp": "/recovery.jsp", "url": "/verify.do"}, + {"name": "channelselection.do", "jsp": "/username-recovery-channel.jsp", "url": "/channelselection.do"}, {"name": "confirmliteuserregistration.do", "jsp": "/lite-user-confirm.jsp", "url": "/confirmliteuserregistration.do"}, {"name": "acceptinvitation.do", "jsp": "/accept-invitation.jsp", "url": "/acceptinvitation.do"} ] %} diff --git a/modules/i18n/src/models/namespaces/branding-ns.ts b/modules/i18n/src/models/namespaces/branding-ns.ts index e699b2e96d7..a51fd497527 100644 --- a/modules/i18n/src/models/namespaces/branding-ns.ts +++ b/modules/i18n/src/models/namespaces/branding-ns.ts @@ -175,12 +175,17 @@ export interface BrandingNS { "password-recovery": string; "password-reset": string; "password-reset-success": string; + "username-recovery-claim": string; + "username-recovery-channel-selection": string; + "username-recovery-success": string; }; variations: { "sms-otp": string; "email-link": string; "base": string; "multi": string; + "sms": string; + "email": string; }; ai: { banner: { diff --git a/modules/i18n/src/translations/en-US/portals/branding.ts b/modules/i18n/src/translations/en-US/portals/branding.ts index 240e66e454a..f62d422000f 100644 --- a/modules/i18n/src/translations/en-US/portals/branding.ts +++ b/modules/i18n/src/translations/en-US/portals/branding.ts @@ -174,13 +174,18 @@ export const branding: BrandingNS = { "password-reset-success": "Password Reset Link Sent", "sign-up": "Sign Up", "sms-otp": "SMS OTP", - "totp": "TOTP" + "totp": "TOTP", + "username-recovery-claim": "Username Recovery Claim", + "username-recovery-channel-selection": "Username Recovery Channel Selection", + "username-recovery-success": "Username Recovery Success" }, variations: { "sms-otp": "SMS OTP", "email-link": "Email Link", "base": "Base", - "multi": "Multi Option" + "multi": "Multi Option", + "sms": "SMS", + "email": "Email" }, ai: { banner: {