diff --git a/PLUGINS.md b/PLUGINS.md
index 20ba789be..a77b0b8b2 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -149,7 +149,7 @@ This will merge with the existing resolvers in core and from previous plugins.
{
RootMutation: {
createPerson: {
- post: async (obj, args, {plugins: {Slack}}, person) {
+ post: async (obj, args, {plugins: {Slack}}, info, person) {
if (!person) {
return person;
}
@@ -248,7 +248,7 @@ module.exports = {
hooks: {
RootMutation: {
createPerson: {
- post: async (obj, args, {plugins: {Slack}}, person) => {
+ post: async (obj, args, {plugins: {Slack}}, info, person) => {
if (!person) {
return person;
}
diff --git a/README.md b/README.md
index 64fe2739c..7e303f70e 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,8 @@ Facebook Login enabled app.
- `TALK_SMTP_HOST` (*required for email*) - SMTP host url with format `smtp.domain.com`.
- `TALK_SMTP_PORT` (*required for email*) - SMTP port.
- `TALK_INSTALL_LOCK` (_optional for dynamic setup_) - Defaults to `FALSE`. When `TRUE`, disables the dynamic setup endpoint.
+- `TALK_RECAPTCHA_SECRET` (*required for reCAPTCHA support*) - server secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout.
+- `TALK_RECAPTCHA_PUBLIC` (*required for reCAPTCHA support*) - client secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout.
Refer to the wiki page on [Configuration Loading](https://github.com/coralproject/talk/wiki/Configuration-Loading) for
alternative methods of loading configuration during development.
diff --git a/client/coral-admin/src/containers/Stories/Stories.js b/client/coral-admin/src/containers/Stories/Stories.js
index 4d2ad086a..1264663f0 100644
--- a/client/coral-admin/src/containers/Stories/Stories.js
+++ b/client/coral-admin/src/containers/Stories/Stories.js
@@ -2,13 +2,14 @@ import React, {Component} from 'react';
import styles from './Stories.css';
import {connect} from 'react-redux';
import I18n from 'coral-framework/modules/i18n/i18n';
-import {fetchAssets, updateAssetState} from '../../actions/assets';
-import translations from '../../translations.json';
+import {fetchAssets, updateAssetState} from 'coral-admin/src/actions/assets';
+import translations from 'coral-admin/src/translations.json';
import {Link} from 'react-router';
import {Pager, Icon} from 'coral-ui';
import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl';
import EmptyCard from 'coral-admin/src/components/EmptyCard';
+import sortBy from 'lodash/sortBy';
const limit = 25;
@@ -104,7 +105,11 @@ class Stories extends Component {
const {search, sort, filter} = this.state;
const {assets} = this.props;
- const assetsIds = assets.ids.map((id) => assets.byId[id]);
+ const assetsIds = sortBy(assets.ids.map((id) => assets.byId[id]), 'publication_date');
+
+ if (this.state.sort === 'desc') {
+ assetsIds.reverse();
+ }
return (
diff --git a/client/coral-admin/src/reducers/assets.js b/client/coral-admin/src/reducers/assets.js
index c9a82f1c5..03f0be9bb 100644
--- a/client/coral-admin/src/reducers/assets.js
+++ b/client/coral-admin/src/reducers/assets.js
@@ -23,8 +23,13 @@ export default function assets (state = initialState, action) {
}
const replaceAssets = (action, state) => {
- const assets = fromJS(action.assets.reduce((prev, curr) => { prev[curr.id] = curr; return prev; }, {}));
- return state.set('byId', assets)
- .set('count', action.count)
- .set('ids', List(assets.keys()));
+ const assets = fromJS(action.assets.reduce((prev, curr) => {
+ prev[curr.id] = curr;
+ return prev;
+ }, {}));
+
+ return state
+ .set('byId', assets)
+ .set('count', action.count)
+ .set('ids', List(assets.keys()));
};
diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js
index 06352ec2f..caffb6894 100644
--- a/client/coral-embed-stream/src/Comment.js
+++ b/client/coral-embed-stream/src/Comment.js
@@ -43,6 +43,7 @@ class Comment extends React.Component {
// id of currently opened ReplyBox. tracked in Stream.js
activeReplyBox: PropTypes.string.isRequired,
+ disableReply: PropTypes.bool,
setActiveReplyBox: PropTypes.func.isRequired,
showSignInDialog: PropTypes.func.isRequired,
postFlag: PropTypes.func.isRequired,
@@ -109,6 +110,7 @@ class Comment extends React.Component {
deleteAction,
addCommentTag,
removeCommentTag,
+ disableReply,
} = this.props;
const like = getActionSummary('LikeActionSummary', comment);
@@ -155,6 +157,7 @@ class Comment extends React.Component {
?
: null }
+
@@ -166,13 +169,16 @@ class Comment extends React.Component {
showSignInDialog={showSignInDialog}
currentUser={currentUser} />
-
- setActiveReplyBox(comment.id)}
- parentCommentId={parentId || comment.id}
- currentUserId={currentUser && currentUser.id}
- banned={false} />
-
+ {
+ !disableReply &&
+
+ setActiveReplyBox(comment.id)}
+ parentCommentId={parentId || comment.id}
+ currentUserId={currentUser && currentUser.id}
+ banned={false} />
+
+ }
{
return ;
})
}
diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
index ce6dc638a..4722a44b1 100644
--- a/client/coral-embed-stream/src/Embed.js
+++ b/client/coral-embed-stream/src/Embed.js
@@ -202,6 +202,7 @@ class Embed extends Component {
/>
asset.comments.length}
+ moreComments={countCache[asset.id] > asset.comments.length}
loadMore={this.props.loadMore}/>
diff --git a/client/coral-embed-stream/src/LoadMore.js b/client/coral-embed-stream/src/LoadMore.js
index bf89793d0..942a53b27 100644
--- a/client/coral-embed-stream/src/LoadMore.js
+++ b/client/coral-embed-stream/src/LoadMore.js
@@ -7,18 +7,17 @@ const lang = new I18n(translations);
const loadMoreComments = (assetId, comments, loadMore, parentId) => {
- if (!comments.length) {
- return;
+ let cursor = null;
+ if (comments.length) {
+ cursor = parentId
+ ? comments[0].created_at
+ : comments[comments.length - 1].created_at;
}
- const cursor = parentId
- ? comments[0].created_at
- : comments[comments.length - 1].created_at;
-
loadMore({
limit: ADDTL_COMMENTS_ON_LOAD_MORE,
cursor,
- assetId,
+ asset_id: assetId,
parent_id: parentId,
sort: parentId ? 'CHRONOLOGICAL' : 'REVERSE_CHRONOLOGICAL'
});
diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js
index 7743f3f1c..defca1a61 100644
--- a/client/coral-embed-stream/src/Stream.js
+++ b/client/coral-embed-stream/src/Stream.js
@@ -8,6 +8,7 @@ class Stream extends React.Component {
addNotification: PropTypes.func.isRequired,
postItem: PropTypes.func.isRequired,
asset: PropTypes.object.isRequired,
+ open: PropTypes.bool.isRequired,
comments: PropTypes.array.isRequired,
currentUser: PropTypes.shape({
username: PropTypes.string,
@@ -15,10 +16,10 @@ class Stream extends React.Component {
}),
// dispatch action to add a tag to a comment
- addCommentTag: React.PropTypes.func,
+ addCommentTag: PropTypes.func,
// dispatch action to remove a tag from a comment
- removeCommentTag: React.PropTypes.func,
+ removeCommentTag: PropTypes.func,
}
constructor(props) {
@@ -55,6 +56,7 @@ class Stream extends React.Component {
addNotification,
postFlag,
postLike,
+ open,
postDontAgree,
loadMore,
deleteAction,
@@ -68,6 +70,7 @@ class Stream extends React.Component {
{
comments.map(comment =>
({limit, cursor, parent_id = null, asset_id, s
...oldData,
asset: {
...oldData.asset,
- comments: oldData.asset.comments.map((comment) =>
- comment.id === parent_id
- ? {...comment, replies: [...comment.replies, ...new_top_level_comments]}
- : comment)
+ comments: oldData.asset.comments.map(comment => {
+
+ // since the dipslayed replies and the returned replies can overlap,
+ // pull out the unique ones.
+ const uniqueReplies = uniqBy([...new_top_level_comments, ...comment.replies], 'id');
+
+ // since we just gave the returned replies precedence, they're now out of order.
+ // resort according to date.
+ return comment.id === parent_id
+ ? {...comment, replies: sortBy(uniqueReplies, 'created_at')}
+ : comment;
+ })
}
};
} else {
diff --git a/services/passport.js b/services/passport.js
index 4ec80bb60..f1994392f 100644
--- a/services/passport.js
+++ b/services/passport.js
@@ -116,14 +116,15 @@ const CheckIfNeedsRecaptcha = (user, email) => {
* This stores the Recaptcha secret.
*/
const RECAPTCHA_SECRET = process.env.TALK_RECAPTCHA_SECRET;
+const RECAPTCHA_PUBLIC = process.env.TALK_RECAPTCHA_PUBLIC;
/**
* This is true when the recaptcha secret is provided and the Recaptcha feature
* is to be enabled.
*/
-const RECAPTCHA_ENABLED = RECAPTCHA_SECRET && RECAPTCHA_SECRET.length > 0;
+const RECAPTCHA_ENABLED = RECAPTCHA_SECRET && RECAPTCHA_SECRET.length > 0 && RECAPTCHA_PUBLIC && RECAPTCHA_PUBLIC.length > 0;
if (!RECAPTCHA_ENABLED) {
- console.log('Recaptcha is not enabled for login/signup abuse prevention, set TALK_RECAPTCHA_SECRET to enable Recaptcha.');
+ console.log('Recaptcha is not enabled for login/signup abuse prevention, set TALK_RECAPTCHA_SECRET and TALK_RECAPTCHA_PUBLIC to enable Recaptcha.');
}
/**