2022-02-13 23:39:27 +08:00
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
2021-03-26 21:57:41 +08:00
//
// Licensed 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 React from 'react' ;
2021-03-26 21:58:10 +08:00
import { Link } from "react-router-dom" ;
2021-08-03 21:00:07 +08:00
import { Form , Input , Checkbox , Button , Row , Col , Result , Modal } from 'antd' ;
2021-03-26 21:57:41 +08:00
import * as Setting from "../Setting" ;
import * as AuthBackend from "./AuthBackend" ;
2021-04-28 00:47:12 +08:00
import i18next from "i18next" ;
2021-04-28 15:54:50 +08:00
import * as Util from "./Util" ;
import { authConfig } from "./Auth" ;
import * as ApplicationBackend from "../backend/ApplicationBackend" ;
2022-01-07 20:34:27 +08:00
import { CountDownInput } from "../common/CountDownInput" ;
2021-07-31 16:02:48 +08:00
import SelectRegionBox from "../SelectRegionBox" ;
2021-11-06 22:04:20 +08:00
import CustomGithubCorner from "../CustomGithubCorner" ;
2021-03-26 21:57:41 +08:00
2022-06-17 19:57:11 +08:00
/* eslint-disable jsx-a11y/anchor-is-valid */
2021-03-26 21:57:41 +08:00
const formItemLayout = {
labelCol : {
xs : {
span : 24 ,
} ,
sm : {
2021-07-31 16:02:48 +08:00
span : 8 ,
2021-03-26 21:57:41 +08:00
} ,
} ,
wrapperCol : {
xs : {
span : 24 ,
} ,
sm : {
2021-04-28 15:54:50 +08:00
span : 18 ,
2021-03-26 21:57:41 +08:00
} ,
} ,
} ;
const tailFormItemLayout = {
wrapperCol : {
xs : {
span : 24 ,
offset : 0 ,
} ,
sm : {
span : 16 ,
offset : 8 ,
} ,
} ,
} ;
2021-04-27 22:47:44 +08:00
class SignupPage extends React . Component {
2021-03-26 21:57:41 +08:00
constructor ( props ) {
super ( props ) ;
this . state = {
classes : props ,
2021-06-14 13:13:39 +08:00
applicationName : props . match ? . params . applicationName !== undefined ? props . match . params . applicationName : authConfig . appName ,
2021-04-28 15:54:50 +08:00
application : null ,
2021-05-18 20:11:03 +08:00
email : "" ,
2021-05-20 21:09:12 +08:00
phone : "" ,
emailCode : "" ,
2021-06-23 11:41:21 +08:00
phoneCode : "" ,
validEmail : false ,
validPhone : false ,
2021-07-31 16:02:48 +08:00
region : "" ,
2021-08-03 21:00:07 +08:00
isTermsOfUseVisible : false ,
2021-08-10 10:43:33 +08:00
termsOfUseContent : "" ,
2021-03-26 21:57:41 +08:00
} ;
this . form = React . createRef ( ) ;
}
2021-04-28 15:54:50 +08:00
UNSAFE _componentWillMount ( ) {
if ( this . state . applicationName !== undefined ) {
this . getApplication ( ) ;
} else {
Util . showMessage ( "error" , ` Unknown application name: ${ this . state . applicationName } ` ) ;
}
}
getApplication ( ) {
if ( this . state . applicationName === undefined ) {
return ;
}
ApplicationBackend . getApplication ( "admin" , this . state . applicationName )
. then ( ( application ) => {
this . setState ( {
application : application ,
} ) ;
2022-06-26 09:34:01 +08:00
if ( application !== null && application !== undefined ) {
this . getTermsofuseContent ( application . termsOfUse ) ;
}
2021-04-28 15:54:50 +08:00
} ) ;
}
2021-04-28 22:40:21 +08:00
getResultPath ( application ) {
if ( authConfig . appName === application . name ) {
return "/result" ;
} else {
2021-06-20 09:46:06 +08:00
if ( Setting . hasPromptPage ( application ) ) {
return ` /prompt/ ${ application . name } ` ;
} else {
return ` /result/ ${ application . name } ` ;
}
2021-04-28 22:40:21 +08:00
}
}
2021-06-14 13:13:39 +08:00
getApplicationObj ( ) {
if ( this . props . application !== undefined ) {
return this . props . application ;
} else {
return this . state . application ;
}
}
2021-08-10 10:43:33 +08:00
getTermsofuseContent ( url ) {
fetch ( url , {
method : "GET" ,
} ) . then ( r => {
r . text ( ) . then ( res => {
this . setState ( { termsOfUseContent : res } )
} )
} )
}
2021-06-20 13:27:26 +08:00
onUpdateAccount ( account ) {
this . props . onUpdateAccount ( account ) ;
}
2021-03-26 21:57:41 +08:00
onFinish ( values ) {
2021-06-14 13:13:39 +08:00
const application = this . getApplicationObj ( ) ;
values . phonePrefix = application . organizationObj . phonePrefix ;
2021-04-27 22:47:44 +08:00
AuthBackend . signup ( values )
2021-03-26 21:57:41 +08:00
. then ( ( res ) => {
if ( res . status === 'ok' ) {
2021-08-01 00:16:13 +08:00
if ( Setting . hasPromptPage ( application ) ) {
AuthBackend . getAccount ( "" )
. then ( ( res ) => {
let account = null ;
if ( res . status === "ok" ) {
account = res . data ;
account . organization = res . data2 ;
this . onUpdateAccount ( account ) ;
Setting . goToLinkSoft ( this , this . getResultPath ( application ) ) ;
} else {
2021-06-20 13:27:26 +08:00
Setting . showMessage ( "error" , ` Failed to sign in: ${ res . msg } ` ) ;
}
2021-08-01 00:16:13 +08:00
} ) ;
} else {
Setting . goToLinkSoft ( this , this . getResultPath ( application ) ) ;
}
2021-03-26 21:57:41 +08:00
} else {
2021-05-18 20:11:03 +08:00
Setting . showMessage ( "error" , i18next . t ( ` signup: ${ res . msg } ` ) ) ;
2021-03-26 21:57:41 +08:00
}
} ) ;
}
onFinishFailed ( values , errorFields , outOfDate ) {
this . form . current . scrollToField ( errorFields [ 0 ] . name ) ;
}
2021-06-16 14:06:41 +08:00
renderFormItem ( application , signupItem ) {
if ( ! signupItem . visible ) {
return null ;
2021-05-08 00:23:08 +08:00
}
2021-06-16 15:25:54 +08:00
const required = signupItem . required ;
2021-06-16 14:06:41 +08:00
if ( signupItem . name === "Username" ) {
return (
2021-03-26 21:57:41 +08:00
< Form . Item
2021-04-28 00:47:12 +08:00
name = "username"
2021-07-16 17:04:16 +08:00
key = "username"
2021-04-28 00:47:12 +08:00
label = { i18next . t ( "signup:Username" ) }
2021-03-26 21:57:41 +08:00
rules = { [
{
2021-06-16 15:25:54 +08:00
required : required ,
2021-06-17 22:43:30 +08:00
message : i18next . t ( "forget:Please input your username!" ) ,
2021-03-26 21:57:41 +08:00
whitespace : true ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
2021-06-16 14:06:41 +08:00
)
} else if ( signupItem . name === "Display name" ) {
2022-02-27 14:02:52 +08:00
if ( signupItem . rule === "First, last" && Setting . getLanguage ( ) !== "zh" ) {
return (
< React . Fragment >
< Form . Item
name = "firstName"
key = "firstName"
label = { i18next . t ( "general:First name" ) }
rules = { [
{
required : required ,
message : i18next . t ( "signup:Please input your first name!" ) ,
whitespace : true ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
< Form . Item
name = "lastName"
key = "lastName"
label = { i18next . t ( "general:Last name" ) }
rules = { [
{
required : required ,
message : i18next . t ( "signup:Please input your last name!" ) ,
whitespace : true ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
< / R e a c t . F r a g m e n t >
)
}
2021-06-16 14:06:41 +08:00
return (
2021-03-26 21:57:41 +08:00
< Form . Item
2021-04-28 21:25:58 +08:00
name = "name"
2021-07-16 17:04:16 +08:00
key = "name"
2022-02-27 14:02:52 +08:00
label = { ( signupItem . rule === "Real name" || signupItem . rule === "First, last" ) ? i18next . t ( "general:Real name" ) : i18next . t ( "general:Display name" ) }
2021-03-26 21:57:41 +08:00
rules = { [
{
2021-06-16 15:25:54 +08:00
required : required ,
2022-02-27 14:02:52 +08:00
message : ( signupItem . rule === "Real name" || signupItem . rule === "First, last" ) ? i18next . t ( "signup:Please input your real name!" ) : i18next . t ( "signup:Please input your display name!" ) ,
2021-03-26 21:57:41 +08:00
whitespace : true ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
2021-06-16 14:06:41 +08:00
)
} else if ( signupItem . name === "Affiliation" ) {
return (
2021-03-26 21:57:41 +08:00
< Form . Item
2021-04-27 22:35:14 +08:00
name = "affiliation"
2021-07-16 17:04:16 +08:00
key = "affiliation"
2021-04-28 00:47:12 +08:00
label = { i18next . t ( "user:Affiliation" ) }
2021-03-26 21:57:41 +08:00
rules = { [
{
2021-06-16 15:25:54 +08:00
required : required ,
2021-04-28 00:47:12 +08:00
message : i18next . t ( "signup:Please input your affiliation!" ) ,
2021-03-26 21:57:41 +08:00
whitespace : true ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
2021-06-16 14:06:41 +08:00
)
2021-12-28 17:48:24 +08:00
} else if ( signupItem . name === "ID card" ) {
return (
< Form . Item
name = "idCard"
key = "idCard"
label = { i18next . t ( "user:ID card" ) }
rules = { [
{
required : required ,
message : i18next . t ( "signup:Please input your ID card number!" ) ,
whitespace : true ,
} ,
{
required : required ,
pattern : new RegExp ( /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9X]$/ , "g" ) ,
message : i18next . t ( "signup:Please input the correct ID card number!" ) ,
} ,
] }
>
< Input / >
< / F o r m . I t e m >
)
2021-07-31 16:02:48 +08:00
} else if ( signupItem . name === "Country/Region" ) {
return (
< Form . Item
name = "country_region"
key = "region"
label = { i18next . t ( "user:Country/Region" ) }
rules = { [
{
required : required ,
message : i18next . t ( "signup:Please select your country/region!" ) ,
} ,
] }
>
< SelectRegionBox onChange = { ( value ) => { this . setState ( { region : value } ) } } / >
< / F o r m . I t e m >
)
2021-06-16 14:06:41 +08:00
} else if ( signupItem . name === "Email" ) {
return (
< React . Fragment >
< Form . Item
name = "email"
2021-07-16 17:04:16 +08:00
key = "email"
2021-06-16 14:06:41 +08:00
label = { i18next . t ( "general:Email" ) }
rules = { [
{
2021-06-16 15:25:54 +08:00
required : required ,
2021-06-16 14:06:41 +08:00
message : i18next . t ( "signup:Please input your Email!" ) ,
} ,
2021-06-23 11:41:21 +08:00
{
2021-09-21 14:04:17 +08:00
validator : ( _ , value ) => {
if ( this . state . email !== "" && ! Setting . isValidEmail ( this . state . email ) ) {
this . setState ( { validEmail : false } ) ;
return Promise . reject ( i18next . t ( "signup:The input is not valid Email!" ) ) ;
2021-06-23 11:41:21 +08:00
}
2021-09-21 14:04:17 +08:00
this . setState ( { validEmail : true } ) ;
return Promise . resolve ( ) ;
2021-06-23 11:41:21 +08:00
}
}
2021-06-16 14:06:41 +08:00
] }
>
< Input onChange = { e => this . setState ( { email : e . target . value } ) } / >
< / F o r m . I t e m >
2022-05-02 17:19:40 +08:00
{
signupItem . rule !== "No verification" &&
< Form . Item
name = "emailCode"
key = "emailCode"
label = { i18next . t ( "code:Email code" ) }
rules = { [ {
required : required ,
message : i18next . t ( "code:Please input your verification code!" ) ,
} ] }
>
< CountDownInput
disabled = { ! this . state . validEmail }
onButtonClickArgs = { [ this . state . email , "email" , Setting . getApplicationOrgName ( application ) ] }
/ >
< / F o r m . I t e m >
}
2021-06-16 14:06:41 +08:00
< / R e a c t . F r a g m e n t >
)
} else if ( signupItem . name === "Phone" ) {
return (
< React . Fragment >
< Form . Item
name = "phone"
2021-07-16 17:04:16 +08:00
key = "phone"
2021-06-16 14:06:41 +08:00
label = { i18next . t ( "general:Phone" ) }
rules = { [
{
2021-06-16 15:25:54 +08:00
required : required ,
2021-06-16 14:06:41 +08:00
message : i18next . t ( "signup:Please input your phone number!" ) ,
} ,
2021-06-23 11:41:21 +08:00
{
validator : ( _ , value ) => {
2021-09-21 14:04:17 +08:00
if ( this . state . phone !== "" && ! Setting . isValidPhone ( this . state . phone ) ) {
this . setState ( { validPhone : false } ) ;
return Promise . reject ( i18next . t ( "signup:The input is not valid Phone!" ) ) ;
2021-06-23 11:41:21 +08:00
}
2021-09-21 14:04:17 +08:00
this . setState ( { validPhone : true } ) ;
return Promise . resolve ( ) ;
2021-06-23 11:41:21 +08:00
}
}
2021-06-16 14:06:41 +08:00
] }
>
< Input
style = { {
width : '100%' ,
} }
addonBefore = { ` + ${ this . state . application ? . organizationObj . phonePrefix } ` }
onChange = { e => this . setState ( { phone : e . target . value } ) }
/ >
< / F o r m . I t e m >
< Form . Item
name = "phoneCode"
2021-07-16 17:04:16 +08:00
key = "phoneCode"
2021-06-17 22:43:30 +08:00
label = { i18next . t ( "code:Phone code" ) }
2021-06-16 14:06:41 +08:00
rules = { [
{
2021-06-16 15:25:54 +08:00
required : required ,
2021-06-17 22:43:30 +08:00
message : i18next . t ( "code:Please input your phone verification code!" ) ,
2021-06-16 14:06:41 +08:00
} ,
] }
>
< CountDownInput
2021-06-23 11:41:21 +08:00
disabled = { ! this . state . validPhone }
2021-11-28 20:57:14 +08:00
onButtonClickArgs = { [ this . state . phone , "phone" , Setting . getApplicationOrgName ( application ) ] }
2021-06-16 14:06:41 +08:00
/ >
< / F o r m . I t e m >
< / R e a c t . F r a g m e n t >
)
2021-09-21 14:04:17 +08:00
} else if ( signupItem . name === "Password" ) {
return (
< Form . Item
name = "password"
key = "password"
label = { i18next . t ( "general:Password" ) }
rules = { [
{
required : required ,
2021-12-06 09:38:22 +08:00
min : 6 ,
message : i18next . t ( "login:Please input your password, at least 6 characters!" ) ,
2021-09-21 14:04:17 +08:00
} ,
] }
hasFeedback
>
< Input . Password / >
< / F o r m . I t e m >
)
} else if ( signupItem . name === "Confirm password" ) {
return (
< Form . Item
name = "confirm"
key = "confirm"
label = { i18next . t ( "signup:Confirm" ) }
dependencies = { [ 'password' ] }
hasFeedback
rules = { [
{
required : required ,
message : i18next . t ( "signup:Please confirm your password!" ) ,
} ,
( { getFieldValue } ) => ( {
validator ( rule , value ) {
if ( ! value || getFieldValue ( 'password' ) === value ) {
return Promise . resolve ( ) ;
}
return Promise . reject ( i18next . t ( "signup:Your confirmed password is inconsistent with the password!" ) ) ;
} ,
} ) ,
] }
>
< Input . Password / >
< / F o r m . I t e m >
)
2021-06-16 14:06:41 +08:00
} else if ( signupItem . name === "Agreement" ) {
return (
2021-03-26 21:57:41 +08:00
< Form . Item
2021-06-16 14:06:41 +08:00
name = "agreement"
2021-07-16 17:04:16 +08:00
key = "agreement"
2021-06-16 14:06:41 +08:00
valuePropName = "checked"
2021-06-16 15:25:54 +08:00
rules = { [
{
required : required ,
message : i18next . t ( "signup:Please accept the agreement!" ) ,
} ,
] }
2021-06-16 14:06:41 +08:00
{ ... tailFormItemLayout }
2021-03-26 21:57:41 +08:00
>
2021-06-16 14:06:41 +08:00
< Checkbox >
{ i18next . t ( "signup:Accept" ) } & nbsp ;
2021-08-03 21:00:07 +08:00
< Link onClick = { ( ) => {
this . setState ( {
isTermsOfUseVisible : true ,
} ) ;
} } >
2021-06-16 14:06:41 +08:00
{ i18next . t ( "signup:Terms of Use" ) }
< / L i n k >
< / C h e c k b o x >
2021-03-26 21:57:41 +08:00
< / F o r m . I t e m >
2021-06-16 14:06:41 +08:00
)
}
}
2021-03-26 21:57:41 +08:00
2021-08-03 21:00:07 +08:00
renderModal ( ) {
return (
< Modal
title = { i18next . t ( "signup:Terms of Use" ) }
visible = { this . state . isTermsOfUseVisible }
2021-08-10 10:43:33 +08:00
width = { "55vw" }
2021-08-03 21:00:07 +08:00
closable = { false }
2021-08-10 10:43:33 +08:00
okText = { i18next . t ( "signup:Accept" ) }
cancelText = { i18next . t ( "signup:Decline" ) }
onOk = { ( ) => {
this . form . current . setFieldsValue ( { agreement : true } )
this . setState ( {
isTermsOfUseVisible : false ,
} ) ;
} }
onCancel = { ( ) => {
this . form . current . setFieldsValue ( { agreement : false } )
this . setState ( {
isTermsOfUseVisible : false ,
} ) ;
this . props . history . goBack ( ) ;
} }
2021-08-03 21:00:07 +08:00
>
2021-08-15 00:17:53 +08:00
< iframe title = { "terms" } style = { { border : 0 , width : "100%" , height : "60vh" } } srcDoc = { this . state . termsOfUseContent } / >
2021-08-03 21:00:07 +08:00
< / M o d a l >
)
}
2021-06-16 14:06:41 +08:00
renderForm ( application ) {
if ( ! application . enableSignUp ) {
return (
< Result
status = "error"
title = "Sign Up Error"
subTitle = { "The application does not allow to sign up new account" }
extra = { [
2021-07-16 17:04:16 +08:00
< Button type = "primary" key = "signin" onClick = { ( ) => {
2021-06-16 14:06:41 +08:00
Setting . goToLogin ( this , application ) ;
} } >
2021-07-16 17:04:16 +08:00
Sign In
< / B u t t o n >
2021-03-26 21:57:41 +08:00
] }
>
2021-06-16 14:06:41 +08:00
< / R e s u l t >
)
}
return (
< Form
{ ... formItemLayout }
ref = { this . form }
name = "signup"
onFinish = { ( values ) => this . onFinish ( values ) }
onFinishFailed = { ( errorInfo ) => this . onFinishFailed ( errorInfo . values , errorInfo . errorFields , errorInfo . outOfDate ) }
initialValues = { {
application : application . name ,
organization : application . organization ,
} }
style = { { width : ! Setting . isMobile ( ) ? "400px" : "250px" } }
size = "large"
>
2021-03-26 21:57:41 +08:00
< Form . Item
2021-06-16 14:06:41 +08:00
style = { { height : 0 , visibility : "hidden" } }
name = "application"
2021-03-26 21:57:41 +08:00
rules = { [
{
required : true ,
2021-06-16 14:06:41 +08:00
message : 'Please input your application!' ,
2021-03-26 21:57:41 +08:00
} ,
] }
>
< / F o r m . I t e m >
2021-05-18 20:11:03 +08:00
< Form . Item
2021-06-16 14:06:41 +08:00
style = { { height : 0 , visibility : "hidden" } }
name = "organization"
2021-05-18 20:11:03 +08:00
rules = { [
{
required : true ,
2021-06-16 14:06:41 +08:00
message : 'Please input your organization!' ,
2021-05-18 20:11:03 +08:00
} ,
] }
>
2021-03-26 21:57:41 +08:00
< / F o r m . I t e m >
2021-06-16 14:06:41 +08:00
{
2021-06-22 11:31:53 +08:00
application . signupItems ? . map ( signupItem => this . renderFormItem ( application , signupItem ) )
2021-06-16 14:06:41 +08:00
}
2021-03-26 21:57:41 +08:00
< Form . Item { ... tailFormItemLayout } >
< Button type = "primary" htmlType = "submit" >
2021-04-28 00:47:12 +08:00
{ i18next . t ( "account:Sign Up" ) }
2021-03-26 21:57:41 +08:00
< / B u t t o n >
2021-04-28 00:47:12 +08:00
& nbsp ; & nbsp ; { i18next . t ( "signup:Have account?" ) } & nbsp ;
2022-06-17 19:57:11 +08:00
< a onClick = { ( ) => {
2022-03-05 16:47:08 +08:00
let linkInStorage = sessionStorage . getItem ( "loginURL" )
2022-04-08 20:01:30 +08:00
if ( linkInStorage != null ) {
2022-03-05 16:47:08 +08:00
Setting . goToLink ( linkInStorage )
} else {
Setting . goToLogin ( this , application )
}
2021-04-28 22:40:21 +08:00
} } >
2021-04-28 00:47:12 +08:00
{ i18next . t ( "signup:sign in now" ) }
2021-07-16 17:04:16 +08:00
< / a >
2021-03-26 21:57:41 +08:00
< / F o r m . I t e m >
< / F o r m >
)
}
render ( ) {
2021-06-14 13:13:39 +08:00
const application = this . getApplicationObj ( ) ;
2021-04-28 15:54:50 +08:00
if ( application === null ) {
return null ;
}
2021-10-26 13:11:21 +08:00
if ( application . signupHtml !== "" ) {
return (
< div dangerouslySetInnerHTML = { { _ _html : application . signupHtml } } / >
)
}
2021-03-26 21:57:41 +08:00
return (
< div >
2021-11-06 22:04:20 +08:00
< CustomGithubCorner / >
2021-03-26 21:57:41 +08:00
& nbsp ;
< Row >
< Col span = { 24 } style = { { display : "flex" , justifyContent : "center" } } >
2021-04-28 15:54:50 +08:00
< div style = { { marginTop : "10px" , textAlign : "center" } } >
2021-04-29 21:28:24 +08:00
{
Setting . renderHelmet ( application )
}
2021-04-28 15:54:50 +08:00
{
Setting . renderLogo ( application )
}
{
this . renderForm ( application )
}
< / d i v >
2021-03-26 21:57:41 +08:00
< / C o l >
< / R o w >
2021-08-03 21:00:07 +08:00
{
this . renderModal ( )
}
2021-03-26 21:57:41 +08:00
< / d i v >
)
}
}
2021-04-27 22:47:44 +08:00
export default SignupPage ;