From 66f068359fa774ba630d821998539b9158c5c256 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 4 Jan 2018 14:14:27 -0700 Subject: [PATCH 01/12] Added index for `item_id` on Actions. --- models/action.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/models/action.js b/models/action.js index 8aa7322e3..e378eec18 100644 --- a/models/action.js +++ b/models/action.js @@ -21,8 +21,8 @@ const ActionSchema = new Schema({ item_id: String, user_id: String, - // The element that summaries will additionally group on in addtion to their action_type, item_type, and - // item_id. + // The element that summaries will additionally group on in addition to their + // action_type, item_type, and item_id. group_id: String, // Additional metadata stored on the field. @@ -34,6 +34,14 @@ const ActionSchema = new Schema({ } }); +// Create an index on the `item_id` field so that queries looking for +// actions based on the item id can resolve faster. +ActionSchema.index({ + 'item_id': 1 +}, { + background: true +}); + const Action = mongoose.model('Action', ActionSchema); module.exports = Action; From e256b0da8a61b0111d69fd6641f3b7bbc8033324 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 12 Jan 2018 15:47:09 -0300 Subject: [PATCH 02/12] Adding format to admin comment - CommentFormatter --- ...BodyHighlighter.js => CommentFormatter.js} | 50 +++++++++++++++---- .../routes/Moderation/components/Comment.js | 13 +++-- 2 files changed, 47 insertions(+), 16 deletions(-) rename client/coral-admin/src/components/{CommentBodyHighlighter.js => CommentFormatter.js} (63%) diff --git a/client/coral-admin/src/components/CommentBodyHighlighter.js b/client/coral-admin/src/components/CommentFormatter.js similarity index 63% rename from client/coral-admin/src/components/CommentBodyHighlighter.js rename to client/coral-admin/src/components/CommentFormatter.js index 9e381bf0b..6c086b6aa 100644 --- a/client/coral-admin/src/components/CommentBodyHighlighter.js +++ b/client/coral-admin/src/components/CommentFormatter.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { matchLinks } from '../utils'; import memoize from 'lodash/memoize'; @@ -62,16 +63,43 @@ function markLinks(body) { return content; } -export default ({ suspectWords, bannedWords, body, ...rest }) => { - // First highlight links. - const content = markLinks(body).map((element, index) => { - // Keep highlighted links. - if (typeof element !== 'string') { - return element; - } +function format(body, { suspectWords, bannedWords, className = 'comment' }) { + // Breaking the body by line break + const textbreaks = body.split('\n'); - // Highlight suspect and banned phrase inside this part of text. - return markPhrases(element, suspectWords, bannedWords, index); - }); - return
{content}
; + return ( + + {textbreaks.map((line, i) => { + const content = markLinks(line).map((element, index) => { + // Keep highlighted links. + if (typeof element !== 'string') { + return element; + } + + // Highlight suspect and banned phrase inside this part of text. + return markPhrases(element, suspectWords, bannedWords, index); + }); + + return ( + + {content} + {i !== textbreaks.length - 1 && ( +
+ )} +
+ ); + })} +
+ ); +} + +const CommentFormatter = ({ body, settings, ...rest }) => { + return
{format(body, settings)}
; }; + +CommentFormatter.propTypes = { + settings: PropTypes.object, + body: PropTypes.string, +}; + +export default CommentFormatter; diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index fc6894a46..504c3e04d 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -8,7 +8,7 @@ import styles from './Comment.css'; import CommentLabels from 'coral-admin/src/components/CommentLabels'; import CommentAnimatedEdit from 'coral-admin/src/components/CommentAnimatedEdit'; import Slot from 'coral-framework/components/Slot'; -import CommentBodyHighlighter from 'coral-admin/src/components/CommentBodyHighlighter'; +import CommentFormatter from 'coral-admin/src/components/CommentFormatter'; import IfHasLink from 'coral-admin/src/components/IfHasLink'; import cn from 'classnames'; import ApproveButton from 'coral-admin/src/components/ApproveButton'; @@ -126,11 +126,14 @@ class Comment extends React.Component {
- {' '} + /> Date: Fri, 12 Jan 2018 15:51:49 -0300 Subject: [PATCH 03/12] Adding changes to the comments on the User Drawer Detail --- client/coral-admin/src/components/UserDetailComment.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/coral-admin/src/components/UserDetailComment.js b/client/coral-admin/src/components/UserDetailComment.js index b1cc486ec..669f035fe 100644 --- a/client/coral-admin/src/components/UserDetailComment.js +++ b/client/coral-admin/src/components/UserDetailComment.js @@ -5,7 +5,7 @@ import { Link } from 'react-router'; import { Icon } from 'coral-ui'; import CommentDetails from './CommentDetails'; import styles from './UserDetailComment.css'; -import CommentBodyHighlighter from 'coral-admin/src/components/CommentBodyHighlighter'; +import CommentFormatter from 'coral-admin/src/components/CommentFormatter'; import IfHasLink from 'coral-admin/src/components/IfHasLink'; import cn from 'classnames'; import CommentAnimatedEdit from './CommentAnimatedEdit'; @@ -78,9 +78,11 @@ class UserDetailComment extends React.Component {
- {' '} Date: Fri, 12 Jan 2018 15:53:32 -0300 Subject: [PATCH 04/12] Wrong name --- client/coral-admin/src/components/CommentAnimatedEdit.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/coral-admin/src/components/CommentAnimatedEdit.js b/client/coral-admin/src/components/CommentAnimatedEdit.js index 43fbf9fbf..12280b204 100644 --- a/client/coral-admin/src/components/CommentAnimatedEdit.js +++ b/client/coral-admin/src/components/CommentAnimatedEdit.js @@ -4,7 +4,7 @@ import { CSSTransitionGroup } from 'react-transition-group'; import styles from './CommentAnimatedEdit.css'; import PropTypes from 'prop-types'; -const CommentBodyHighlighter = ({ children, body }) => { +const CommentAnimatedEdit = ({ children, body }) => { return ( { ); }; -CommentBodyHighlighter.propTypes = { +CommentAnimatedEdit.propTypes = { children: PropTypes.node, body: PropTypes.string, }; -export default CommentBodyHighlighter; +export default CommentAnimatedEdit; From bfa2f6edb958df93e5a042b6123d384cbb229c73 Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 15 Jan 2018 12:59:39 -0300 Subject: [PATCH 05/12] Updates --- .../src/components/CommentFormatter.js | 18 +++++++++++------- .../src/components/UserDetailComment.js | 9 ++++----- .../routes/Moderation/components/Comment.js | 8 +++----- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/client/coral-admin/src/components/CommentFormatter.js b/client/coral-admin/src/components/CommentFormatter.js index 6c086b6aa..152ae8547 100644 --- a/client/coral-admin/src/components/CommentFormatter.js +++ b/client/coral-admin/src/components/CommentFormatter.js @@ -63,12 +63,18 @@ function markLinks(body) { return content; } -function format(body, { suspectWords, bannedWords, className = 'comment' }) { +const CommentFormatter = ( + body, + suspectWords, + bannedWords, + className = 'comment', + ...rest +) => { // Breaking the body by line break const textbreaks = body.split('\n'); return ( - + {textbreaks.map((line, i) => { const content = markLinks(line).map((element, index) => { // Keep highlighted links. @@ -91,14 +97,12 @@ function format(body, { suspectWords, bannedWords, className = 'comment' }) { })} ); -} - -const CommentFormatter = ({ body, settings, ...rest }) => { - return
{format(body, settings)}
; }; CommentFormatter.propTypes = { - settings: PropTypes.object, + className: PropTypes.string, + bannedWords: PropTypes.array, + suspectWords: PropTypes.array, body: PropTypes.string, }; diff --git a/client/coral-admin/src/components/UserDetailComment.js b/client/coral-admin/src/components/UserDetailComment.js index 669f035fe..2d98edcb3 100644 --- a/client/coral-admin/src/components/UserDetailComment.js +++ b/client/coral-admin/src/components/UserDetailComment.js @@ -79,12 +79,11 @@ class UserDetailComment extends React.Component {
{' '} + className="talk-admin-user-detail-comment" + />
Date: Mon, 15 Jan 2018 13:14:24 -0300 Subject: [PATCH 06/12] missing brackets --- client/coral-admin/src/components/CommentFormatter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/components/CommentFormatter.js b/client/coral-admin/src/components/CommentFormatter.js index 152ae8547..8874d123f 100644 --- a/client/coral-admin/src/components/CommentFormatter.js +++ b/client/coral-admin/src/components/CommentFormatter.js @@ -63,13 +63,13 @@ function markLinks(body) { return content; } -const CommentFormatter = ( +const CommentFormatter = ({ body, suspectWords, bannedWords, className = 'comment', ...rest -) => { +}) => { // Breaking the body by line break const textbreaks = body.split('\n'); From 45d4c79c265fe8f8290142a29a6d1db2791fd275 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 16 Jan 2018 12:30:38 -0700 Subject: [PATCH 07/12] added reply bug --- errors.js | 16 +++++- locales/en.yml | 1 + locales/es.yml | 1 + services/comments.js | 36 ++++++++---- test/.eslintrc.json | 2 +- test/server/graph/mutations/createComment.js | 2 + test/server/services/comments.js | 58 ++++++++++++++++++++ 7 files changed, 100 insertions(+), 16 deletions(-) diff --git a/errors.js b/errors.js index 868be0488..a19afe3c6 100644 --- a/errors.js +++ b/errors.js @@ -265,6 +265,15 @@ const ErrCannotIgnoreStaff = new APIError('Cannot ignore staff members.', { status: 400, }); +// ErrParentDoesNotVisible is returned when the user tries to reply to a comment +// that isn't visible. +const ErrParentDoesNotVisible = new APIError( + 'Cannot reply to a comment that is not visible', + { + translation_key: 'COMMENT_PARENT_NOT_VISIBLE', + } +); + module.exports = { APIError, ErrAlreadyExists, @@ -276,24 +285,25 @@ module.exports = { ErrContainsProfanity, ErrEditWindowHasEnded, ErrEmailTaken, + ErrEmailVerificationToken, ErrInstallLock, ErrInvalidAssetURL, ErrLoginAttemptMaximumExceeded, ErrMaxRateLimit, ErrMissingEmail, ErrMissingPassword, - ErrEmailVerificationToken, - ErrPasswordResetToken, ErrMissingUsername, ErrNotAuthorized, ErrNotFound, ErrNotVerified, + ErrParentDoesNotVisible, + ErrPasswordResetToken, ErrPasswordTooShort, ErrPermissionUpdateUsername, + ErrSameUsernameProvided, ErrSettingsInit, ErrSettingsNotInit, ErrSpecialChars, ErrUsernameTaken, - ErrSameUsernameProvided, ExtendableError, }; diff --git a/locales/en.yml b/locales/en.yml index 0da121202..39cd3f0d9 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -198,6 +198,7 @@ en: embedlink: copy: "Copy to Clipboard" error: + COMMENT_PARENT_NOT_VISIBLE: "The comment that you're replying to has been removed or doesn't exist." EMAIL_VERIFICATION_TOKEN_INVALID: "Email verification token is invalid." PASSWORD_RESET_TOKEN_INVALID: "Your password reset link is invalid." COMMENT_TOO_SHORT: "Comments should be more than one character, please revise your comment and try again." diff --git a/locales/es.yml b/locales/es.yml index 584e83f7b..010119728 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -182,6 +182,7 @@ es: embedlink: copy: "Copiar al portapapeles" error: + COMMENT_PARENT_NOT_VISIBLE: "El comentario a la que estás contestando ha sido eliminado o no existe." COMMENT_TOO_SHORT: "Tu comentario debe tener algo escrito" COMMENTING_CLOSED: "Los comentarios ya estan cerrados" confirm_password: "Las contraseñas no coinciden. Inténtelo nuevamente" diff --git a/services/comments.js b/services/comments.js index 344115054..d82f5046d 100644 --- a/services/comments.js +++ b/services/comments.js @@ -7,24 +7,35 @@ const SettingsService = require('./settings'); const cloneDeep = require('lodash/cloneDeep'); const errors = require('../errors'); const events = require('./events'); +const merge = require('lodash/merge'); const { COMMENTS_NEW, COMMENTS_EDIT } = require('./events/constants'); module.exports = class CommentsService { /** * Creates a new Comment that came from a public source. - * @param {Mixed} comment either a single comment or an array of comments. + * @param {Mixed} input either a single comment or an array of comments. * @return {Promise} */ - static async publicCreate(comment) { + static async publicCreate(input) { // Check to see if this is an array of comments, if so map it out. - if (Array.isArray(comment)) { - return Promise.all(comment.map(CommentsService.publicCreate)); + if (Array.isArray(input)) { + return Promise.all(input.map(CommentsService.publicCreate)); } - const { status = 'NONE' } = comment; + const { status = 'NONE', parent_id = null } = input; - const commentModel = new CommentModel( - Object.assign( + // Check to see if we are replying to a comment, and if that comment is + // visible. + if (parent_id !== null) { + const parent = await CommentModel.findOne({ id: parent_id }); + if (parent === null || !parent.visible) { + throw errors.ErrParentDoesNotVisible; + } + } + + // Turn the comment into a new object. + const comment = new CommentModel( + merge( { status_history: status ? [ @@ -36,21 +47,22 @@ module.exports = class CommentsService { : [], body_history: [ { - body: comment.body, + body: input.body, created_at: new Date(), }, ], }, - comment + input ) ); - const savedCommentModel = await commentModel.save(); + // Save the comment to the database. + await comment.save(); // Emit that the comment was created! - await events.emitAsync(COMMENTS_NEW, savedCommentModel); + await events.emitAsync(COMMENTS_NEW, comment); - return savedCommentModel; + return comment; } /** diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 027872c7d..36b8a3078 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -7,6 +7,6 @@ ], "extends": "../.eslintrc.json", "rules": { - "mocha/no-exclusive-tests": "warn" + "mocha/no-exclusive-tests": "error" } } diff --git a/test/server/graph/mutations/createComment.js b/test/server/graph/mutations/createComment.js index 88ca3415a..8a5a98aa1 100644 --- a/test/server/graph/mutations/createComment.js +++ b/test/server/graph/mutations/createComment.js @@ -116,6 +116,8 @@ describe('graph.mutations.createComment', () => { } expect(data.createComment).to.have.property('errors').null; expect(data.createComment).to.have.property('comment').not.null; + expect(data.createComment.comment).to.have.property('id').not + .null; } } ); diff --git a/test/server/services/comments.js b/test/server/services/comments.js index c9ec1315d..b0af0b0e0 100644 --- a/test/server/services/comments.js +++ b/test/server/services/comments.js @@ -131,6 +131,64 @@ describe('services.CommentsService', () => { }); describe('#publicCreate()', () => { + describe('does not allow replies to comments that are not visible', () => { + it('parent not found', async () => { + try { + await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'ACCEPTED', + parent_id: 'does not exist', + }); + throw new Error('comment should not have been created'); + } catch (err) { + expect(err).to.have.property( + 'translation_key', + 'COMMENT_PARENT_NOT_VISIBLE' + ); + } + }); + + it('parent REJECTED', async () => { + try { + const parent = await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'REJECTED', + }); + await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'ACCEPTED', + parent_id: parent.id, + }); + throw new Error('comment should not have been created'); + } catch (err) { + expect(err).to.have.property( + 'translation_key', + 'COMMENT_PARENT_NOT_VISIBLE' + ); + } + }); + + it('parent SYSTEM_WITHHELD', async () => { + try { + const parent = await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'SYSTEM_WITHHELD', + }); + await CommentsService.publicCreate({ + body: 'This is a comment!', + status: 'ACCEPTED', + parent_id: parent.id, + }); + throw new Error('comment should not have been created'); + } catch (err) { + expect(err).to.have.property( + 'translation_key', + 'COMMENT_PARENT_NOT_VISIBLE' + ); + } + }); + }); + it('creates a new comment', async () => { const c = await CommentsService.publicCreate({ body: 'This is a comment!', From b709cb485e3ceec5e3cdca030ff1524b685e6848 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 16 Jan 2018 14:11:24 -0700 Subject: [PATCH 08/12] refactored publicCreate --- services/comments.js | 15 +++------- test/server/graph/queries/asset.js | 14 +++++---- test/server/services/assets.js | 46 ++++++++++++++++-------------- test/server/services/comments.js | 25 ---------------- 4 files changed, 36 insertions(+), 64 deletions(-) diff --git a/services/comments.js b/services/comments.js index d82f5046d..e18e04970 100644 --- a/services/comments.js +++ b/services/comments.js @@ -13,15 +13,11 @@ const { COMMENTS_NEW, COMMENTS_EDIT } = require('./events/constants'); module.exports = class CommentsService { /** * Creates a new Comment that came from a public source. - * @param {Mixed} input either a single comment or an array of comments. + * @param {Object} input either a single comment or an array of comments. * @return {Promise} */ static async publicCreate(input) { - // Check to see if this is an array of comments, if so map it out. - if (Array.isArray(input)) { - return Promise.all(input.map(CommentsService.publicCreate)); - } - + // Extract the parent_id from the comment, if there is one. const { status = 'NONE', parent_id = null } = input; // Check to see if we are replying to a comment, and if that comment is @@ -33,8 +29,8 @@ module.exports = class CommentsService { } } - // Turn the comment into a new object. - const comment = new CommentModel( + // Create the comment in the database. + const comment = await CommentModel.create( merge( { status_history: status @@ -56,9 +52,6 @@ module.exports = class CommentsService { ) ); - // Save the comment to the database. - await comment.save(); - // Emit that the comment was created! await events.emitAsync(COMMENTS_NEW, comment); diff --git a/test/server/graph/queries/asset.js b/test/server/graph/queries/asset.js index af19e4f69..f976bd37d 100644 --- a/test/server/graph/queries/asset.js +++ b/test/server/graph/queries/asset.js @@ -34,12 +34,14 @@ describe('graph.queries.asset', () => { username: 'usernameC', }, ]); - comments = await CommentsService.publicCreate( - [0, 0, 1, 1].map(idx => ({ - author_id: users[idx].id, - asset_id: assets[idx].id, - body: `hello there! ${String(Math.random()).slice(2)}`, - })) + comments = await Promise.all( + [0, 0, 1, 1].map(idx => + CommentsService.publicCreate({ + author_id: users[idx].id, + asset_id: assets[idx].id, + body: `hello there! ${String(Math.random()).slice(2)}`, + }) + ) ); }); diff --git a/test/server/services/assets.js b/test/server/services/assets.js index c071b5634..923f28751 100644 --- a/test/server/services/assets.js +++ b/test/server/services/assets.js @@ -176,28 +176,30 @@ describe('services.AssetsService', () => { ); // Create some comments on both assets. - await CommentsService.publicCreate([ - { - asset_id: '1', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - asset_id: '1', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - asset_id: '2', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - asset_id: '2', - body: 'This is a comment!', - status: 'ACCEPTED', - }, - ]); + await Promise.all( + [ + { + asset_id: '1', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + { + asset_id: '1', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + { + asset_id: '2', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + { + asset_id: '2', + body: 'This is a comment!', + status: 'ACCEPTED', + }, + ].map(comment => CommentsService.publicCreate(comment)) + ); // Merge all the comments from asset 1 into asset 2, followed by deleting // asset 1. diff --git a/test/server/services/comments.js b/test/server/services/comments.js index b0af0b0e0..2d0b37163 100644 --- a/test/server/services/comments.js +++ b/test/server/services/comments.js @@ -200,31 +200,6 @@ describe('services.CommentsService', () => { expect(c.id).to.be.uuid; expect(c.status).to.be.equal('ACCEPTED'); }); - - it('creates many new comments', async () => { - const [c1, c2, c3] = await CommentsService.publicCreate([ - { - body: 'This is a comment!', - status: 'ACCEPTED', - }, - { - body: 'This is another comment!', - }, - { - body: 'This is a rejected comment!', - status: 'REJECTED', - }, - ]); - - expect(c1).to.not.be.null; - expect(c1.status).to.be.equal('ACCEPTED'); - - expect(c2).to.not.be.null; - expect(c2.status).to.be.equal('NONE'); - - expect(c3).to.not.be.null; - expect(c3.status).to.be.equal('REJECTED'); - }); }); describe('#edit', () => { From e3cdad1f2aaa3a1da1523dc8b48796c0bca071f8 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 16 Jan 2018 16:47:05 -0700 Subject: [PATCH 09/12] added asset re-write --- bin/cli-assets | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ models/asset.js | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/bin/cli-assets b/bin/cli-assets index d14df320b..e13d7db28 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -14,6 +14,8 @@ const AssetsService = require('../services/assets'); const mongoose = require('../services/mongoose'); const scraper = require('../services/scraper'); const inquirer = require('inquirer'); +const { URL } = require('url'); +const errors = require('../errors'); // Register the shutdown criteria. util.onshutdown([() => mongoose.disconnect()]); @@ -125,6 +127,51 @@ async function merge(srcID, dstID) { } } +async function rewrite(search, replace) { + try { + search = new RegExp(search); + + const assets = await AssetModel.find({ + url: { $regex: search }, + }); + if (assets.length === 0) { + console.log(`No assets found with the pattern: ${search}`); + return util.shutdown(0); + } + + const bulk = AssetModel.collection.initializeUnorderedBulkOp(); + + let ops = 0; + assets.forEach(({ id, url: oldURL }) => { + // Replace the url. + const newURL = oldURL.replace(search, replace); + + // Try to validate that the new url is valid. + try { + new URL(newURL); + } catch (err) { + throw errors.ErrInvalidAssetURL; + } + + // If the url was updated with the operation, then queue up the update op. + if (newURL !== oldURL) { + ops++; + bulk.find({ id }).updateOne({ $set: { url: newURL } }); + } + }); + + if (ops > 0) { + await bulk.execute(); + } + console.log(`${ops} assets had their url's updated`); + + util.shutdown(0); + } catch (err) { + console.error(err); + util.shutdown(1); + } +} + //============================================================================== // Setting up the program command line arguments. //============================================================================== @@ -151,6 +198,13 @@ program ) .action(merge); +program + .command('rewrite ') + .description( + "rewrites asset url's using the provided regex replacement pattern" + ) + .action(rewrite); + program.parse(process.argv); // If there is no command listed, output help. diff --git a/models/asset.js b/models/asset.js index 1f565a110..68ca08bbf 100644 --- a/models/asset.js +++ b/models/asset.js @@ -41,7 +41,7 @@ const AssetSchema = new Schema( publication_date: Date, modified_date: Date, - // This object is used exclusivly for storing settings that are to override + // This object is used exclusively for storing settings that are to override // the base settings from the base Settings object. This is to be accessed // always after running `rectifySettings` against it. settings: { From 45d6800143517003ed0b127935b15857678a8a9b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 16 Jan 2018 17:10:59 -0700 Subject: [PATCH 10/12] added dry mode --- bin/cli-assets | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/bin/cli-assets b/bin/cli-assets index e13d7db28..78229d91b 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -15,7 +15,6 @@ const mongoose = require('../services/mongoose'); const scraper = require('../services/scraper'); const inquirer = require('inquirer'); const { URL } = require('url'); -const errors = require('../errors'); // Register the shutdown criteria. util.onshutdown([() => mongoose.disconnect()]); @@ -127,7 +126,7 @@ async function merge(srcID, dstID) { } } -async function rewrite(search, replace) { +async function rewrite(search, replace, options) { try { search = new RegExp(search); @@ -139,9 +138,7 @@ async function rewrite(search, replace) { return util.shutdown(0); } - const bulk = AssetModel.collection.initializeUnorderedBulkOp(); - - let ops = 0; + let opts = []; assets.forEach(({ id, url: oldURL }) => { // Replace the url. const newURL = oldURL.replace(search, replace); @@ -150,20 +147,41 @@ async function rewrite(search, replace) { try { new URL(newURL); } catch (err) { - throw errors.ErrInvalidAssetURL; + throw new Error( + `Rewrite would have replaced the valid URL ${oldURL} with an invalid one ${newURL}` + ); } - // If the url was updated with the operation, then queue up the update op. - if (newURL !== oldURL) { - ops++; - bulk.find({ id }).updateOne({ $set: { url: newURL } }); - } + opts.push({ + find: { id }, + updateOne: { $set: { url: newURL } }, + id, + oldURL, + newURL, + }); }); - if (ops > 0) { - await bulk.execute(); + if (opts.length > 0) { + if (options.dryRun) { + const table = new Table({ head: ['ID', 'Old URL', 'New URL'] }); + + opts.forEach(({ id, oldURL, newURL }) => { + table.push([id, oldURL, newURL]); + }); + + console.log(table.toString()); + } else { + const bulk = AssetModel.collection.initializeUnorderedBulkOp(); + opts.forEach(({ find, updateOne, oldURL, newURL }) => { + // If the url was updated with the operation, then queue up the update op. + if (newURL !== oldURL) { + bulk.find(find).updateOne(updateOne); + } + }); + await bulk.execute(); + console.log(`${opts.length} assets had their url's updated`); + } } - console.log(`${ops} assets had their url's updated`); util.shutdown(0); } catch (err) { @@ -200,6 +218,7 @@ program program .command('rewrite ') + .option('-d, --dry-run', 'enables dry run of the replacement') .description( "rewrites asset url's using the provided regex replacement pattern" ) From 3938b7883fe2b532d812ec0aa79043bd76afcaa5 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 17 Jan 2018 13:24:54 -0700 Subject: [PATCH 11/12] introduced some locale fixes --- client/coral-framework/services/bootstrap.js | 6 +++- client/coral-framework/services/i18n.js | 35 ++++++++++++-------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/client/coral-framework/services/bootstrap.js b/client/coral-framework/services/bootstrap.js index b1014f2db..912c6c532 100644 --- a/client/coral-framework/services/bootstrap.js +++ b/client/coral-framework/services/bootstrap.js @@ -5,7 +5,7 @@ import EventEmitter from 'eventemitter2'; import { createReduxEmitter } from './events'; import { createRestClient } from './rest'; import thunk from 'redux-thunk'; -import { loadTranslations } from './i18n'; +import { setupTranslations, loadTranslations } from './i18n'; import bowser from 'bowser'; import noop from 'lodash/noop'; import { BASE_PATH } from 'coral-framework/constants/url'; @@ -88,6 +88,10 @@ export async function createContext({ const pymStorage = createPymStorage(pym); const history = createHistory(BASE_PATH); const introspection = createIntrospection(introspectionData); + + // Setup the translation framework with the storage. + setupTranslations(storage); + let store = null; const token = () => { // Try to get the token from localStorage. If it isn't here, it may diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 6c4aa00a9..4c387a0ca 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -3,6 +3,8 @@ import has from 'lodash/has'; import get from 'lodash/get'; import merge from 'lodash/merge'; +import { createStorage } from 'coral-framework/services/storage'; + import moment from 'moment'; import 'moment/locale/da'; import 'moment/locale/es'; @@ -38,25 +40,34 @@ const translations = { let lang; let timeagoInstance; -function setLocale(locale) { +function setLocale(storage, locale) { try { - localStorage.setItem('locale', locale); + if (storage) { + storage.setItem('locale', locale); + } } catch (err) { console.error(err); } } -function getLocale() { - return ( - localStorage.getItem('locale') || - navigator.language || - defaultLanguage - ).split('-')[0]; +function getLocale(storage) { + try { + const locale = ( + (storage && storage.getItem('locale')) || + navigator.language || + defaultLanguage + ).split('-')[0]; + + return locale; + } catch (err) { + console.error(err); + return null; + } } -function init() { - const locale = getLocale(); - setLocale(locale); +export function setupTranslations(storage) { + const locale = getLocale(storage); + setLocale(storage, locale); // Setting moment moment.locale(locale); @@ -113,5 +124,3 @@ export function t(key, ...replacements) { } export default t; - -init(); From c612902779d8ac890726f443d5fbb74de5b53f0a Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 17 Jan 2018 13:41:02 -0700 Subject: [PATCH 12/12] fixed styles --- client/coral-framework/services/bootstrap.js | 6 +----- client/coral-framework/services/i18n.js | 16 ++++++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/coral-framework/services/bootstrap.js b/client/coral-framework/services/bootstrap.js index 912c6c532..b1014f2db 100644 --- a/client/coral-framework/services/bootstrap.js +++ b/client/coral-framework/services/bootstrap.js @@ -5,7 +5,7 @@ import EventEmitter from 'eventemitter2'; import { createReduxEmitter } from './events'; import { createRestClient } from './rest'; import thunk from 'redux-thunk'; -import { setupTranslations, loadTranslations } from './i18n'; +import { loadTranslations } from './i18n'; import bowser from 'bowser'; import noop from 'lodash/noop'; import { BASE_PATH } from 'coral-framework/constants/url'; @@ -88,10 +88,6 @@ export async function createContext({ const pymStorage = createPymStorage(pym); const history = createHistory(BASE_PATH); const introspection = createIntrospection(introspectionData); - - // Setup the translation framework with the storage. - setupTranslations(storage); - let store = null; const token = () => { // Try to get the token from localStorage. If it isn't here, it may diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 4c387a0ca..91f8560cc 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -3,14 +3,14 @@ import has from 'lodash/has'; import get from 'lodash/get'; import merge from 'lodash/merge'; -import { createStorage } from 'coral-framework/services/storage'; - import moment from 'moment'; import 'moment/locale/da'; import 'moment/locale/es'; import 'moment/locale/fr'; import 'moment/locale/pt-br'; +import { createStorage } from 'coral-framework/services/storage'; + import daTA from 'timeago.js/locales/da'; import esTA from 'timeago.js/locales/es'; import frTA from 'timeago.js/locales/fr'; @@ -52,20 +52,21 @@ function setLocale(storage, locale) { function getLocale(storage) { try { - const locale = ( + return ( (storage && storage.getItem('locale')) || navigator.language || defaultLanguage ).split('-')[0]; - - return locale; } catch (err) { console.error(err); return null; } } -export function setupTranslations(storage) { +export function setupTranslations() { + // Setup the translation framework with the storage. + const storage = createStorage(); + const locale = getLocale(storage); setLocale(storage, locale); @@ -124,3 +125,6 @@ export function t(key, ...replacements) { } export default t; + +// Setup the translations globally as soon as this module runs. +setupTranslations();