mirror of
https://github.com/wassname/talk.git
synced 2026-07-01 02:50:26 +08:00
Merge branch 'master' into status-history
This commit is contained in:
+3
-1
@@ -1,10 +1,12 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
npm-debug.log*
|
||||
dist
|
||||
!dist/coral-admin
|
||||
dist/coral-admin/bundle.js
|
||||
tests/e2e/reports
|
||||
.DS_Store
|
||||
*.iml
|
||||
dump.rdb
|
||||
.env
|
||||
gaba.cfg
|
||||
.idea/
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@ const util = require('../util');
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
const port = normalizePort(process.env.TALK_PORT || '3000');
|
||||
|
||||
const port = normalizePort(process.env.TALK_PORT || (process.env.NODE_ENV === 'test' ? '3011' : '3000'));
|
||||
|
||||
app.set('port', port);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Notification,
|
||||
notificationActions,
|
||||
authActions,
|
||||
configActions
|
||||
} from '../../coral-framework';
|
||||
|
||||
import CommentBox from '../../coral-plugin-commentbox/CommentBox';
|
||||
@@ -21,14 +22,17 @@ import LikeButton from '../../coral-plugin-likes/LikeButton';
|
||||
import PermalinkButton from '../../coral-plugin-permalinks/PermalinkButton';
|
||||
import SignInContainer from '../../coral-sign-in/containers/SignInContainer';
|
||||
import UserBox from '../../coral-sign-in/components/UserBox';
|
||||
|
||||
import {TabBar, Tab, TabContent, Spinner} from '../../coral-ui';
|
||||
import SettingsContainer from '../../coral-settings/containers/SettingsContainer';
|
||||
import RestrictedContent from '../../coral-framework/components/RestrictedContent';
|
||||
import SuspendedAccount from '../../coral-framework/components/SuspendedAccount';
|
||||
import CloseCommentsInfo from '../../coral-framework/components/CloseCommentsInfo';
|
||||
|
||||
const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions;
|
||||
const {addNotification, clearNotification} = notificationActions;
|
||||
const {logout, showSignInDialog} = authActions;
|
||||
const {updateOpenStatus} = configActions;
|
||||
|
||||
class CommentStream extends Component {
|
||||
|
||||
@@ -40,6 +44,7 @@ class CommentStream extends Component {
|
||||
};
|
||||
|
||||
this.changeTab = this.changeTab.bind(this);
|
||||
this.toggleStatus = this.toggleStatus.bind(this);
|
||||
}
|
||||
|
||||
changeTab (tab) {
|
||||
@@ -48,6 +53,10 @@ class CommentStream extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
toggleStatus () {
|
||||
this.props.updateStatus(this.props.config.status === 'open' ? 'closed' : 'open');
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
items: PropTypes.object.isRequired,
|
||||
addItem: PropTypes.func.isRequired,
|
||||
@@ -56,31 +65,22 @@ class CommentStream extends Component {
|
||||
|
||||
componentDidMount () {
|
||||
// Set up messaging between embedded Iframe an parent component
|
||||
// Using recommended Pym init code which violates .eslint standards
|
||||
const pym = new Pym.Child({polling: 100});
|
||||
this.pym = new Pym.Child({polling: 100});
|
||||
|
||||
if (/https?\:\/\/([^?]+)/.test(pym.parentUrl)) {
|
||||
this.props.getStream(pym.parentUrl);
|
||||
} else {
|
||||
this.props.getStream(window.location);
|
||||
}
|
||||
const path = /https?\:\/\/([^?#]+)/.exec(this.pym.parentUrl);
|
||||
|
||||
this.props.getStream(path[1] || window.location);
|
||||
this.path = path;
|
||||
|
||||
this.pym.sendMessage('childReady');
|
||||
}
|
||||
|
||||
render () {
|
||||
if (Object.keys(this.props.items).length === 0) {
|
||||
// Loading mock asset
|
||||
this.props.postItem({
|
||||
comments: [],
|
||||
url: 'http://coralproject.net'
|
||||
}, 'asset', 'assetTest');
|
||||
}
|
||||
|
||||
// TODO: Replace teststream id with id from params
|
||||
|
||||
const rootItemId = this.props.items.assets && Object.keys(this.props.items.assets)[0];
|
||||
const rootItem = this.props.items.assets && this.props.items.assets[rootItemId];
|
||||
const {actions, users, comments} = this.props.items;
|
||||
const {loggedIn, user, showSignInDialog} = this.props.auth;
|
||||
const {status} = this.props.config;
|
||||
const {activeTab} = this.state;
|
||||
|
||||
return <div className={showSignInDialog ? 'expandForSignin' : ''}>
|
||||
@@ -90,135 +90,145 @@ class CommentStream extends Component {
|
||||
<TabBar onChange={this.changeTab} activeTab={activeTab}>
|
||||
<Tab><Count id={rootItemId} items={this.props.items}/></Tab>
|
||||
<Tab>Settings</Tab>
|
||||
<Tab>Configure Stream</Tab>
|
||||
</TabBar>
|
||||
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
|
||||
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
|
||||
{/* Add to the restricted param a boolean if the user is suspended*/}
|
||||
<RestrictedContent restricted={false} restrictedComp={<SuspendedAccount />}>
|
||||
<TabContent show={activeTab === 0}>
|
||||
<div id="commentBox">
|
||||
<InfoBox
|
||||
content={this.props.config.infoBoxContent}
|
||||
enable={this.props.config.infoBoxEnable}
|
||||
/>
|
||||
<CommentBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
premod={this.props.config.moderation}
|
||||
reply={false}
|
||||
author={user}
|
||||
/>
|
||||
{
|
||||
status === 'open'
|
||||
? <div id="commentBox">
|
||||
<InfoBox
|
||||
content={this.props.config.infoBoxContent}
|
||||
enable={this.props.config.infoBoxEnable}
|
||||
/>
|
||||
<CommentBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
premod={this.props.config.moderation}
|
||||
reply={false}
|
||||
author={user}
|
||||
/>
|
||||
</div>
|
||||
: <p>Comments are closed for this thread.</p>
|
||||
}
|
||||
{!loggedIn && <SignInContainer />}
|
||||
</div>
|
||||
{
|
||||
rootItem.comments && rootItem.comments.map((commentId) => {
|
||||
const comment = comments[commentId];
|
||||
return <div className="comment" key={commentId} id={`c_${commentId}`}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[comment.author_id]}/>
|
||||
<PubDate created_at={comment.created_at}/>
|
||||
<Content body={comment.body}/>
|
||||
<div className="commentActionsLeft">
|
||||
<ReplyButton
|
||||
{
|
||||
rootItem.comments && rootItem.comments.map((commentId) => {
|
||||
const comment = comments[commentId];
|
||||
return <div className="comment" key={commentId} id={`c_${commentId}`}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[comment.author_id]}/>
|
||||
<PubDate created_at={comment.created_at}/>
|
||||
<Content body={comment.body}/>
|
||||
<div className="commentActionsLeft">
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
id={commentId}
|
||||
showReply={comment.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
like={actions[comment.like]}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="commentActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
flag={actions[comment.flag]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
commentId={commentId}
|
||||
articleURL={this.path}/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={commentId}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={comment.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
like={actions[comment.like]}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="commentActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={commentId}
|
||||
flag={actions[comment.flag]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
commentId={commentId}
|
||||
articleURL={this.path}/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={comment.showReply}/>
|
||||
{
|
||||
comment.children &&
|
||||
comment.children.map((replyId) => {
|
||||
let reply = this.props.items.comments[replyId];
|
||||
return <div className="reply" key={replyId} id={`c_${replyId}`}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[reply.author_id]}/>
|
||||
<PubDate created_at={reply.created_at}/>
|
||||
<Content body={reply.body}/>
|
||||
<div className="replyActionsLeft">
|
||||
<ReplyButton
|
||||
{
|
||||
comment.children &&
|
||||
comment.children.map((replyId) => {
|
||||
let reply = this.props.items.comments[replyId];
|
||||
return <div className="reply" key={replyId} id={`c_${replyId}`}>
|
||||
<hr aria-hidden={true}/>
|
||||
<AuthorName author={users[reply.author_id]}/>
|
||||
<PubDate created_at={reply.created_at}/>
|
||||
<Content body={reply.body}/>
|
||||
<div className="replyActionsLeft">
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
id={replyId}
|
||||
showReply={reply.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
like={this.props.items.actions[reply.like]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="replyActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
flag={this.props.items.actions[reply.flag]}
|
||||
postAction={this.props.postAction}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
commentId={reply.parent_id}
|
||||
articleURL={this.path}
|
||||
/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={replyId}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
child_id={replyId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={reply.showReply}/>
|
||||
<LikeButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
like={this.props.items.actions[reply.like]}
|
||||
postAction={this.props.postAction}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
</div>
|
||||
<div className="replyActionsRight">
|
||||
<FlagButton
|
||||
addNotification={this.props.addNotification}
|
||||
id={replyId}
|
||||
flag={this.props.items.actions[reply.flag]}
|
||||
postAction={this.props.postAction}
|
||||
showSignInDialog={this.props.showSignInDialog}
|
||||
deleteAction={this.props.deleteAction}
|
||||
addItem={this.props.addItem}
|
||||
updateItem={this.props.updateItem}
|
||||
currentUser={this.props.auth.user}/>
|
||||
<PermalinkButton
|
||||
commentId={reply.parent_id}
|
||||
articleURL={this.path}
|
||||
/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
author={user}
|
||||
parent_id={commentId}
|
||||
child_id={replyId}
|
||||
premod={this.props.config.moderation}
|
||||
showReply={reply.showReply}/>
|
||||
</div>;
|
||||
})
|
||||
</div>;
|
||||
})
|
||||
}
|
||||
</div>;
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
<Notification
|
||||
notifLength={4500}
|
||||
clearNotification={this.props.clearNotification}
|
||||
notification={this.props.notification}
|
||||
/>
|
||||
</TabContent>
|
||||
<TabContent show={activeTab === 1}>
|
||||
<SettingsContainer
|
||||
@@ -226,7 +236,12 @@ class CommentStream extends Component {
|
||||
userData={this.props.userData}
|
||||
showSignInDialog={this.props.handleSignInDialog}
|
||||
/>
|
||||
{!loggedIn && <SignInContainer noButton/>}
|
||||
</TabContent>
|
||||
<TabContent show={activeTab === 2}>
|
||||
<h3>{status === 'open' ? 'Close' : 'Open'} Comment Stream</h3>
|
||||
<RestrictedContent restricted={!loggedIn}>
|
||||
<CloseCommentsInfo onClick={this.toggleStatus} status={status} />
|
||||
</RestrictedContent>
|
||||
</TabContent>
|
||||
</RestrictedContent>
|
||||
<Notification
|
||||
@@ -236,7 +251,7 @@ class CommentStream extends Component {
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<Spinner/>
|
||||
<Spinner/>
|
||||
}
|
||||
</div>;
|
||||
}
|
||||
@@ -263,6 +278,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
appendItemArray: (item, property, value, addToFront, itemType) => dispatch(appendItemArray(item, property, value, addToFront, itemType)),
|
||||
handleSignInDialog: () => dispatch(authActions.showSignInDialog()),
|
||||
logout: () => dispatch(logout()),
|
||||
updateStatus: status => dispatch(updateOpenStatus(status))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CommentStream);
|
||||
|
||||
@@ -197,3 +197,42 @@ hr {
|
||||
float: right;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
/* Close comments */
|
||||
|
||||
.close-comments-intro-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.close-comments-intro-wrapper button {
|
||||
width: 300px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.close-comments-intro-wrapper button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.close-comments-message {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.close-comments-confirm-wrapper {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.close-comments-alert {
|
||||
background-color: #d65344;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.close-comments-alert i.material-icons {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import coralApi from '../helpers/response';
|
||||
/* Config Actions */
|
||||
|
||||
/**
|
||||
* Action name constants
|
||||
*/
|
||||
|
||||
export const OPEN_COMMENTS = 'OPEN_COMMENTS';
|
||||
export const CLOSE_COMMENTS = 'CLOSE_COMMENTS';
|
||||
export const ADD_ITEM = 'ADD_ITEM';
|
||||
|
||||
export const updateOpenStatus = status => (dispatch, getState) => {
|
||||
const assetId = getState().items.get('assets')
|
||||
.keySeq()
|
||||
.toArray()[0];
|
||||
return coralApi(`/asset/${assetId}/status?status=${status}`, {method: 'PUT'})
|
||||
.then(() => dispatch({type: status === 'open' ? OPEN_COMMENTS : CLOSE_COMMENTS}));
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import {Button} from 'coral-ui';
|
||||
|
||||
export default ({status, onClick}) => (
|
||||
status === 'open' ? (
|
||||
<div className="close-comments-intro-wrapper">
|
||||
<p>
|
||||
This comment stream is currently open. By closing this comment stream,
|
||||
no new comments may be submitted and all previous comments will still
|
||||
be displayed.
|
||||
</p>
|
||||
<Button onClick={onClick}>Close Stream</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="close-comments-intro-wrapper">
|
||||
<p>
|
||||
This comment stream is currently closed. By opening this comment stream,
|
||||
new comments may be submitted and displayed
|
||||
</p>
|
||||
<Button onClick={onClick}>Open Stream</Button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
@@ -4,6 +4,7 @@ import * as itemActions from './actions/items';
|
||||
import I18n from './modules/i18n/i18n';
|
||||
import * as notificationActions from './actions/notification';
|
||||
import * as authActions from './actions/auth';
|
||||
import * as configActions from './actions/config';
|
||||
|
||||
export {
|
||||
Notification,
|
||||
@@ -12,4 +13,5 @@ export {
|
||||
I18n,
|
||||
notificationActions,
|
||||
authActions,
|
||||
configActions
|
||||
};
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
/* @flow */
|
||||
|
||||
import {Map} from 'immutable';
|
||||
import * as actions from '../actions/items';
|
||||
import * as actions from '../actions/config';
|
||||
|
||||
const initialState = Map({
|
||||
features: Map({})
|
||||
features: Map({}),
|
||||
status: 'open'
|
||||
});
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch(action.type) {
|
||||
|
||||
// Override config if worked
|
||||
case actions.UPDATE_SETTINGS:
|
||||
return action.config;
|
||||
|
||||
case actions.OPEN_COMMENTS:
|
||||
return state.set('status', 'open');
|
||||
|
||||
case actions.CLOSE_COMMENTS:
|
||||
return state.set('status', 'closed');
|
||||
|
||||
case actions.ADD_ITEM:
|
||||
return action.item_type === 'assets' ?
|
||||
state.set('status', action.item.status)
|
||||
: state;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ class CommentBox extends Component {
|
||||
{ author && (
|
||||
<Button
|
||||
cStyle='darkGrey'
|
||||
className={`${name}-button`}
|
||||
onClick={this.postComment}>
|
||||
{lang.t('post')}
|
||||
</Button>
|
||||
|
||||
@@ -44,7 +44,7 @@ const SignInContent = ({handleChange, formData, ...props}) => (
|
||||
<div className={styles.action}>
|
||||
{
|
||||
!props.auth.isLoading ?
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton} full>
|
||||
<Button id='coralLogInButton' type="submit" cStyle="black" className={styles.signInButton} full>
|
||||
{lang.t('signIn.signIn')}
|
||||
</Button>
|
||||
:
|
||||
@@ -56,7 +56,7 @@ const SignInContent = ({handleChange, formData, ...props}) => (
|
||||
<span><a onClick={() => props.changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')}</a></span>
|
||||
<span>
|
||||
{lang.t('signIn.needAnAccount')}
|
||||
<a onClick={() => props.changeView('SIGNUP')}>
|
||||
<a onClick={() => props.changeView('SIGNUP')} id='coralRegister'>
|
||||
{lang.t('signIn.register')}
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -69,7 +69,7 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
|
||||
/>
|
||||
<div className={styles.action}>
|
||||
{ !props.auth.isLoading && !props.auth.successSignUp && (
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton} full>
|
||||
<Button type="submit" cStyle="black" id='coralSignUpButton' className={styles.signInButton} full>
|
||||
{lang.t('signIn.signUp')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -132,7 +132,9 @@ class SignInContainer extends Component {
|
||||
const {auth, showSignInDialog, noButton} = this.props;
|
||||
return (
|
||||
<div>
|
||||
{!noButton && <Button onClick={showSignInDialog} full> Sign in to comment</Button>}
|
||||
{!noButton && <Button id='coralSignInButton' onClick={showSignInDialog} full>
|
||||
Sign in to comment
|
||||
</Button>}
|
||||
<SignDialog
|
||||
open={auth.showSignInDialog}
|
||||
view={auth.view}
|
||||
|
||||
+5
-1
@@ -36,7 +36,11 @@ const AssetSchema = new Schema({
|
||||
subsection: String,
|
||||
author: String,
|
||||
publication_date: Date,
|
||||
modified_date: Date
|
||||
modified_date: Date,
|
||||
status: {
|
||||
type: String,
|
||||
default: 'open'
|
||||
}
|
||||
}, {
|
||||
versionKey: false,
|
||||
timestamps: {
|
||||
|
||||
+7
-1
@@ -1,7 +1,13 @@
|
||||
const mongoose = require('mongoose');
|
||||
const debug = require('debug')('talk:db');
|
||||
const enabled = require('debug').enabled;
|
||||
const url = process.env.TALK_MONGO_URL || (process.env.NODE_ENV === 'test' ? 'mongodb://localhost/test' : 'mongodb://localhost/talk');
|
||||
|
||||
//Append '-test' to the db if node_env === 'test'
|
||||
let url = process.env.TALK_MONGO_URL || 'mongodb://localhost/coral-talk';
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
url += '-test';
|
||||
}
|
||||
|
||||
// Use native promises
|
||||
mongoose.Promise = global.Promise;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
require('babel-core/register');
|
||||
|
||||
module.exports = {
|
||||
'src_folders': './tests/e2e/tests',
|
||||
'output_folder': './tests/e2e/reports',
|
||||
'page_objects_path': './tests/e2e/pages',
|
||||
'globals_path': './tests/e2e/globals',
|
||||
'custom_commands_path' : '',
|
||||
'custom_assertions_path' : '',
|
||||
'selenium': {
|
||||
'start_process': true,
|
||||
'server_path': 'node_modules/selenium-standalone/.selenium/selenium-server/2.53.1-server.jar',
|
||||
'log_path': './tests/e2e/reports',
|
||||
'host': '127.0.0.1',
|
||||
'port': 6666,
|
||||
'cli_args': {
|
||||
'webdriver.chrome.driver': 'node_modules/selenium-standalone/.selenium/chromedriver/2.25-x64-chromedriver'
|
||||
}
|
||||
},
|
||||
'test_settings': {
|
||||
'default': {
|
||||
'selenium_port': 6666,
|
||||
'selenium_host': 'localhost',
|
||||
'silent': true,
|
||||
'desiredCapabilities': {
|
||||
'browserName': 'chrome',
|
||||
'javascriptEnabled': true,
|
||||
'acceptSslCerts': true,
|
||||
'webStorageEnabled' : true,
|
||||
'databaseEnabled' : true,
|
||||
'applicationCacheEnabled' : false,
|
||||
'nativeEvents' : true
|
||||
},
|
||||
'screenshots' : {
|
||||
'enabled' : true,
|
||||
'on_failure' : true,
|
||||
'on_error' : true,
|
||||
'path' : './tests/e2e/reports'
|
||||
},
|
||||
'exclude': [
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
+9
-15
@@ -11,18 +11,15 @@
|
||||
"lint-fix": "eslint . --fix",
|
||||
"test": "NODE_ENV=test mocha --compilers js:babel-core/register --recursive tests",
|
||||
"test-watch": "NODE_ENV=test mocha --compilers js:babel-core/register --recursive -w tests",
|
||||
"pree2e": "NODE_ENV=test ./pree2e.sh",
|
||||
"e2e": "NODE_ENV=test node_modules/.bin/nightwatch",
|
||||
"embed-start": "NODE_ENV=development npm run build && ./bin/cli serve --jobs"
|
||||
},
|
||||
"config": {
|
||||
"pre-git": {
|
||||
"commit-msg": [],
|
||||
"pre-commit": [
|
||||
"npm run lint",
|
||||
"npm test"
|
||||
],
|
||||
"pre-push": [
|
||||
"npm test"
|
||||
],
|
||||
"pre-commit": ["npm run lint", "npm test"],
|
||||
"pre-push": ["npm test"],
|
||||
"post-commit": [],
|
||||
"post-merge": []
|
||||
}
|
||||
@@ -31,12 +28,7 @@
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/coralproject/talk.git"
|
||||
},
|
||||
"keywords": [
|
||||
"talk",
|
||||
"coral",
|
||||
"coralproject",
|
||||
"ask"
|
||||
],
|
||||
"keywords": ["talk", "coral", "coralproject", "ask"],
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
@@ -111,6 +103,8 @@
|
||||
"material-design-lite": "^1.2.1",
|
||||
"mocha": "^3.1.2",
|
||||
"mocha-junit-reporter": "^1.12.1",
|
||||
"nightwatch": "^0.9.9",
|
||||
"node-fetch": "^1.6.3",
|
||||
"postcss-loader": "^1.1.0",
|
||||
"postcss-modules": "^0.5.2",
|
||||
"postcss-smart-import": "^0.5.1",
|
||||
@@ -128,11 +122,11 @@
|
||||
"redux-mock-store": "^1.2.1",
|
||||
"redux-thunk": "^2.1.0",
|
||||
"regenerator": "^0.8.46",
|
||||
"selenium-standalone": "^5.8.0",
|
||||
"style-loader": "^0.13.1",
|
||||
"supertest": "^2.0.1",
|
||||
"timeago.js": "^2.0.3",
|
||||
"webpack": "^1.13.3",
|
||||
"whatwg-fetch": "^1.0.0"
|
||||
"webpack": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/selenium-standalone/bin/selenium-standalone install
|
||||
npm start &
|
||||
@@ -96,4 +96,12 @@ router.put('/:asset_id/settings', (req, res, next) => {
|
||||
|
||||
});
|
||||
|
||||
router.put('/:asset_id/status', (req, res, next) => {
|
||||
// Update the asset status
|
||||
Asset
|
||||
.update({id: req.params.asset_id}, {status: req.query.status})
|
||||
.then(() => res.status(204).end())
|
||||
.catch((err) => next(err));
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
waitForConditionTimeout: 20000,
|
||||
baseUrl: 'localhost:3011/'
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
const Comments = require('../../models/comment');
|
||||
const Users = require('../../models/user');
|
||||
const Actions = require('../../models/action');
|
||||
const Assets = require('../../models/asset');
|
||||
const Settings = require('../../models/setting');
|
||||
const globals = require('./globals');
|
||||
|
||||
/* Create an array of comments */
|
||||
module.exports.comments = (comments) => Assets.findOrCreateByUrl(globals.baseUrl)
|
||||
.then((asset) => {
|
||||
comments = comments.map((comment) => {
|
||||
comment.asset_id = asset.id;
|
||||
return comment;
|
||||
});
|
||||
return Comments.create(comments);
|
||||
});
|
||||
|
||||
/* Create an array of users */
|
||||
module.exports.users = (users) => Users.createLocalUsers(users);
|
||||
|
||||
/* Create an array of actions */
|
||||
module.exports.actions = (actions) => Actions.create(actions);
|
||||
|
||||
/* Update a setting */
|
||||
module.exports.settings = (setting) => Settings.init().then(() => Settings.updateSettings(setting));
|
||||
@@ -0,0 +1,45 @@
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const embedCommands = {
|
||||
ready() {
|
||||
return this.resizeWindow(1200, 800)
|
||||
.url(client.globals.baseUrl)
|
||||
.waitForElementVisible('body', 2000)
|
||||
.frame('coralStreamIframe');
|
||||
},
|
||||
setConfig(config, baseUrl) {
|
||||
return fetch(`${baseUrl}/api/v1/settings`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
},
|
||||
enterComment() {
|
||||
const comment = 'This is a test comment';
|
||||
return this
|
||||
.waitForElementVisible('@commentBox')
|
||||
.setValue('@commentBox', comment)
|
||||
.click('@postButton')
|
||||
.waitForElementVisible('.comment', 1000);
|
||||
},
|
||||
validateComment(comment) {
|
||||
return this
|
||||
.assert.equal(comment, '.comment');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
commands: [embedCommands],
|
||||
elements: {
|
||||
|
||||
commentBox: {
|
||||
selector: '#commentBox'
|
||||
},
|
||||
postButton: {
|
||||
selector: '#commentBox .coral-plugin-commentbox-button'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
'@tags': ['app'],
|
||||
'Base url and Hostname': browser => {
|
||||
const {baseUrl} = browser.globals;
|
||||
browser
|
||||
.url(baseUrl)
|
||||
.assert.title('Coral Talk')
|
||||
.waitForElementPresent('body', 1000);
|
||||
},
|
||||
after: client => {
|
||||
client.end();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
const utils = require('../../utils/e2e-mongoose');
|
||||
const mocks = require('../mocks');
|
||||
|
||||
const mockComment = 'This is a test comment.';
|
||||
const mockReply = 'This is a test reply';
|
||||
const mockUser = {
|
||||
email: `${new Date().getTime()}@test.com`,
|
||||
name: 'Test User',
|
||||
pw: 'testtesttest'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
'@tags': ['embed-stream', 'comment', 'premodoff', 'premodon'],
|
||||
before: () => {
|
||||
utils.before();
|
||||
},
|
||||
'User registers and posts a comment with premod off': client => {
|
||||
client.perform((client, done) => {
|
||||
mocks.settings({moderation: 'post'})
|
||||
.then(() => {
|
||||
//Load Page
|
||||
client.resizeWindow(1200, 800)
|
||||
.url(client.globals.baseUrl)
|
||||
.frame('coralStreamIframe')
|
||||
|
||||
//Register and Log In
|
||||
.waitForElementVisible('#commentBox', 1000)
|
||||
.waitForElementVisible('#coralSignInButton', 2000)
|
||||
.click('#coralSignInButton')
|
||||
.waitForElementVisible('#coralRegister', 1000)
|
||||
.click('#coralRegister')
|
||||
.waitForElementVisible('#email', 1000)
|
||||
.setValue('#email', mockUser.email)
|
||||
.setValue('#displayName', mockUser.name)
|
||||
.setValue('#password', mockUser.pw)
|
||||
.setValue('#confirmPassword', mockUser.pw)
|
||||
.click('#coralSignUpButton')
|
||||
.waitForElementVisible('#coralLogInButton', 10000)
|
||||
.click('#coralLogInButton')
|
||||
.waitForElementVisible('.coral-plugin-commentbox-button', 4000)
|
||||
|
||||
// Post a comment
|
||||
.setValue('.coral-plugin-commentbox-textarea', mockComment)
|
||||
.click('.coral-plugin-commentbox-button')
|
||||
.waitForElementVisible('.comment', 1000)
|
||||
|
||||
//Verify that it appears
|
||||
.assert.containsText('.comment', mockComment);
|
||||
done();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
'User posts a comment with premod on': client => {
|
||||
client.perform((client, done) => {
|
||||
mocks.settings({moderation: 'pre'})
|
||||
.then(() => {
|
||||
//Load Page
|
||||
client.url(client.globals.baseUrl)
|
||||
.frame('coralStreamIframe');
|
||||
|
||||
// Post a comment
|
||||
client.waitForElementVisible('.coral-plugin-commentbox-button', 2000)
|
||||
.setValue('.coral-plugin-commentbox-textarea', mockComment)
|
||||
.click('.coral-plugin-commentbox-button')
|
||||
.waitForElementVisible('#coral-notif', 1000)
|
||||
|
||||
//Verify that it appears
|
||||
.assert.containsText('#coral-notif', 'moderation team');
|
||||
done();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
'User replies to a comment with premod off': client => {
|
||||
client.perform((client, done) => {
|
||||
mocks.settings({moderation: 'post'})
|
||||
.then(() => {
|
||||
//Load Page
|
||||
client.resizeWindow(1200, 800)
|
||||
.url(client.globals.baseUrl)
|
||||
.frame('coralStreamIframe');
|
||||
|
||||
// Post a comment
|
||||
client.waitForElementVisible('.coral-plugin-commentbox-button', 2000)
|
||||
.setValue('.coral-plugin-commentbox-textarea', mockComment)
|
||||
.click('.coral-plugin-commentbox-button')
|
||||
|
||||
// Post a reply
|
||||
.waitForElementVisible('.coral-plugin-replies-reply-button', 5000)
|
||||
.click('.coral-plugin-replies-reply-button')
|
||||
.waitForElementVisible('#replyText')
|
||||
.setValue('#replyText', mockReply)
|
||||
.click('.coral-plugin-replies-textarea button')
|
||||
.waitForElementVisible('.reply', 2000)
|
||||
|
||||
//Verify that it appears
|
||||
.assert.containsText('.reply', mockReply);
|
||||
done();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
'User replies to a comment with premod on': client => {
|
||||
client.perform((client, done) => {
|
||||
mocks.settings({moderation: 'pre'})
|
||||
|
||||
// Add a mock user
|
||||
.then(() => {
|
||||
return mocks.users([{
|
||||
displayName: 'Baby Blue',
|
||||
email: 'whale@tale.sea',
|
||||
password: 'krill'
|
||||
}]);
|
||||
})
|
||||
|
||||
// Add a mock preapproved comment by that user
|
||||
.then((user) => {
|
||||
return mocks.comments([{
|
||||
body: 'Whales are not fish.',
|
||||
status: 'accepted',
|
||||
author_id: user.id
|
||||
}]);
|
||||
})
|
||||
.then(() => {
|
||||
//Load Page
|
||||
client.resizeWindow(1200, 800)
|
||||
.url(client.globals.baseUrl)
|
||||
.frame('coralStreamIframe');
|
||||
|
||||
// Post a reply
|
||||
client.waitForElementVisible('.coral-plugin-replies-reply-button', 5000)
|
||||
.click('.coral-plugin-replies-reply-button')
|
||||
.waitForElementVisible('#replyText')
|
||||
.setValue('#replyText', mockReply)
|
||||
.click('.coral-plugin-replies-textarea button')
|
||||
.waitForElementVisible('#coral-notif', 1000)
|
||||
|
||||
//Verify that it appears
|
||||
.assert.containsText('#coral-notif', 'moderation team');
|
||||
done();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
'Total comment count premod on': client => {
|
||||
client.perform((client, done) => {
|
||||
client.url(client.globals.baseUrl)
|
||||
.frame('coralStreamIframe');
|
||||
|
||||
// Verify that comment count is correct
|
||||
client.waitForElementVisible('.coral-plugin-comment-count-text', 2000)
|
||||
.assert.containsText('.coral-plugin-comment-count-text', '1 Comment');
|
||||
done();
|
||||
});
|
||||
},
|
||||
after: client => {
|
||||
utils.after();
|
||||
client.end();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
const mongoose = require('../../mongoose');
|
||||
|
||||
// Ensure the NODE_ENV is set to 'test',
|
||||
// this is helpful when you would like to change behavior when testing.
|
||||
function clearDB() {
|
||||
// console.log('Clearing DB', mongoose.connection);
|
||||
for (let i in mongoose.connection.collections) {
|
||||
// console.log('Clearing', i);
|
||||
mongoose.connection.collections[i].remove(function() {});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
before: () => {
|
||||
clearDB();
|
||||
},
|
||||
beforeEach: () => {
|
||||
if (mongoose.connection.readyState === 0) {
|
||||
mongoose.on('open', function() {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return clearDB();
|
||||
});
|
||||
} else {
|
||||
return clearDB();
|
||||
}
|
||||
},
|
||||
after: () => {
|
||||
clearDB();
|
||||
mongoose.disconnect();
|
||||
}
|
||||
};
|
||||
+2
-1
@@ -15,6 +15,7 @@
|
||||
width:500px;
|
||||
}
|
||||
</style>
|
||||
<title><%= title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
@@ -35,7 +36,7 @@
|
||||
<script type='text/javascript' src='<%= basePath %>/pym.v1.min.js'></script>
|
||||
<script>
|
||||
var ready = false;
|
||||
var pymParent = new pym.Parent('coralStreamEmbed', '/embed/stream', {title: 'Talk Comments'});
|
||||
var pymParent = new pym.Parent('coralStreamEmbed', '/embed/stream', {title: 'Talk Comments', id:'coralStreamIframe'});
|
||||
pymParent.onMessage('height', function(height) {document.querySelector('#coralStreamEmbed iframe').height = height + 'px'})
|
||||
pymParent.onMessage('childReady', function () {
|
||||
var interval = setInterval(function () {
|
||||
|
||||
Reference in New Issue
Block a user