Implement create account and login; fix validation issues
This commit is contained in:
parent
bc7dc27443
commit
a25e4726e5
|
@ -33,11 +33,11 @@ class Dictionary {
|
||||||
if ($insert_dictionary === true) {
|
if ($insert_dictionary === true) {
|
||||||
$new_dictionary_id = $this->db->lastInsertId();
|
$new_dictionary_id = $this->db->lastInsertId();
|
||||||
|
|
||||||
$insert_linguistics_query = "INSERT INTO dictionary_linguistics (dictionary, partsOfSpeech, phonology)
|
$insert_linguistics_query = "INSERT INTO dictionary_linguistics (dictionary, parts_of_speech, phonology)
|
||||||
VALUES ($new_dictionary_id, ?, ?)";
|
VALUES ($new_dictionary_id, ?, ?)";
|
||||||
$insert_linguistics = $this->db->execute($insert_linguistics_query, array(
|
$insert_linguistics = $this->db->execute($insert_linguistics_query, array(
|
||||||
json_encode($this->defaults['partsOfSpeech']),
|
json_encode($this->defaults['partsOfSpeech']),
|
||||||
json_encode($this->defaults['phonotactics']),
|
json_encode($this->defaults['phonology']),
|
||||||
));
|
));
|
||||||
|
|
||||||
if ($insert_linguistics === true) {
|
if ($insert_linguistics === true) {
|
||||||
|
|
|
@ -13,8 +13,8 @@ class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logIn ($email, $password) {
|
public function logIn ($email, $password) {
|
||||||
$query = 'SELECT * FROM users WHERE email=?';
|
$query = 'SELECT * FROM users WHERE email=:email OR username=:email';
|
||||||
$user = $this->db->query($query, array($email))->fetch();
|
$user = $this->db->query($query, array(':email' => $email))->fetch();
|
||||||
if ($user) {
|
if ($user) {
|
||||||
if ($user['old_password'] !== null) {
|
if ($user['old_password'] !== null) {
|
||||||
if ($user['old_password'] === crypt($password, $email)) {
|
if ($user['old_password'] === crypt($password, $email)) {
|
||||||
|
@ -23,6 +23,7 @@ class User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (password_verify($password, $user['password'])) {
|
} else if (password_verify($password, $user['password'])) {
|
||||||
|
$this->db->execute('UPDATE users SET last_login=' . time() . ' WHERE id=' . $user['id']);
|
||||||
return $this->generateUserToken($user['id'], $user['current_dictionary']);
|
return $this->generateUserToken($user['id'], $user['current_dictionary']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,11 +42,18 @@ class User {
|
||||||
return $user->rowCount() > 0;
|
return $user->rowCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create ($email, $password) {
|
public function create ($email, $password, $user_data) {
|
||||||
$insert_user_query = 'INSERT INTO users (email, password) VALUES (?, ?)';
|
$insert_user_query = 'INSERT INTO users (email, password, public_name, username, allow_email, created_on)
|
||||||
|
VALUES (?, ?, ?, ?, ?, '. time() .')';
|
||||||
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
$insert_user = $this->db->execute($insert_user_query, array($email, $password_hash));
|
$insert_user = $this->db->execute($insert_user_query, array(
|
||||||
|
$email,
|
||||||
|
$password_hash,
|
||||||
|
$user_data['publicName'] !== '' ? $user_data['publicName'] : null,
|
||||||
|
$user_data['username'] !== '' ? $user_data['username'] : null,
|
||||||
|
$user_data['allowEmail'] ? 1 : 0,
|
||||||
|
));
|
||||||
if ($insert_user === true) {
|
if ($insert_user === true) {
|
||||||
$new_user_id = $this->db->lastInsertId();
|
$new_user_id = $this->db->lastInsertId();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ switch ($action) {
|
||||||
if (isset($request['email']) && isset($request['password'])) {
|
if (isset($request['email']) && isset($request['password'])) {
|
||||||
$user = new User();
|
$user = new User();
|
||||||
if (!$user->emailExists($request['email'])) {
|
if (!$user->emailExists($request['email'])) {
|
||||||
$token = $user->create($request['email'], $request['password']);
|
$token = $user->create($request['email'], $request['password'], $request['userData']);
|
||||||
if ($token !== false) {
|
if ($token !== false) {
|
||||||
return Response::json(array(
|
return Response::json(array(
|
||||||
'data' => $token,
|
'data' => $token,
|
||||||
|
|
|
@ -24,6 +24,7 @@ export class LoginForm extends Component {
|
||||||
loginPassword: '',
|
loginPassword: '',
|
||||||
loginEmailError: '',
|
loginEmailError: '',
|
||||||
loginPasswordError: '',
|
loginPasswordError: '',
|
||||||
|
loginFormIsValid: true,
|
||||||
signupEmail: '',
|
signupEmail: '',
|
||||||
signupUsername: '',
|
signupUsername: '',
|
||||||
signupPublicName: '',
|
signupPublicName: '',
|
||||||
|
@ -38,48 +39,104 @@ export class LoginForm extends Component {
|
||||||
signupUsernameChecking: false,
|
signupUsernameChecking: false,
|
||||||
signupEmailIsUnique: true,
|
signupEmailIsUnique: true,
|
||||||
signupUsernameIsUnique: true,
|
signupUsernameIsUnique: true,
|
||||||
|
signupFormIsValid: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get loginFormIsValid () {
|
||||||
|
const {
|
||||||
|
loginEmailError,
|
||||||
|
loginPasswordError,
|
||||||
|
} = this.state;
|
||||||
|
return loginEmailError === '' && loginPasswordError === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
get signupFormIsValid () {
|
||||||
|
const {
|
||||||
|
signupEmailError,
|
||||||
|
signupEmailChecking,
|
||||||
|
signupEmailIsUnique,
|
||||||
|
signupUsernameError,
|
||||||
|
signupUsernameChecking,
|
||||||
|
signupUsernameIsUnique,
|
||||||
|
signupPasswordError,
|
||||||
|
signupConfirmError,
|
||||||
|
} = this.state;
|
||||||
|
return !signupEmailChecking && !signupUsernameChecking
|
||||||
|
&& signupEmailIsUnique && signupUsernameIsUnique
|
||||||
|
&& signupEmailError === '' && signupUsernameError === ''
|
||||||
|
&& signupPasswordError === '' && signupConfirmError === '';
|
||||||
|
}
|
||||||
|
|
||||||
changeTab (tab) {
|
changeTab (tab) {
|
||||||
this.setState({ visibleTab: tab });
|
this.setState({ visibleTab: tab });
|
||||||
}
|
}
|
||||||
|
|
||||||
updateField (field, event) {
|
updateField (field, event) {
|
||||||
const requiredFields = ['loginEmail', 'loginPassword', 'signupEmail', 'signupPassword', 'signupConfirm'];
|
|
||||||
const {value, checked} = event.target;
|
const {value, checked} = event.target;
|
||||||
const fieldUpdate = {};
|
const fieldUpdate = {};
|
||||||
|
const fieldErrors = this.validateField(field, value);
|
||||||
|
fieldUpdate[field] = (field === 'signupAllowEmail') ? checked : value;
|
||||||
|
this.setState(Object.assign(fieldUpdate, fieldErrors));
|
||||||
|
}
|
||||||
|
|
||||||
|
validateField (field, value) {
|
||||||
|
const fieldErrors = {};
|
||||||
const errorFieldName = `${field}Error`;
|
const errorFieldName = `${field}Error`;
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
|
const requiredFields = ['loginEmail', 'loginPassword', 'signupEmail', 'signupPassword', 'signupConfirm'];
|
||||||
if (requiredFields.includes(field)) {
|
if (requiredFields.includes(field)) {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
fieldUpdate[errorFieldName] = 'This field must not be blank';
|
fieldErrors[errorFieldName] = 'This field must not be blank';
|
||||||
} else if (field.includes('Email') && !/.+@.+/g.test(value)) {
|
} else if (field === 'signupEmail' && !/.+@.+/g.test(value)) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
fieldUpdate[errorFieldName] = 'The email address you entered looks wrong';
|
fieldErrors[errorFieldName] = 'The email address you entered looks wrong';
|
||||||
} else if (field === 'signupPassword' && value.length < 6) {
|
} else if (field === 'signupPassword' && value.length < 6) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
fieldUpdate[errorFieldName] = 'Please make your password at least 6 characters long';
|
fieldErrors[errorFieldName] = 'Please make your password at least 6 characters long';
|
||||||
} else if ((field === 'signupPassword' && value !== this.state.signupConfirm)
|
} else if (field === 'signupConfirm' && value !== this.state.signupPassword) {
|
||||||
|| (field === 'signupConfirm' && value !== this.state.signupPassword)) {
|
|
||||||
isValid = false;
|
isValid = false;
|
||||||
fieldUpdate[errorFieldName] = 'Your passwords must match';
|
fieldErrors[errorFieldName] = 'Your passwords must match';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field === 'signupUsername') {
|
if (field === 'signupUsername') {
|
||||||
if (value !== '' && /[^a-zA-Z0-9]+/g.test(value)) {
|
if (value !== '' && /[^a-zA-Z0-9]+/g.test(value)) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
fieldUpdate[errorFieldName] = 'Please use only letters and numbers';
|
fieldErrors[errorFieldName] = 'Please use only letters and numbers';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
fieldUpdate[errorFieldName] = '';
|
fieldErrors[errorFieldName] = '';
|
||||||
}
|
}
|
||||||
fieldUpdate[field] = (field === 'signupAllowEmail') ? checked : value;
|
return fieldErrors;
|
||||||
this.setState(fieldUpdate);
|
}
|
||||||
|
|
||||||
|
validateSignupForm (callback) {
|
||||||
|
const fields = ['signupEmail', 'signupUsername', 'signupPassword', 'signupConfirm'];
|
||||||
|
let errors = {};
|
||||||
|
fields.forEach(field => {
|
||||||
|
const fieldErrors = this.validateField(field, this.state[field]);
|
||||||
|
errors = Object.assign(errors, fieldErrors);
|
||||||
|
});
|
||||||
|
errors.signupFormIsValid = !signupEmailChecking && !signupUsernameChecking
|
||||||
|
&& signupEmailIsUnique && signupUsernameIsUnique
|
||||||
|
&& Object.keys(errors).every(field => errors[field] === '');
|
||||||
|
this.setState(errors, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateLoginForm (callback) {
|
||||||
|
const fields = ['loginEmail','loginPassword'];
|
||||||
|
let errors = {};
|
||||||
|
fields.forEach(field => {
|
||||||
|
errors = Object.assign(errors, this.validateField(field, this.state[field]));
|
||||||
|
});
|
||||||
|
errors.loginFormIsValid = Object.keys(errors).every(field => {
|
||||||
|
return errors[field] === '';
|
||||||
|
});
|
||||||
|
this.setState(errors, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkFieldUnique (field, event) {
|
checkFieldUnique (field, event) {
|
||||||
|
@ -117,6 +174,34 @@ export class LoginForm extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logIn () {
|
||||||
|
this.validateLoginForm(() => {
|
||||||
|
if (this.loginFormIsValid) {
|
||||||
|
const { loginEmail, loginPassword } = this.state;
|
||||||
|
this.props.logIn(loginEmail, loginPassword);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createAccount () {
|
||||||
|
this.validateSignupForm(() => {
|
||||||
|
if (this.signupFormIsValid) {
|
||||||
|
const {
|
||||||
|
signupEmail,
|
||||||
|
signupUsername,
|
||||||
|
signupPublicName,
|
||||||
|
signupPassword,
|
||||||
|
signupAllowEmail
|
||||||
|
} = this.state;
|
||||||
|
this.props.signUp(signupEmail, signupPassword, {
|
||||||
|
username: signupUsername,
|
||||||
|
publicName: signupPublicName,
|
||||||
|
allowEmail: signupAllowEmail,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className='columns'>
|
<div className='columns'>
|
||||||
|
@ -137,7 +222,7 @@ export class LoginForm extends Component {
|
||||||
</div>
|
</div>
|
||||||
{this.state.visibleTab === 'login'
|
{this.state.visibleTab === 'login'
|
||||||
? (
|
? (
|
||||||
<div>
|
<div className="has-text-left">
|
||||||
<h3 className='title is-3'>
|
<h3 className='title is-3'>
|
||||||
Log In
|
Log In
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -146,7 +231,16 @@ export class LoginForm extends Component {
|
||||||
Email/Username
|
Email/Username
|
||||||
</label>
|
</label>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<input className='input' type='email' />
|
<input className={`input ${this.state.loginEmailError !== '' && 'is-danger'}`}
|
||||||
|
type='email' onInput={(event) => this.updateField('loginEmail', event)} />
|
||||||
|
{
|
||||||
|
this.state.loginEmailError !== ''
|
||||||
|
? (
|
||||||
|
<div className='help is-danger'>
|
||||||
|
{this.state.loginEmailError}
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='field'>
|
<div className='field'>
|
||||||
|
@ -154,12 +248,28 @@ export class LoginForm extends Component {
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<input className='input' type='password' />
|
<input className={`input ${this.state.loginPasswordError !== '' && 'is-danger'}`}
|
||||||
|
type='password' onInput={(event) => this.updateField('loginPassword', event)}
|
||||||
|
onChange={this.validateLoginForm.bind(this)} />
|
||||||
|
{
|
||||||
|
this.state.loginPasswordError !== ''
|
||||||
|
? (
|
||||||
|
<div className='help is-danger'>
|
||||||
|
{this.state.loginPasswordError}
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='field'>
|
||||||
|
<a className='button is-success'
|
||||||
|
onClick={this.logIn.bind(this)}>
|
||||||
|
Log In
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className="has-text-left">
|
||||||
<h3 className='title is-3'>
|
<h3 className='title is-3'>
|
||||||
Create a New Account
|
Create a New Account
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -285,6 +395,14 @@ export class LoginForm extends Component {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='field'>
|
||||||
|
<div className='control'>
|
||||||
|
<a className='button is-success'
|
||||||
|
onClick={this.createAccount.bind(this)}>
|
||||||
|
Create Account
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,17 +23,7 @@ export class AccountManager extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
logIn (email, password) {
|
logIn (email, password) {
|
||||||
return request('login', { email, password }, response => {
|
return request('login', { email, password }, this.handleResponse.bind(this));
|
||||||
const { data, error } = response;
|
|
||||||
if (error) {
|
|
||||||
console.error(data);
|
|
||||||
} else {
|
|
||||||
store.set('LexicongaToken', data);
|
|
||||||
this.setState({ isLoggedIn: true }, () => {
|
|
||||||
this.props.updater.sync();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logOut () {
|
logOut () {
|
||||||
|
@ -41,6 +31,26 @@ export class AccountManager extends Component {
|
||||||
this.setState({ isLoggedIn: false });
|
this.setState({ isLoggedIn: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signUp (email, password, userData) {
|
||||||
|
request('create-account', {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
userData,
|
||||||
|
}, this.handleResponse.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResponse (response) {
|
||||||
|
const { data, error } = response;
|
||||||
|
if (error) {
|
||||||
|
console.error(data);
|
||||||
|
} else {
|
||||||
|
store.set('LexicongaToken', data);
|
||||||
|
this.setState({ isLoggedIn: true }, () => {
|
||||||
|
this.props.updater.sync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const token = store.get('LexicongaToken');
|
const token = store.get('LexicongaToken');
|
||||||
|
|
||||||
|
@ -60,7 +70,7 @@ export class AccountManager extends Component {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Modal buttonText='Log In/Sign Up' title='Log In/Sign Up'>
|
<Modal buttonText='Log In/Sign Up' title='Log In/Sign Up'>
|
||||||
<LoginForm logIn={this.logIn.bind(this)} signUp={() => {}} />
|
<LoginForm logIn={this.logIn.bind(this)} signUp={this.signUp.bind(this)} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,10 +48,10 @@ export class Header extends Component {
|
||||||
|
|
||||||
<div className={`navbar-menu${ this.state.displayNavMenu ? ' is-active' : '' }`}>
|
<div className={`navbar-menu${ this.state.displayNavMenu ? ' is-active' : '' }`}>
|
||||||
<div className='navbar-end'>
|
<div className='navbar-end'>
|
||||||
<span className='navbar-item'>
|
<span className='navbar-item has-text-right-touch'>
|
||||||
<AccountManager updater={ this.props.updater } />
|
<AccountManager updater={ this.props.updater } />
|
||||||
</span>
|
</span>
|
||||||
<span className='navbar-item'>
|
<span className='navbar-item has-text-right-touch'>
|
||||||
<Modal buttonText='Help' title='Lexiconga Help'>
|
<Modal buttonText='Help' title='Lexiconga Help'>
|
||||||
<div className='content has-text-left'>
|
<div className='content has-text-left'>
|
||||||
<div dangerouslySetInnerHTML={{
|
<div dangerouslySetInnerHTML={{
|
||||||
|
|
Loading…
Reference in New Issue