Merge branch 'master' into logout-bug

This commit is contained in:
Kim Gardner
2018-03-14 18:18:50 -04:00
committed by GitHub
4 changed files with 85 additions and 4 deletions
@@ -76,7 +76,8 @@ class UserDetailComment extends React.Component {
</div>
</div>
<div className={styles.story}>
Story: {comment.asset.title}
{t('common.story')}:{' '}
{comment.asset.title ? comment.asset.title : comment.asset.url}
{
<Link to={`/admin/moderate/${comment.asset.id}`}>
{t('modqueue.moderate')}
@@ -109,6 +110,7 @@ class UserDetailComment extends React.Component {
<div className={styles.sideActions}>
<IfHasLink text={comment.body}>
<span className={styles.hasLinks}>
{/* TODO: translate string */}
<Icon name="error_outline" /> Contains Link
</span>
</IfHasLink>
@@ -122,7 +122,8 @@ class Comment extends React.Component {
</div>
<div className={styles.moderateArticle}>
Story: {comment.asset.title}
{t('common.story')}:{' '}
{comment.asset.title ? comment.asset.title : comment.asset.url}
{!currentAsset && (
<Link to={`/admin/moderate/${comment.asset.id}`}>
{t('modqueue.moderate')}
@@ -156,6 +157,7 @@ class Comment extends React.Component {
<div className={styles.sideActions}>
<IfHasLink text={comment.body}>
<span className={styles.hasLinks}>
{/* TODO: translate string */}
<Icon name="error_outline" /> Contains Link
</span>
</IfHasLink>
+66 -2
View File
@@ -1,7 +1,7 @@
const uuid = require('uuid');
const bcrypt = require('bcryptjs');
const errors = require('../errors');
const { some, merge } = require('lodash');
const { difference, sample, some, merge, random } = require('lodash');
const { ROOT_URL } = require('../config');
const { jwt: JWT_SECRET } = require('../secrets');
const debug = require('debug')('talk:services:users');
@@ -346,6 +346,70 @@ class UsersService {
return username.replace(/ /g, '_').replace(/[^a-zA-Z_]/g, '');
}
/**
* Creates the initial username for an external account. Searches to make
* sure username not already used. Adds a random number if username already
* in use.
*/
static async getInitialUsername(username) {
const MAX_ATTEMPTS = 10;
const END_NUMBER_MAX = 99999;
const GROUP_ATTEMPTS = 50;
// Cast the original username.
const castedName = UsersService.castUsername(username);
const lowercaseUsername = castedName.toLowerCase();
// Try to see if our first guess has been taken.
const existingUserWithName = await UserModel.findOne({
lowercaseUsername,
});
if (!existingUserWithName) {
return castedName;
}
// Our first username was taken, lets try to find a non-taken name.
for (let i = 0; i < MAX_ATTEMPTS; i++) {
// Generate `GROUP_ATTEMPTS` guesses for the username.
const usernameGuesses = Array.from(Array(GROUP_ATTEMPTS)).map(
() => `${castedName}_${random(0, END_NUMBER_MAX)}`
);
// Map them all to lowercase.
const lowercaseUsernameGuesses = usernameGuesses.map(guess =>
guess.toLowerCase()
);
// See if any of these users aren't taken already.
const existingUsernames = (await UserModel.find(
{
lowercaseUsername: { $in: lowercaseUsernameGuesses },
},
{ lowercaseUsername: 1 }
)).map(({ lowercaseUsername }) => lowercaseUsername);
if (existingUsernames.length === lowercaseUsernameGuesses.length) {
// The number of found users is the same as the number of username
// guesses, aka, all the usernames are taken.
continue;
}
// At least one of the usernames wasn't taken! Let's filter this to only
// include unused usernames and grab one random entry from the list.
const foundLowercaseUsernameIndex = lowercaseUsernameGuesses.indexOf(
sample(difference(lowercaseUsernameGuesses, existingUsernames))
);
// Now we get the uppercase version of that string.
return usernameGuesses[foundLowercaseUsernameIndex];
}
throw new Error(
'cannot find free name after ' +
(MAX_ATTEMPTS * GROUP_ATTEMPTS + 1) +
' tries'
);
}
/**
* Finds a user given a social profile and if the user does not exist, creates
* them.
@@ -368,7 +432,7 @@ class UsersService {
// User does not exist and need to be created.
// Create an initial username for the user.
let username = UsersService.castUsername(displayName);
let username = await UsersService.getInitialUsername(displayName);
// The user was not found, lets create them!
user = new UserModel({
+13
View File
@@ -58,6 +58,19 @@ describe('services.UsersService', () => {
});
});
describe('#getInitialUsername', () => {
it('should find the first result when there is no conflict', async () => {
const username = await UsersService.getInitialUsername(
'TheGreatSockmonster'
);
expect(username).to.equal('TheGreatSockmonster');
});
it('should find a first result when there is a conflict', async () => {
const username = await UsersService.getInitialUsername('Sockmonster');
expect(username).to.match(/Sockmonster_[0-9]+/);
});
});
describe('#findPublicByIdArray()', () => {
it('should find an array of users from an array of ids', async () => {
const ids = mockUsers.map(user => user.id);