PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.18.0
GiveWP – Donation Plugin and Fundraising Platform v4.18.0
4.18.0.1 4.18.0 4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 All 258 releases
give / src / DonationForms / resources / registrars / templates / groups / Authentication.tsx

Authentication.tsx in GiveWP – Donation Plugin and Fundraising Platform 4.18.0, at src/DonationForms/resources/registrars/templates/groups/Authentication.tsx

220 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {FC, useState} from 'react';
2 import {__} from '@wordpress/i18n';
3 import type {FieldProps} from '@givewp/forms/propTypes';
4 import {GroupProps} from '@givewp/forms/propTypes';
5 import getWindowData from '@givewp/forms/app/utilities/getWindowData';
6 import postData from '@givewp/forms/app/utilities/postData';
7 import getCurrentFormUrlData from '@givewp/forms/app/utilities/getCurrentFormUrlData';
8 import navigateTop from '@givewp/forms/app/utilities/navigateTop';
9 import {setAuthToken} from '@givewp/forms/app/utilities/authToken';
10 import FieldError from '../layouts/FieldError';
11 import styles from '../styles.module.scss';
12
13 const {originUrl, isEmbed, embedId, isCrossOriginEmbed} = getCurrentFormUrlData();
14
15 const getRedirectUrl = (redirectUrl: URL) => {
16 const formPageUrl = new URL(originUrl);
17 formPageUrl.searchParams.append('givewp-auth-redirect', 'true');
18
19 if (isEmbed) {
20 formPageUrl.searchParams.append('givewp-is-embed', String(isEmbed));
21 formPageUrl.searchParams.append('givewp-embed-id', embedId);
22 }
23
24 // use wp core login redirect param
25 redirectUrl.searchParams.set('redirect_to', formPageUrl.toString());
26
27 return redirectUrl;
28 };
29 /**
30 * @since 4.17.0 Bail in cross-origin embeds where the parent page URL is unreadable.
31 */
32 const handleLoginPageRedirected = () => {
33 let formPageUrl;
34
35 try {
36 formPageUrl = new URL(window.top.location.href);
37 } catch (e) {
38 return;
39 }
40
41 const isAuthRedirect =
42 formPageUrl.searchParams.has('givewp-auth-redirect') &&
43 formPageUrl.searchParams.get('givewp-auth-redirect') === 'true';
44
45 if (isAuthRedirect) {
46 const isEmbedRedirect =
47 formPageUrl.searchParams.has('givewp-is-embed') &&
48 formPageUrl.searchParams.get('givewp-is-embed') === 'true';
49 const isThisEmbed =
50 formPageUrl.searchParams.has('givewp-embed-id') &&
51 formPageUrl.searchParams.get('givewp-embed-id') === embedId;
52
53 if (isEmbedRedirect && isThisEmbed) {
54 window.frameElement?.scrollIntoView({
55 behavior: 'smooth',
56 });
57 }
58 }
59 };
60
61 handleLoginPageRedirected();
62
63 interface AuthProps extends GroupProps {
64 nodeComponents: {
65 login: FC<FieldProps | {}>;
66 password: FC<FieldProps | {}>;
67 };
68 required: boolean;
69 isAuthenticated: boolean;
70 loginRedirect: boolean;
71 loginRedirectUrl: string;
72 loginNotice: string;
73 loginConfirmation: string;
74 lostPasswordUrl: string;
75 }
76
77 export default function Authentication({
78 nodeComponents: {login: Login, password: Password},
79 required,
80 isAuthenticated,
81 loginRedirect,
82 loginRedirectUrl,
83 loginNotice,
84 loginConfirmation,
85 lostPasswordUrl,
86 name,
87 }: AuthProps) {
88 const [isAuth, setIsAuth] = useState<boolean>(isAuthenticated);
89 const [showLogin, setShowLogin] = useState<boolean>(required);
90 const toggleShowLogin = () => setShowLogin(!showLogin);
91 /**
92 * @since 4.17.0 In cross-origin embeds, use the inline login form instead:
93 * wp-login.php rejects a redirect_to pointing at an external site, which
94 * would strand the donor on the WordPress admin after logging in.
95 */
96 const redirectToLoginPage = (e) => {
97 e.preventDefault();
98
99 if (isCrossOriginEmbed) {
100 toggleShowLogin();
101 return;
102 }
103
104 const loginUrl = getRedirectUrl(new URL(loginRedirectUrl));
105 navigateTop(loginUrl);
106 };
107
108 return (
109 <>
110 {isAuth && (
111 <p style={{textAlign: 'center'}}>
112 <em>{loginConfirmation}</em>
113 </p>
114 )}
115 {!isAuth && showLogin && (
116 <LoginForm success={() => setIsAuth(true)} lostPasswordUrl={lostPasswordUrl} nodeName={name}>
117 <Login />
118 <Password />
119 </LoginForm>
120 )}
121 {!isAuth && !showLogin && (
122 <div style={{display: 'flex'}}>
123 {loginRedirect ? (
124 <LoginNotice onClick={redirectToLoginPage}>{loginNotice}</LoginNotice>
125 ) : (
126 <LoginNotice onClick={toggleShowLogin}>{loginNotice}</LoginNotice>
127 )}
128 </div>
129 )}
130 </>
131 );
132 }
133
134 /**
135 * @since 4.3.0 replace "Forgot your password" anchor element with a button since it does not contain a valid href attribute.
136 */
137 const LoginForm = ({children, success, lostPasswordUrl, nodeName}) => {
138 const {authUrl} = getWindowData();
139 const {useWatch, useFormContext} = window.givewp.form.hooks;
140 const {setValue, getValues} = useFormContext();
141 const {firstName, lastName, email} = getValues();
142
143 const login = useWatch({name: 'login'});
144 const password = useWatch({name: 'password'});
145
146 const [errorMessage, setErrorMessage] = useState<string>('');
147
148 const tryLogin = async (event) => {
149 event.preventDefault();
150
151 const {response} = await postData(authUrl, {
152 login: login,
153 password: password,
154 });
155
156 const {data: responseData} = await response.json();
157 if (response.ok) {
158 setValue('firstName', firstName || responseData.firstName);
159 setValue('lastName', lastName || responseData.lastName);
160 setValue('email', email || responseData.email);
161
162 /*
163 * Inside a cross-site iframe the browser drops the login cookie, so the
164 * server also returns a signed token that rides along with every
165 * validate and donate request instead.
166 */
167 setAuthToken(responseData.authToken);
168
169 success();
170 } else {
171 setErrorMessage(
172 'authentication_error' === responseData.type
173 ? responseData.message
174 : __('Something went wrong. Please try or again or contact a site administrator.', 'give')
175 );
176 }
177 };
178
179 return (
180 <div className={styles['authentication__login-form']}>
181 <div className={styles['authentication__login-form__fields-wrapper']}>{children}</div>
182
183 {!!errorMessage && <FieldError error={errorMessage} name={nodeName} />}
184
185 <div className={styles['authentication__login-form__buttons-wrapper']}>
186 <div className={styles['authentication__login-form__reset-wrapper']}>
187 {__('Forgot your password?', 'give')}
188 <button
189 className={styles['authentication__login-form__reset-button']}
190 onClick={(event) => {
191 event.preventDefault();
192
193 if (isCrossOriginEmbed) {
194 window.open(lostPasswordUrl, '_blank', 'noopener');
195 return;
196 }
197
198 const passwordResetUrl = getRedirectUrl(new URL(lostPasswordUrl));
199 navigateTop(passwordResetUrl);
200 }}
201 >
202 <span>{__('Reset', 'give')}</span>
203 </button>
204 </div>
205 <button className={styles['authentication__login-form__login-button']} onClick={tryLogin}>
206 {__('Log In', 'give')}
207 </button>
208 </div>
209 </div>
210 );
211 };
212
213 const LoginNotice = ({children, onClick}) => {
214 return (
215 <button className={styles['authentication__login-notice']} onClick={onClick}>
216 {children}
217 </button>
218 );
219 };
220