mirror of
https://github.com/wassname/talk.git
synced 2026-07-29 11:28:24 +08:00
Merge branch 'master' into community-flags
This commit is contained in:
@@ -74,8 +74,12 @@
|
||||
background-color: transparent;
|
||||
transition: background-color 200ms;
|
||||
|
||||
&:hover {
|
||||
background-color: #232323;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #232323;
|
||||
background-color: #232323;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ class ModerationContainer extends Component {
|
||||
activeTab={activeTab}
|
||||
singleView={moderation.singleView}
|
||||
selectedIndex={this.state.selectedIndex}
|
||||
bannedWords={settings.wordlist.banned}
|
||||
suspectWords={settings.wordlist.suspect}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
acceptComment={props.acceptComment}
|
||||
|
||||
@@ -24,6 +24,7 @@ const ModerationQueue = ({comments, selectedIndex, commentCount, singleView, loa
|
||||
commentType={activeTab}
|
||||
selected={i === selectedIndex}
|
||||
suspectWords={props.suspectWords}
|
||||
bannedWords={props.bannedWords}
|
||||
actions={actionsMap[status]}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
acceptComment={props.acceptComment}
|
||||
@@ -47,6 +48,7 @@ const ModerationQueue = ({comments, selectedIndex, commentCount, singleView, loa
|
||||
};
|
||||
|
||||
ModerationQueue.propTypes = {
|
||||
bannedWords: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
currentAsset: PropTypes.object,
|
||||
showBanUserDialog: PropTypes.func.isRequired,
|
||||
|
||||
@@ -19,6 +19,7 @@ const lang = new I18n(translations);
|
||||
|
||||
const Comment = ({actions = [], ...props}) => {
|
||||
const links = linkify.getMatches(props.comment.body);
|
||||
const linkText = links ? links.map(link => link.raw) : [];
|
||||
const actionSummaries = props.comment.action_summaries;
|
||||
return (
|
||||
<li tabIndex={props.index} className={`mdl-card ${props.selected ? 'mdl-shadow--8dp' : 'mdl-shadow--2dp'} ${styles.Comment} ${styles.listItem}`}>
|
||||
@@ -49,9 +50,9 @@ const Comment = ({actions = [], ...props}) => {
|
||||
</div>
|
||||
<div className={styles.itemBody}>
|
||||
<p className={styles.body}>
|
||||
<Linkify component='span' properties={{style: linkStyles}}>
|
||||
<Highlighter searchWords={props.suspectWords} textToHighlight={props.comment.body}/>
|
||||
</Linkify>
|
||||
<Highlighter
|
||||
searchWords={[...props.suspectWords, ...props.bannedWords, ...linkText]}
|
||||
textToHighlight={props.comment.body} />
|
||||
</p>
|
||||
<div className={styles.sideActions}>
|
||||
{links ? <span className={styles.hasLinks}><Icon name='error_outline'/> Contains Link</span> : null}
|
||||
@@ -77,6 +78,7 @@ Comment.propTypes = {
|
||||
acceptComment: PropTypes.func.isRequired,
|
||||
rejectComment: PropTypes.func.isRequired,
|
||||
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
bannedWords: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
currentAsset: PropTypes.object,
|
||||
comment: PropTypes.shape({
|
||||
body: PropTypes.string.isRequired,
|
||||
@@ -92,9 +94,4 @@ Comment.propTypes = {
|
||||
})
|
||||
};
|
||||
|
||||
const linkStyles = {
|
||||
backgroundColor: 'rgb(255, 219, 135)',
|
||||
padding: '1px 2px'
|
||||
};
|
||||
|
||||
export default Comment;
|
||||
|
||||
@@ -1,35 +1,53 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import React, { PropTypes } from 'react';
|
||||
import CommentCount from './CommentCount';
|
||||
import styles from './styles.css';
|
||||
import {SelectField, Option} from 'react-mdl-selectfield';
|
||||
import { SelectField, Option } from 'react-mdl-selectfield';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from 'coral-admin/src/translations.json';
|
||||
import {Link} from 'react-router';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const ModerationMenu = ({asset, premodCount, rejectedCount, flaggedCount, selectSort, sort}) => {
|
||||
const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
|
||||
const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
|
||||
const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
|
||||
const ModerationMenu = (
|
||||
{ asset, premodCount, rejectedCount, flaggedCount, selectSort, sort }
|
||||
) => {
|
||||
const premodPath = asset
|
||||
? `/admin/moderate/premod/${asset.id}`
|
||||
: '/admin/moderate/premod';
|
||||
const rejectPath = asset
|
||||
? `/admin/moderate/rejected/${asset.id}`
|
||||
: '/admin/moderate/rejected';
|
||||
const flagPath = asset
|
||||
? `/admin/moderate/flagged/${asset.id}`
|
||||
: '/admin/moderate/flagged';
|
||||
|
||||
return (
|
||||
<div className='mdl-tabs'>
|
||||
<div className="mdl-tabs">
|
||||
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
|
||||
<div className={styles.tabBarPadding}/>
|
||||
<div className={styles.tabBarPadding} />
|
||||
<div>
|
||||
<Link to={premodPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
|
||||
<Link
|
||||
to={premodPath}
|
||||
className={`mdl-tabs__tab ${styles.tab}`}
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('modqueue.premod')} <CommentCount count={premodCount} />
|
||||
</Link>
|
||||
<Link to={rejectPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
|
||||
{lang.t('modqueue.rejected')} <CommentCount count={rejectedCount} />
|
||||
</Link>
|
||||
<Link to={flagPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
|
||||
<Link
|
||||
to={flagPath}
|
||||
className={`mdl-tabs__tab ${styles.tab}`}
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('modqueue.flagged')} <CommentCount count={flaggedCount} />
|
||||
</Link>
|
||||
<Link
|
||||
to={rejectPath}
|
||||
className={`mdl-tabs__tab ${styles.tab}`}
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('modqueue.rejected')} <CommentCount count={rejectedCount} />
|
||||
</Link>
|
||||
</div>
|
||||
<SelectField
|
||||
className={styles.selectField}
|
||||
label='Sort'
|
||||
label="Sort"
|
||||
value={sort}
|
||||
onChange={sort => selectSort(sort)}>
|
||||
<Option value={'REVERSE_CHRONOLOGICAL'}>Newest First</Option>
|
||||
|
||||
@@ -170,7 +170,7 @@ span {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
font-size: 18px;
|
||||
width: 100%;
|
||||
max-width: 660px;
|
||||
max-width: 700px;
|
||||
min-width: 400px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
@@ -192,7 +192,7 @@ span {
|
||||
}
|
||||
|
||||
&.selected {
|
||||
max-width: 670px;
|
||||
max-width: 720px;
|
||||
max-height: 410px;
|
||||
}
|
||||
|
||||
@@ -216,10 +216,12 @@ span {
|
||||
justify-content: space-between;
|
||||
|
||||
.author {
|
||||
font-weight: 600;
|
||||
font-weight: 300;
|
||||
min-width: 230px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #262626;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +244,11 @@ span {
|
||||
}
|
||||
|
||||
.created {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
color: #262626;
|
||||
font-size: 14px;
|
||||
margin-left: 15px;
|
||||
line-height: 1px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.actionButton {
|
||||
@@ -260,6 +263,7 @@ span {
|
||||
max-width: 500px;
|
||||
word-wrap: break-word;
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.flagged {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
.Reply {
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.Comment {
|
||||
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.pendingComment {
|
||||
|
||||
@@ -121,7 +121,7 @@ class Embed extends Component {
|
||||
<div className="commentStream">
|
||||
<TabBar onChange={this.changeTab} activeTab={activeTab}>
|
||||
<Tab><Count count={asset.commentCount}/></Tab>
|
||||
<Tab>{lang.t('profile')}</Tab>
|
||||
<Tab>{lang.t('MY_COMMENTS')}</Tab>
|
||||
<Tab restricted={!isAdmin}>Configure Stream</Tab>
|
||||
</TabBar>
|
||||
{loggedIn && <UserBox user={user} logout={() => this.props.logout().then(refetch)} changeTab={this.changeTab}/>}
|
||||
@@ -160,7 +160,6 @@ class Embed extends Component {
|
||||
charCount={asset.settings.charCountEnable && asset.settings.charCount} />
|
||||
: null
|
||||
}
|
||||
<ModerationLink assetId={asset.id} isAdmin={isAdmin} />
|
||||
</RestrictedContent>
|
||||
</div>
|
||||
: <p>{asset.settings.closedMessage}</p>
|
||||
@@ -170,6 +169,7 @@ class Embed extends Component {
|
||||
refetch={refetch}
|
||||
offset={signInOffset}/>}
|
||||
{loggedIn && user && <ChangeUsernameContainer loggedIn={loggedIn} offset={signInOffset} user={user} />}
|
||||
{loggedIn && <ModerationLink assetId={asset.id} isAdmin={isAdmin} />}
|
||||
{
|
||||
highlightedComment &&
|
||||
<Comment
|
||||
|
||||
@@ -44,14 +44,15 @@ class LoadMore extends React.Component {
|
||||
render () {
|
||||
const {assetId, comments, loadMore, moreComments, parentId, replyCount, topLevel} = this.props;
|
||||
return moreComments
|
||||
? <Button
|
||||
className='coral-load-more'
|
||||
onClick={() => {
|
||||
this.initialState = false;
|
||||
loadMoreComments(assetId, comments, loadMore, parentId);
|
||||
}}>
|
||||
{topLevel ? lang.t('viewMoreComments') : this.replyCountFormat(replyCount)}
|
||||
</Button>
|
||||
? <div className='coral-load-more'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.initialState = false;
|
||||
loadMoreComments(assetId, comments, loadMore, parentId);
|
||||
}}>
|
||||
{topLevel ? lang.t('viewMoreComments') : this.replyCountFormat(replyCount)}
|
||||
</Button>
|
||||
</div>
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ hr {
|
||||
|
||||
.coral-plugin-commentcontent-text {
|
||||
margin-bottom: 7px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.coral-plugin-author-name-text {
|
||||
@@ -417,8 +418,11 @@ button.comment__action-button[disabled],
|
||||
|
||||
/* Load More */
|
||||
|
||||
button.coral-load-more {
|
||||
width: 100%;
|
||||
.coral-load-more {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.coral-load-more button {
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
background-color: #2376D8;
|
||||
@@ -427,9 +431,10 @@ button.coral-load-more {
|
||||
border-radius: 2px;
|
||||
line-height: 1em;
|
||||
text-transform: capitalize;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button.coral-load-more:hover {
|
||||
.coral-load-more:hover button {
|
||||
background-color: #4399FF;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,16 @@ Talk.render = function (el, opts) {
|
||||
el.id = `_${Math.random()}`;
|
||||
}
|
||||
|
||||
let asset_url = opts.asset_url || window.location.href.split('#')[0];
|
||||
let asset_url = opts.asset_url;
|
||||
if (!asset_url) {
|
||||
try {
|
||||
asset_url = document.querySelector('link[rel="canonical"]').href;
|
||||
} catch (e) {
|
||||
console.warn('This page does not include a canonical link tag. Talk has inferred this asset_url from the window object. Query params have been stripped, which may cause a single thread to be present across multiple pages.');
|
||||
asset_url = window.location.origin + window.location.pathname;
|
||||
}
|
||||
}
|
||||
|
||||
let comment = window.location.hash.slice(1);
|
||||
|
||||
let query = {
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as actions from '../constants/asset';
|
||||
import coralApi from '../helpers/response';
|
||||
import {addNotification} from '../actions/notification';
|
||||
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import I18n from '../../coral-framework/modules/i18n/i18n';
|
||||
import translations from './../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import I18n from '../../coral-framework/modules/i18n/i18n';
|
||||
import translations from './../translations';
|
||||
const lang = new I18n(translations);
|
||||
import * as actions from '../constants/auth';
|
||||
import coralApi, {base} from '../helpers/response';
|
||||
import {pym} from 'coral-framework';
|
||||
|
||||
// Dialog Actions
|
||||
export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset});
|
||||
@@ -135,7 +136,8 @@ const forgotPassowordFailure = () => ({type: actions.FETCH_FORGOT_PASSWORD_FAILU
|
||||
|
||||
export const fetchForgotPassword = email => (dispatch) => {
|
||||
dispatch(forgotPassowordRequest(email));
|
||||
coralApi('/account/password/reset', {method: 'POST', body: {email}})
|
||||
const redirectUri = pym.parentUrl || location.href;
|
||||
coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}})
|
||||
.then(() => dispatch(forgotPassowordSuccess()))
|
||||
.catch(error => dispatch(forgotPassowordFailure(error)));
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {pym} from 'coral-framework';
|
||||
import {pym} from '../../coral-framework';
|
||||
|
||||
export const addNotification = (notifType, text) => {
|
||||
pym.sendMessage('coral-alert', `${notifType}|${text}`);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"en": {
|
||||
"MY_COMMENTS": "My Comments",
|
||||
"profile": "Profile",
|
||||
"successUpdateSettings": "The changes you have made have been applied to the comment stream on this article",
|
||||
"successNameUpdate": "Your username has been updated",
|
||||
@@ -15,7 +16,7 @@
|
||||
"viewReply": "view reply",
|
||||
"viewAllRepliesInitial": "view all {0} replies",
|
||||
"viewAllReplies": "view {0} replies",
|
||||
"newCount": "View {0} more {1}",
|
||||
"newCount": "View {0} new {1}",
|
||||
"comment": "comment",
|
||||
"comments": "comments",
|
||||
"error": {
|
||||
@@ -40,13 +41,14 @@
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"MY_COMMENTS": "Mis Comentarios",
|
||||
"profile": "Perfil",
|
||||
"successUpdateSettings": "La configuración de este articulo fue actualizada",
|
||||
"successBioUpdate": "Tu bio fue actualizada",
|
||||
"contentNotAvailable": "El contenido no se encuentra disponible",
|
||||
"bannedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios.",
|
||||
"editNameMsg": "",
|
||||
"viewMoreComments": "Var commentarios más",
|
||||
"viewMoreComments": "Ver commentarios más",
|
||||
"viewReply": "ver respuesta",
|
||||
"viewAllRepliesInitial": "ver todas las {0} respuestas",
|
||||
"viewAllReplies": "ver {0} respuestas",
|
||||
|
||||
@@ -1,16 +1,74 @@
|
||||
@custom-media --big-viewport (min-width: 780px);
|
||||
|
||||
.myComment {
|
||||
border-bottom: 1px solid lightgrey;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.myComment:last-child {
|
||||
border-bottom: none;
|
||||
border-bottom: solid 1px #EBEBEB;
|
||||
}
|
||||
|
||||
.assetURL {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.commentBody {
|
||||
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
ul {
|
||||
min-width: 136px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:nth-child(1) {
|
||||
color: #5394D7;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
color: #909090;
|
||||
}
|
||||
|
||||
|
||||
i {
|
||||
margin-right: 5px;
|
||||
font-size: 15px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@custom-media --mobile-viewport (max-width: 480px);
|
||||
|
||||
@media (--mobile-viewport) {
|
||||
.myComment {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
li {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pubdate {
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
margin: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Icon } from '../coral-ui';
|
||||
import styles from './Comment.css';
|
||||
import PubDate from '../coral-plugin-pubdate/PubDate';
|
||||
import Content from '../coral-plugin-commentcontent/CommentContent';
|
||||
|
||||
const Comment = props => {
|
||||
return (
|
||||
<div className={styles.myComment}>
|
||||
<p className="myCommentAsset">
|
||||
<a className={`${styles.assetURL} myCommentAnchor`} href='#' onClick={props.link(`${props.asset.url}#${props.comment.id}`)}>{props.asset.title ? props.asset.title : props.asset.url}</a>
|
||||
</p>
|
||||
<p className={`${styles.commentBody} myCommentBody`}>{props.comment.body}</p>
|
||||
<div>
|
||||
<Content
|
||||
className={`${styles.commentBody} myCommentBody`}
|
||||
body={props.comment.body}
|
||||
/>
|
||||
<p className="myCommentAsset">
|
||||
<a
|
||||
className={`${styles.assetURL} myCommentAnchor`}
|
||||
href="#"
|
||||
onClick={props.link(`${props.asset.url}`)}>
|
||||
Story: {props.asset.title ? props.asset.title : props.asset.url}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.sidebar}>
|
||||
<ul>
|
||||
<li>
|
||||
<a onClick={props.link(`${props.asset.url}#${props.comment.id}`)}>
|
||||
<Icon name="open_in_new" />View Conversation
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<Icon name="schedule" />
|
||||
<PubDate
|
||||
className={styles.pubdate}
|
||||
created_at={props.comment.created_at}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
.base li:hover {
|
||||
background: #f3f3f3;
|
||||
background: #d5d5d5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ router.get('/password-reset', (req, res) => {
|
||||
|
||||
// TODO: store the redirect uri in the token or something fancy.
|
||||
// admins and regular users should probably be redirected to different places.
|
||||
res.render('admin/password-reset', {redirectUri: process.env.TALK_ROOT_URL});
|
||||
res.render('admin/password-reset');
|
||||
});
|
||||
|
||||
router.get('*', (req, res) => {
|
||||
|
||||
@@ -41,14 +41,14 @@ router.post('/email/verify', (req, res, next) => {
|
||||
* if it does, create a JWT and send an email
|
||||
*/
|
||||
router.post('/password/reset', (req, res, next) => {
|
||||
const {email} = req.body;
|
||||
const {email, loc} = req.body;
|
||||
|
||||
if (!email) {
|
||||
return next('you must submit an email when requesting a password.');
|
||||
}
|
||||
|
||||
UsersService
|
||||
.createPasswordResetToken(email)
|
||||
.createPasswordResetToken(email, loc)
|
||||
.then((token) => {
|
||||
|
||||
// Check to see if the token isn't defined.
|
||||
@@ -101,11 +101,11 @@ router.put('/password/reset', (req, res, next) => {
|
||||
|
||||
UsersService
|
||||
.verifyPasswordResetToken(token)
|
||||
.then((user) => {
|
||||
return UsersService.changePassword(user.id, password);
|
||||
.then(([user, loc]) => {
|
||||
return Promise.all([UsersService.changePassword(user.id, password), loc]);
|
||||
})
|
||||
.then(() => {
|
||||
res.status(204).end();
|
||||
.then(([ , loc]) => {
|
||||
res.json({redirect: loc});
|
||||
})
|
||||
.catch(() => {
|
||||
next(authorization.ErrNotAuthorized);
|
||||
|
||||
+23
-4
@@ -1,4 +1,5 @@
|
||||
const bcrypt = require('bcrypt');
|
||||
const url = require('url');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const Wordlist = require('./wordlist');
|
||||
|
||||
@@ -16,6 +17,7 @@ const USER_ROLES = require('../models/user').USER_ROLES;
|
||||
const RECAPTCHA_WINDOW_SECONDS = 60 * 10; // 10 minutes.
|
||||
const RECAPTCHA_INCORRECT_TRIGGER = 5; // after 3 incorrect attempts, recaptcha will be required.
|
||||
|
||||
const SettingsService = require('./settings');
|
||||
const ActionsService = require('./actions');
|
||||
const MailerService = require('./mailer');
|
||||
|
||||
@@ -520,15 +522,18 @@ module.exports = class UsersService {
|
||||
* Creates a JWT from a user email. Only works for local accounts.
|
||||
* @param {String} email of the local user
|
||||
*/
|
||||
static createPasswordResetToken(email) {
|
||||
static createPasswordResetToken(email, loc) {
|
||||
if (!email || typeof email !== 'string') {
|
||||
return Promise.reject('email is required when creating a JWT for resetting passord');
|
||||
}
|
||||
|
||||
email = email.toLowerCase();
|
||||
|
||||
return UserModel.findOne({profiles: {$elemMatch: {id: email}}})
|
||||
.then((user) => {
|
||||
return Promise.all([
|
||||
UserModel.findOne({profiles: {$elemMatch: {id: email}}}),
|
||||
SettingsService.retrieve()
|
||||
])
|
||||
.then(([user, settings]) => {
|
||||
if (!user) {
|
||||
|
||||
// Since we don't want to reveal that the email does/doesn't exist
|
||||
@@ -537,9 +542,21 @@ module.exports = class UsersService {
|
||||
return;
|
||||
}
|
||||
|
||||
let redirectDomain;
|
||||
try {
|
||||
redirectDomain = url.parse(loc).hostname;
|
||||
} catch (e) {
|
||||
return Promise.reject('redirect location is invalid');
|
||||
}
|
||||
|
||||
if (settings.domains.whitelist.indexOf(redirectDomain) === -1) {
|
||||
return Promise.reject('redirect location is not on the list of acceptable domains');
|
||||
}
|
||||
|
||||
const payload = {
|
||||
jti: uuid.v4(),
|
||||
email,
|
||||
loc,
|
||||
userId: user.id,
|
||||
version: user.__v
|
||||
};
|
||||
@@ -584,7 +601,9 @@ module.exports = class UsersService {
|
||||
})
|
||||
|
||||
// TODO: add search by __v as well
|
||||
.then((decoded) => UsersService.findById(decoded.userId));
|
||||
.then((decoded) => {
|
||||
return Promise.all([UsersService.findById(decoded.userId), decoded.loc]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import {shallow, mount} from 'enzyme';
|
||||
import {expect} from 'chai';
|
||||
import Comment from '../../../client/coral-plugin-history/Comment';
|
||||
|
||||
describe('coral-plugin-history/Comment', () => {
|
||||
let render;
|
||||
const comment = {body: 'this is a comment', id: '123'};
|
||||
const asset = {url: 'https://google.com'};
|
||||
|
||||
beforeEach(() => {
|
||||
render = shallow(<Comment asset={asset} comment={comment} link={()=>{}}/>);
|
||||
});
|
||||
|
||||
it('should render the provided comment body', () => {
|
||||
const wrapper = mount(<Comment asset={asset} comment={comment} link={()=>{}}/>);
|
||||
expect(wrapper.find('.myCommentBody')).to.have.length(1);
|
||||
expect(wrapper.find('.myCommentBody').text()).to.equal('this is a comment');
|
||||
});
|
||||
|
||||
it('should render the asset url as a link', () => {
|
||||
const wrapper = mount(<Comment asset={asset} comment={comment} link={()=>{}}/>);
|
||||
expect(wrapper.find('.myCommentAnchor')).to.have.length(1);
|
||||
expect(wrapper.find('.myCommentAnchor').text()).to.equal('https://google.com');
|
||||
});
|
||||
|
||||
it('should render the comment with styles', () => {
|
||||
expect(render.props().style).to.be.defined;
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import React from 'react';
|
||||
import {shallow, mount} from 'enzyme';
|
||||
import {expect} from 'chai';
|
||||
import CommentHistory from '../../../client/coral-plugin-history/CommentHistory';
|
||||
|
||||
describe('coral-plugin-history/CommentHistory', () => {
|
||||
let render;
|
||||
const comments = [{body: 'a comment or something', 'status_history':[{'type':'premod', 'created_at':'2016-12-09T01:40:53.327Z', 'assigned_by':null}, {'created_at':'2016-12-09T22:52:44.888Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-09T01:40:53.360Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T22:52:44.893Z', 'id':'3962c2ea-4ec4-42e4-b9bd-c571ff30f56b'}, {'body':'another comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-09T22:53:43.148Z', 'assigned_by':null}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-09T22:53:43.158Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'premod', '__v':0, 'updated_at':'2016-12-09T22:53:43.158Z', 'id':'b51e27af-bcfd-4932-91be-e3f01a4802e6'}, {'body':'can I comment?', 'status_history':[{'type':'premod', 'created_at':'2016-12-13T23:23:47.123Z', 'assigned_by':null}, {'created_at':'2016-12-13T23:23:58.487Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'cef81015-1b53-4d70-b9af-6eca680f22fc', 'created_at':'2016-12-13T23:23:47.131Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-13T23:23:58.493Z', 'id':'dc9d7be1-b911-4dc3-8e1e-400e8b8d110e'}, {'body':'pre-mod comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T21:34:56.994Z', 'assigned_by':null}, {'created_at':'2016-12-08T21:38:04.961Z', 'type':'rejected', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T21:34:56.997Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'rejected', '__v':0, 'updated_at':'2016-12-08T21:38:04.965Z', 'id':'6f02af16-a8f8-4ead-80ea-0d48824eb74d'}, {'body':'a flagged commetn', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T21:38:26.342Z', 'assigned_by':null}, {'created_at':'2016-12-09T23:47:27.009Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T21:38:26.344Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T23:47:27.018Z', 'id':'784c5f91-36b9-4bda-b4ca-a114cef2c9f0'}, {'body':'a post mod comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T22:19:05.870Z', 'assigned_by':null}, {'created_at':'2016-12-09T23:26:41.427Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T22:19:05.874Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T23:26:41.450Z', 'id':'e8b86039-f850-4e53-bd9d-f8c9186a9637'}, {'body':'an actual post-mod comment here', 'status_history':[], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T22:20:11.147Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':null, '__v':0, 'updated_at':'2016-12-08T22:20:11.147Z', 'id':'cff1a318-50c6-431e-9a63-de7a7b7136bf'}];
|
||||
const asset = {
|
||||
'settings': null,
|
||||
'created_at':'2016-12-06T21:36:09.302Z',
|
||||
'url':'localhost:3000/',
|
||||
'scraped':null,
|
||||
'status':'open',
|
||||
'updated_at':'2016-12-08T02:11:15.943Z',
|
||||
'_id':'58472f499e775a38f23d5da0',
|
||||
'type':'article',
|
||||
'closedMessage':null,
|
||||
'id':'7302e637-f884-47c0-9723-02cc10a18617',
|
||||
'closedAt':null
|
||||
};
|
||||
|
||||
comments.forEach((comment) => {
|
||||
comment.asset = asset;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
render = shallow(<CommentHistory comments={comments} asset={asset} link={()=>{}}/>);
|
||||
});
|
||||
|
||||
it('should render Comments as children when given comments and assets', () => {
|
||||
const wrapper = mount(<CommentHistory comments={comments} asset={asset} link={()=>{}}/>);
|
||||
expect(wrapper.find('.commentHistory__list').children()).to.have.length(7);
|
||||
});
|
||||
|
||||
it('should render with styles', () => {
|
||||
expect(render.props().style).to.be.defined;
|
||||
});
|
||||
});
|
||||
@@ -126,7 +126,7 @@
|
||||
},
|
||||
data: JSON.stringify({password: password, token: location.hash.replace('#', '')})
|
||||
}).then(function (success) {
|
||||
location.href = '<%= redirectUri %>';
|
||||
location.href = success.redirect;
|
||||
}).catch(function (error) {
|
||||
showError(error.responseText);
|
||||
});
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
<script src="/embed.js" async onload="
|
||||
Coral.Talk.render(document.getElementById('coralStreamEmbed'), {
|
||||
talk: '/',
|
||||
asset: '<%= asset_url ? asset_url : '' %>'
|
||||
asset_url: '<%= asset_url ? asset_url : '' %>'
|
||||
})
|
||||
"></script>
|
||||
</main>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"@types/express-serve-static-core" "*"
|
||||
"@types/mime" "*"
|
||||
|
||||
abab@^1.0.0:
|
||||
abab@^1.0.0, abab@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
|
||||
|
||||
@@ -76,7 +76,7 @@ acorn-globals@^1.0.4:
|
||||
dependencies:
|
||||
acorn "^2.1.0"
|
||||
|
||||
acorn-globals@^3.0.0:
|
||||
acorn-globals@^3.0.0, acorn-globals@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
|
||||
dependencies:
|
||||
@@ -2022,11 +2022,11 @@ csso@~2.3.1:
|
||||
clap "^1.0.9"
|
||||
source-map "^0.5.3"
|
||||
|
||||
cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0":
|
||||
cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0", "cssom@>= 0.3.2 < 0.4.0":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
|
||||
|
||||
"cssstyle@>= 0.2.29 < 0.3.0", "cssstyle@>= 0.2.36 < 0.3.0":
|
||||
"cssstyle@>= 0.2.29 < 0.3.0", "cssstyle@>= 0.2.37 < 0.3.0":
|
||||
version "0.2.37"
|
||||
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
|
||||
dependencies:
|
||||
@@ -3635,7 +3635,7 @@ iconv-lite@0.4.13:
|
||||
version "0.4.13"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
|
||||
|
||||
iconv-lite@0.4.15, iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
|
||||
iconv-lite@0.4.15, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
|
||||
version "0.4.15"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
|
||||
|
||||
@@ -4188,6 +4188,10 @@ jsbn@~0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd"
|
||||
|
||||
jsdom-global@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-2.1.1.tgz#47d46fe77f6167baf5d34431d3bb59fc41b0915a"
|
||||
|
||||
jsdom@^7.0.2:
|
||||
version "7.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e"
|
||||
@@ -4208,30 +4212,29 @@ jsdom@^7.0.2:
|
||||
whatwg-url-compat "~0.6.5"
|
||||
xml-name-validator ">= 2.0.1 < 3.0.0"
|
||||
|
||||
jsdom@^9.8.3:
|
||||
version "9.9.1"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.9.1.tgz#84f3972ad394ab963233af8725211bce4d01bfd5"
|
||||
jsdom@^9.12.0:
|
||||
version "9.12.0"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
|
||||
dependencies:
|
||||
abab "^1.0.0"
|
||||
acorn "^2.4.0"
|
||||
acorn-globals "^1.0.4"
|
||||
abab "^1.0.3"
|
||||
acorn "^4.0.4"
|
||||
acorn-globals "^3.1.0"
|
||||
array-equal "^1.0.0"
|
||||
content-type-parser "^1.0.1"
|
||||
cssom ">= 0.3.0 < 0.4.0"
|
||||
cssstyle ">= 0.2.36 < 0.3.0"
|
||||
cssom ">= 0.3.2 < 0.4.0"
|
||||
cssstyle ">= 0.2.37 < 0.3.0"
|
||||
escodegen "^1.6.1"
|
||||
html-encoding-sniffer "^1.0.1"
|
||||
iconv-lite "^0.4.13"
|
||||
nwmatcher ">= 1.3.9 < 2.0.0"
|
||||
parse5 "^1.5.1"
|
||||
request "^2.55.0"
|
||||
sax "^1.1.4"
|
||||
symbol-tree ">= 3.1.0 < 4.0.0"
|
||||
tough-cookie "^2.3.1"
|
||||
webidl-conversions "^3.0.1"
|
||||
request "^2.79.0"
|
||||
sax "^1.2.1"
|
||||
symbol-tree "^3.2.1"
|
||||
tough-cookie "^2.3.2"
|
||||
webidl-conversions "^4.0.0"
|
||||
whatwg-encoding "^1.0.1"
|
||||
whatwg-url "^4.1.0"
|
||||
xml-name-validator ">= 2.0.1 < 3.0.0"
|
||||
whatwg-url "^4.3.0"
|
||||
xml-name-validator "^2.0.1"
|
||||
|
||||
jsesc@^1.3.0:
|
||||
version "1.3.0"
|
||||
@@ -6919,7 +6922,7 @@ sax@0.5.x:
|
||||
version "0.5.8"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
|
||||
|
||||
sax@^1.1.4, sax@~1.2.1:
|
||||
sax@^1.1.4, sax@^1.2.1, sax@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
|
||||
|
||||
@@ -7401,7 +7404,7 @@ symbol-observable@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
|
||||
|
||||
"symbol-tree@>= 3.1.0 < 4.0.0":
|
||||
"symbol-tree@>= 3.1.0 < 4.0.0", symbol-tree@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb"
|
||||
|
||||
@@ -7565,7 +7568,7 @@ touch@1.0.0:
|
||||
dependencies:
|
||||
nopt "~1.0.10"
|
||||
|
||||
tough-cookie@^2.0.0, tough-cookie@^2.2.0, tough-cookie@^2.3.1, tough-cookie@~2.3.0:
|
||||
tough-cookie@^2.0.0, tough-cookie@^2.2.0, tough-cookie@^2.3.2, tough-cookie@~2.3.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
|
||||
dependencies:
|
||||
@@ -7801,10 +7804,14 @@ webidl-conversions@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506"
|
||||
|
||||
webidl-conversions@^3.0.0, webidl-conversions@^3.0.1:
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
|
||||
webidl-conversions@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.1.tgz#8015a17ab83e7e1b311638486ace81da6ce206a0"
|
||||
|
||||
webpack-sources@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.4.tgz#ccc2c817e08e5fa393239412690bb481821393cd"
|
||||
@@ -7853,9 +7860,9 @@ whatwg-url-compat@~0.6.5:
|
||||
dependencies:
|
||||
tr46 "~0.0.1"
|
||||
|
||||
whatwg-url@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.3.0.tgz#92aaee21f4f2a642074357d70ef8500a7cbb171a"
|
||||
whatwg-url@^4.3.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.6.0.tgz#ef98da442273be04cf9632e176f257d2395a1ae4"
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
@@ -7956,7 +7963,7 @@ xdg-basedir@^2.0.0:
|
||||
dependencies:
|
||||
os-homedir "^1.0.0"
|
||||
|
||||
"xml-name-validator@>= 2.0.1 < 3.0.0":
|
||||
"xml-name-validator@>= 2.0.1 < 3.0.0", xml-name-validator@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user