diff --git a/.babelrc b/.babelrc
index 92a64c2a4..ed21974ac 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,6 +1,6 @@
{
"presets": [
- ["es2015", {modules: false}]
+ ["es2015", {"modules": false}]
],
"plugins": [
"transform-class-properties",
diff --git a/.gitignore b/.gitignore
index 6f3ac70b8..6f3104629 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ plugins/*
!plugins/talk-plugin-author-menu
!plugins/talk-plugin-comment-content
!plugins/talk-plugin-deep-reply-count
+!plugins/talk-plugin-downvote
!plugins/talk-plugin-facebook-auth
!plugins/talk-plugin-featured-comments
!plugins/talk-plugin-flag-details
@@ -52,14 +53,17 @@ plugins/*
!plugins/talk-plugin-remember-sort
!plugins/talk-plugin-respect
!plugins/talk-plugin-slack-notifications
+!plugins/talk-plugin-sort-most-downvoted
!plugins/talk-plugin-sort-most-liked
!plugins/talk-plugin-sort-most-loved
!plugins/talk-plugin-sort-most-replied
!plugins/talk-plugin-sort-most-respected
+!plugins/talk-plugin-sort-most-upvoted
!plugins/talk-plugin-sort-newest
!plugins/talk-plugin-sort-oldest
!plugins/talk-plugin-subscriber
!plugins/talk-plugin-toxic-comments
+!plugins/talk-plugin-upvote
!plugins/talk-plugin-viewing-options
!plugins/talk-plugin-rich-text
diff --git a/package.json b/package.json
index c2cd648e1..a807d3321 100644
--- a/package.json
+++ b/package.json
@@ -126,6 +126,7 @@
"inquirer": "^3.2.2",
"inquirer-autocomplete-prompt": "^0.12.1",
"ioredis": "3.1.4",
+ "ip": "^1.1.5",
"joi": "^13.0.0",
"jsonwebtoken": "^8.0.0",
"jwt-decode": "^2.2.0",
@@ -143,6 +144,7 @@
"morgan": "^1.9.0",
"ms": "^2.0.0",
"murmurhash-js": "^1.0.0",
+ "name-all-modules-plugin": "^1.0.1",
"node-emoji": "^1.8.1",
"node-fetch": "^1.7.2",
"nodemailer": "^2.6.4",
@@ -195,6 +197,7 @@
"url-search-params": "^0.9.0",
"uuid": "^3.1.0",
"webpack": "^3.10.0",
+ "webpack-manifest-plugin": "^2.0.0-rc.2",
"webpack-sources": "^1.0.1",
"yaml-loader": "^0.4.0",
"yamljs": "^0.2.10"
@@ -216,19 +219,16 @@
"eslint-plugin-mocha": "^4.11.0",
"husky": "^0.14.3",
"identity-obj-proxy": "^3.0.0",
- "ip": "^1.1.5",
"jest": "^21.2.1",
"jest-junit": "^3.6.0",
"lint-staged": "^7.0.0",
"mocha": "^3.1.2",
"mocha-junit-reporter": "^1.12.1",
- "name-all-modules-plugin": "^1.0.1",
"nightwatch": "^0.9.16",
"nodemon": "^1.11.0",
"selenium-standalone": "^6.11.0",
"sinon": "^3.2.1",
"sinon-chai": "^2.13.0",
- "webpack-manifest-plugin": "^2.0.0-rc.2",
"yaml-lint": "^1.0.0"
},
"engines": {
diff --git a/plugins/talk-plugin-downvote/client/.eslintrc.json b/plugins/talk-plugin-downvote/client/.eslintrc.json
new file mode 100644
index 000000000..9fe56bd14
--- /dev/null
+++ b/plugins/talk-plugin-downvote/client/.eslintrc.json
@@ -0,0 +1,23 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "mocha": true
+ },
+ "parserOptions": {
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "experimentalObjectRestSpread": true,
+ "jsx": true
+ }
+ },
+ "parser": "babel-eslint",
+ "plugins": [
+ "react"
+ ],
+ "rules": {
+ "react/jsx-uses-react": "error",
+ "react/jsx-uses-vars": "error",
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
+ }
+}
diff --git a/plugins/talk-plugin-downvote/client/components/DownvoteButton.css b/plugins/talk-plugin-downvote/client/components/DownvoteButton.css
new file mode 100644
index 000000000..71090306b
--- /dev/null
+++ b/plugins/talk-plugin-downvote/client/components/DownvoteButton.css
@@ -0,0 +1,39 @@
+.container {
+ display: inline-block;
+}
+
+.button {
+ color: #2a2a2a;
+ margin: 5px 10px 5px 0px;
+ background: none;
+ padding: 0px;
+ border: none;
+ font-size: inherit;
+ vertical-align: middle;
+
+ &:hover {
+ color: #767676;
+ cursor: pointer;
+ }
+
+ &.downvoted {
+ color: #cc0000;
+
+ &:hover {
+ color: #ff3232;
+ cursor: pointer;
+ }
+ }
+}
+
+.icon {
+ font-size: 12px;
+ padding: 0 3px;
+}
+
+@media (max-width: 425px) {
+ .label {
+ display: none;
+ }
+}
+
diff --git a/plugins/talk-plugin-downvote/client/components/DownvoteButton.js b/plugins/talk-plugin-downvote/client/components/DownvoteButton.js
new file mode 100644
index 000000000..2bf34260b
--- /dev/null
+++ b/plugins/talk-plugin-downvote/client/components/DownvoteButton.js
@@ -0,0 +1,56 @@
+import React from 'react';
+import Icon from './Icon';
+import styles from './DownvoteButton.css';
+import { withReaction } from 'plugin-api/beta/client/hocs';
+import cn from 'classnames';
+
+const plugin = 'talk-plugin-downvote';
+
+class DownvoteButton extends React.Component {
+ handleClick = () => {
+ const {
+ postReaction,
+ deleteReaction,
+ showSignInDialog,
+ alreadyReacted,
+ user,
+ } = this.props;
+
+ // If the current user does not exist, trigger sign in dialog.
+ if (!user) {
+ showSignInDialog();
+ return;
+ }
+
+ if (alreadyReacted) {
+ deleteReaction();
+ } else {
+ postReaction();
+ }
+ };
+
+ render() {
+ const { count, alreadyReacted } = this.props;
+ return (
+
+
+
+ );
+ }
+}
+
+export default withReaction('downvote')(DownvoteButton);
diff --git a/plugins/talk-plugin-downvote/client/components/Icon.js b/plugins/talk-plugin-downvote/client/components/Icon.js
new file mode 100644
index 000000000..56f43029d
--- /dev/null
+++ b/plugins/talk-plugin-downvote/client/components/Icon.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import cn from 'classnames';
+
+// @TODO change icon when we deprecate FA
+export default ({ className }) => (
+
+);
diff --git a/plugins/talk-plugin-downvote/client/index.js b/plugins/talk-plugin-downvote/client/index.js
new file mode 100644
index 000000000..dc60178db
--- /dev/null
+++ b/plugins/talk-plugin-downvote/client/index.js
@@ -0,0 +1,7 @@
+import DownvoteButton from './components/DownvoteButton';
+
+export default {
+ slots: {
+ commentReactions: [DownvoteButton],
+ },
+};
diff --git a/plugins/talk-plugin-downvote/index.js b/plugins/talk-plugin-downvote/index.js
new file mode 100644
index 000000000..c0e29c5ef
--- /dev/null
+++ b/plugins/talk-plugin-downvote/index.js
@@ -0,0 +1,2 @@
+const { getReactionConfig } = require('../../plugin-api/beta/server');
+module.exports = getReactionConfig('downvote');
diff --git a/plugins/talk-plugin-downvote/package.json b/plugins/talk-plugin-downvote/package.json
new file mode 100644
index 000000000..440d18b6a
--- /dev/null
+++ b/plugins/talk-plugin-downvote/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@coralproject/talk-plugin-downvote",
+ "pluginName": "talk-plugin-downvote",
+ "version": "0.0.1",
+ "description": "Downvote comments",
+ "main": "index.js",
+ "author": "The Coral Project Team ",
+ "license": "Apache-2.0"
+}
diff --git a/plugins/talk-plugin-respect/client/Icon.js b/plugins/talk-plugin-respect/client/Icon.js
index 8b45615aa..9af30b86a 100644
--- a/plugins/talk-plugin-respect/client/Icon.js
+++ b/plugins/talk-plugin-respect/client/Icon.js
@@ -1,6 +1,7 @@
import React from 'react';
import cn from 'classnames';
+// @TODO change icon when we deprecate FA
export default ({ className }) => (
);
diff --git a/plugins/talk-plugin-sort-most-downvoted/client/.eslintrc.json b/plugins/talk-plugin-sort-most-downvoted/client/.eslintrc.json
new file mode 100644
index 000000000..c8a6db18a
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-downvoted/client/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@coralproject/eslint-config-talk/client"
+}
diff --git a/plugins/talk-plugin-sort-most-downvoted/client/index.js b/plugins/talk-plugin-sort-most-downvoted/client/index.js
new file mode 100644
index 000000000..fb6ad544c
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-downvoted/client/index.js
@@ -0,0 +1,19 @@
+import translations from './translations.yml';
+import { createSortOption } from 'talk-plugin-viewing-options/client/api/factories';
+import { t } from 'plugin-api/beta/client/services';
+
+const SortOption = createSortOption(
+ () => t('talk-plugin-sort-most-downvoted.label'),
+ { sortBy: 'DOWNVOTES', sortOrder: 'DESC' }
+);
+
+/**
+ * This plugin depends on talk-plugin-viewing-options.
+ */
+
+export default {
+ translations,
+ slots: {
+ viewingOptionsSort: [SortOption],
+ },
+};
diff --git a/plugins/talk-plugin-sort-most-downvoted/client/translations.yml b/plugins/talk-plugin-sort-most-downvoted/client/translations.yml
new file mode 100644
index 000000000..3bff2eed7
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-downvoted/client/translations.yml
@@ -0,0 +1,3 @@
+en:
+ talk-plugin-sort-most-downvoted:
+ label: Most downvoted first
diff --git a/plugins/talk-plugin-sort-most-downvoted/index.js b/plugins/talk-plugin-sort-most-downvoted/index.js
new file mode 100644
index 000000000..f053ebf79
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-downvoted/index.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/plugins/talk-plugin-sort-most-downvoted/package.json b/plugins/talk-plugin-sort-most-downvoted/package.json
new file mode 100644
index 000000000..5f79e0de9
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-downvoted/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@coralproject/talk-plugin-sort-most-downvoted",
+ "pluginName": "talk-plugin-sort-most-downvoted",
+ "version": "0.0.1",
+ "description": "Sort by most downvotes",
+ "main": "index.js",
+ "author": "The Coral Project Team ",
+ "license": "Apache-2.0"
+}
diff --git a/plugins/talk-plugin-sort-most-upvoted/client/.eslintrc.json b/plugins/talk-plugin-sort-most-upvoted/client/.eslintrc.json
new file mode 100644
index 000000000..c8a6db18a
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-upvoted/client/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@coralproject/eslint-config-talk/client"
+}
diff --git a/plugins/talk-plugin-sort-most-upvoted/client/index.js b/plugins/talk-plugin-sort-most-upvoted/client/index.js
new file mode 100644
index 000000000..26744b6d6
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-upvoted/client/index.js
@@ -0,0 +1,19 @@
+import translations from './translations.yml';
+import { createSortOption } from 'talk-plugin-viewing-options/client/api/factories';
+import { t } from 'plugin-api/beta/client/services';
+
+const SortOption = createSortOption(
+ () => t('talk-plugin-sort-most-upvoted.label'),
+ { sortBy: 'UPVOTES', sortOrder: 'DESC' }
+);
+
+/**
+ * This plugin depends on talk-plugin-viewing-options.
+ */
+
+export default {
+ translations,
+ slots: {
+ viewingOptionsSort: [SortOption],
+ },
+};
diff --git a/plugins/talk-plugin-sort-most-upvoted/client/translations.yml b/plugins/talk-plugin-sort-most-upvoted/client/translations.yml
new file mode 100644
index 000000000..7b3c7c29f
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-upvoted/client/translations.yml
@@ -0,0 +1,3 @@
+en:
+ talk-plugin-sort-most-upvoted:
+ label: Most upvoted first
diff --git a/plugins/talk-plugin-sort-most-upvoted/index.js b/plugins/talk-plugin-sort-most-upvoted/index.js
new file mode 100644
index 000000000..f053ebf79
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-upvoted/index.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/plugins/talk-plugin-sort-most-upvoted/package.json b/plugins/talk-plugin-sort-most-upvoted/package.json
new file mode 100644
index 000000000..221e22c22
--- /dev/null
+++ b/plugins/talk-plugin-sort-most-upvoted/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@coralproject/talk-plugin-sort-most-upvoted",
+ "pluginName": "talk-plugin-sort-most-upvoted",
+ "version": "0.0.1",
+ "description": "Sort by most upvotes",
+ "main": "index.js",
+ "author": "The Coral Project Team ",
+ "license": "Apache-2.0"
+}
diff --git a/plugins/talk-plugin-upvote/client/.eslintrc.json b/plugins/talk-plugin-upvote/client/.eslintrc.json
new file mode 100644
index 000000000..9fe56bd14
--- /dev/null
+++ b/plugins/talk-plugin-upvote/client/.eslintrc.json
@@ -0,0 +1,23 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "mocha": true
+ },
+ "parserOptions": {
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "experimentalObjectRestSpread": true,
+ "jsx": true
+ }
+ },
+ "parser": "babel-eslint",
+ "plugins": [
+ "react"
+ ],
+ "rules": {
+ "react/jsx-uses-react": "error",
+ "react/jsx-uses-vars": "error",
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
+ }
+}
diff --git a/plugins/talk-plugin-upvote/client/components/Icon.js b/plugins/talk-plugin-upvote/client/components/Icon.js
new file mode 100644
index 000000000..1b34c40cd
--- /dev/null
+++ b/plugins/talk-plugin-upvote/client/components/Icon.js
@@ -0,0 +1,7 @@
+import React from 'react';
+import cn from 'classnames';
+
+// @TODO change icon when we deprecate FA
+export default ({ className }) => (
+
+);
diff --git a/plugins/talk-plugin-upvote/client/components/UpvoteButton.css b/plugins/talk-plugin-upvote/client/components/UpvoteButton.css
new file mode 100644
index 000000000..56caa60b1
--- /dev/null
+++ b/plugins/talk-plugin-upvote/client/components/UpvoteButton.css
@@ -0,0 +1,39 @@
+.container {
+ display: inline-block;
+}
+
+.button {
+ color: #2a2a2a;
+ margin: 5px 10px 5px 0px;
+ background: none;
+ padding: 0px;
+ border: none;
+ font-size: inherit;
+ vertical-align: middle;
+
+ &:hover {
+ color: #767676;
+ cursor: pointer;
+ }
+
+ &.upvoted {
+ color: #008000;
+
+ &:hover {
+ color: #66b266;
+ cursor: pointer;
+ }
+ }
+}
+
+.icon {
+ font-size: 12px;
+ padding: 0 3px;
+}
+
+@media (max-width: 425px) {
+ .label {
+ display: none;
+ }
+}
+
diff --git a/plugins/talk-plugin-upvote/client/components/UpvoteButton.js b/plugins/talk-plugin-upvote/client/components/UpvoteButton.js
new file mode 100644
index 000000000..c0db1fe01
--- /dev/null
+++ b/plugins/talk-plugin-upvote/client/components/UpvoteButton.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import Icon from './Icon';
+import styles from './UpvoteButton.css';
+import { withReaction } from 'plugin-api/beta/client/hocs';
+import cn from 'classnames';
+
+const plugin = 'talk-plugin-upvote';
+
+class UpvoteButton extends React.Component {
+ handleClick = () => {
+ const {
+ postReaction,
+ deleteReaction,
+ showSignInDialog,
+ alreadyReacted,
+ user,
+ } = this.props;
+
+ // If the current user does not exist, trigger sign in dialog.
+ if (!user) {
+ showSignInDialog();
+ return;
+ }
+
+ if (alreadyReacted) {
+ deleteReaction();
+ } else {
+ postReaction();
+ }
+ };
+
+ render() {
+ const { count, alreadyReacted } = this.props;
+ return (
+
+
+
+ );
+ }
+}
+
+export default withReaction('upvote')(UpvoteButton);
diff --git a/plugins/talk-plugin-upvote/client/index.js b/plugins/talk-plugin-upvote/client/index.js
new file mode 100644
index 000000000..fd81298da
--- /dev/null
+++ b/plugins/talk-plugin-upvote/client/index.js
@@ -0,0 +1,7 @@
+import UpvoteButton from './components/UpvoteButton';
+
+export default {
+ slots: {
+ commentReactions: [UpvoteButton],
+ },
+};
diff --git a/plugins/talk-plugin-upvote/index.js b/plugins/talk-plugin-upvote/index.js
new file mode 100644
index 000000000..119537ab3
--- /dev/null
+++ b/plugins/talk-plugin-upvote/index.js
@@ -0,0 +1,2 @@
+const { getReactionConfig } = require('../../plugin-api/beta/server');
+module.exports = getReactionConfig('upvote');
diff --git a/plugins/talk-plugin-upvote/package.json b/plugins/talk-plugin-upvote/package.json
new file mode 100644
index 000000000..bf32d911a
--- /dev/null
+++ b/plugins/talk-plugin-upvote/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@coralproject/talk-plugin-upvote",
+ "pluginName": "talk-plugin-upvote",
+ "version": "0.0.1",
+ "description": "Upvote comments",
+ "main": "index.js",
+ "author": "The Coral Project Team ",
+ "license": "Apache-2.0"
+}
diff --git a/routes/index.js b/routes/index.js
index 143ceefd8..489a5e8b5 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -13,6 +13,7 @@ const staticMiddleware = require('express-static-gzip');
const { DISABLE_STATIC_SERVER } = require('../config');
const { passport } = require('../services/passport');
const { MOUNT_PATH } = require('../url');
+const url = require('url');
const context = require('../middleware/context');
const router = express.Router();
@@ -31,8 +32,8 @@ if (!DISABLE_STATIC_SERVER) {
/**
* Redirect old embed calls.
*/
- const oldEmbed = path.resolve(MOUNT_PATH, 'embed.js');
- const newEmbed = path.resolve(MOUNT_PATH, 'static/embed.js');
+ const oldEmbed = url.resolve(MOUNT_PATH, 'embed.js');
+ const newEmbed = url.resolve(MOUNT_PATH, 'static/embed.js');
router.get('/embed.js', (req, res) => {
console.warn(
`deprecation warning: ${oldEmbed} will be phased out soon, please replace calls from ${oldEmbed} to ${newEmbed}`
@@ -132,9 +133,9 @@ if (process.env.NODE_ENV !== 'production') {
router.get('/', async (req, res, next) => {
try {
await SetupService.isAvailable();
- return res.redirect('/admin/install');
+ return res.redirect(url.resolve(MOUNT_PATH, 'admin/install'));
} catch (e) {
- return res.redirect('/admin');
+ return res.redirect(url.resolve(MOUNT_PATH, 'admin'));
}
});
}