Merge branch 'master' into community-flags

This commit is contained in:
gaba
2017-03-24 11:19:30 -07:00
26 changed files with 258 additions and 168 deletions
@@ -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 {
+2 -1
View File
@@ -1,9 +1,10 @@
.Reply {
position: relative;
margin-bottom: 15px;
}
.Comment {
margin-bottom: 15px;
}
.pendingComment {
+2 -2
View File
@@ -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
+9 -8
View File
@@ -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;
}
}
+8 -3
View File
@@ -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;
}
+10 -1
View File
@@ -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 = {
+1 -1
View File
@@ -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);
+4 -2
View File
@@ -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}`);
+4 -2
View File
@@ -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",
+59 -1
View File
@@ -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;
}
+34 -5
View File
@@ -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>
);
};
+1 -1
View File
@@ -19,7 +19,7 @@
}
.base li:hover {
background: #f3f3f3;
background: #d5d5d5;
cursor: pointer;
}
+1 -1
View File
@@ -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) => {
+6 -6
View File
@@ -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
View File
@@ -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;
});
});
+1 -1
View File
@@ -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
View File
@@ -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>
+36 -29
View File
@@ -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"