diff --git a/README.md b/README.md index 2c7d2a9369..055b6dfa8e 100644 --- a/README.md +++ b/README.md @@ -21,16 +21,16 @@ NodeBB's base theme utilizes [Bootstrap 3](http://getbootstrap.com/) but themes ## Installation -[Please refer to platform-specific installation documentation](https://docs.nodebb.org/installing/os) +Follow the installation instructions in the [Project 1 Writeup](https://cmu-313.github.io/projects/P1/) on the course website. -For feature development, we highly recommend you install and use the suggested [grunt-cli](https://docs.nodebb.org/configuring/running/#grunt-development) to enable file-watching and live refresh. +For feature development, we highly recommend you install and use the suggested [grunt-cli](https://docs.nodebb.org/configuring/running/#grunt-development) to enable file-watching and live refresh. We also highly recommend you use [VSCode](https://code.visualstudio.com/download) or similar for the development of this project. When running in a development environment, you can find the API specs for NodeBB at [http://localhost:4567/debug/spec/read](http://localhost:4567/debug/spec/read) and [http://localhost:4567/debug/spec/write](http://localhost:4567/debug/spec/write). ## TypeScript -This codebase is in the process of being translated to[TypeScript](https://www.typescriptlang.org/)! During this intermediate stage, translated files will contain both a `.ts` and `.js` file in the repository. Translated files should be edited **only in the `.ts` file**; corresponding `.js` files will be automatically compiled and generated by the `% npx tsc` command. +This codebase is in the process of being translated to [TypeScript](https://www.typescriptlang.org/)! During this intermediate stage, translated files will contain both a `.ts` and `.js` file in the repository. Translated files should be edited **only in the `.ts` file**; corresponding `.js` files will be automatically compiled and generated by the `% npx tsc` command. If using VSCode, you can remove duplicate files from your Explorer view by adding the following to your `.vscode/settings.json` file: ``` @@ -42,14 +42,17 @@ If using VSCode, you can remove duplicate files from your Explorer view by addin } ``` +VSCode can provide a lot of other assistance too, such as recommending package installs and providing typing information on hover. + ## Development Tools + This repository comes with tools for linting (ESLint), testing (Mocha), and coverage reporting (nyc). All of these tools can be run locally: ``` % npm run lint // Runs the linter % npm run test // Runs test suite + generates coverage report ``` -The first time you run the test command, it will ask you to provide a configuration for a test database. Depending on your local database setup (likely Redis), follow the instructions to add a test database configuration to `config.json`, then re-run the command. +The first time you run the test command, it may fail and ask you to provide a configuration for a test database. Scroll up past the errors and, depending on your local database setup, follow the provided instructions to add a test database configuration to `config.json` and re-run the testing command. After running the test suite, you can find the coverage report generated in the `coverage` directory. This can be viewed in the browser by opening the `index.html` file in this directory. @@ -62,7 +65,7 @@ NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://w ## Helpful Links * [NodeBB Demo](https://try.nodebb.org) -* [Documentation & Installation Instructions](http://docs.nodebb.org) +* [NodeBB Documentation](http://docs.nodebb.org) * **Git & Github:** * [Git Documentation](https://git-scm.com/docs/gittutorial) * [Git Flow](https://datasift.github.io/gitflow/IntroducingGitFlow.html) diff --git a/install/package.json b/install/package.json index 25ab00d40b..9284c1cbe4 100644 --- a/install/package.json +++ b/install/package.json @@ -32,10 +32,6 @@ "@isaacs/ttlcache": "1.2.1", "@nodebb/bootswatch": "3.4.2", "@socket.io/redis-adapter": "8.0.0", - "@types/async": "^3.2.16", - "@types/lodash": "^4.14.191", - "@types/nconf": "^0.10.3", - "@types/semver": "^7.3.13", "ace-builds": "1.14.0", "archiver": "5.3.1", "async": "3.2.4", @@ -152,7 +148,11 @@ "@apidevtools/swagger-parser": "10.0.3", "@commitlint/cli": "17.3.0", "@commitlint/config-angular": "17.3.0", + "@types/async": "^3.2.16", "@types/express": "^4.17.15", + "@types/lodash": "^4.14.191", + "@types/nconf": "^0.10.3", + "@types/semver": "^7.3.13", "@typescript-eslint/eslint-plugin": "^5.48.0", "@typescript-eslint/parser": "^5.48.0", "coveralls": "3.1.1", diff --git a/src/controllers/composer.js b/src/controllers/composer.js index 1eca9e1667..ab7f52b79f 100644 --- a/src/controllers/composer.js +++ b/src/controllers/composer.js @@ -1,4 +1,6 @@ "use strict"; +// This is one of the two example TypeScript files included with the NodeBB repository +// It is meant to serve as an example to assist you with your HW1 translation var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -58,8 +60,7 @@ function post(req, res) { }; req.body.noscript = 'true'; if (!data.content) { - yield helpers_1.default.noScriptErrors(req, res, '[[error:invalid-data]]', 400); - return; + return yield helpers_1.default.noScriptErrors(req, res, '[[error:invalid-data]]', 400); } function queueOrPost(postFn, data) { return __awaiter(this, void 0, void 0, function* () { diff --git a/src/controllers/composer.ts b/src/controllers/composer.ts index 2000867109..7df1c0a7dd 100644 --- a/src/controllers/composer.ts +++ b/src/controllers/composer.ts @@ -1,3 +1,6 @@ +// This is one of the two example TypeScript files included with the NodeBB repository +// It is meant to serve as an example to assist you with your HW1 translation + import nconf from 'nconf'; import { Request, Response, NextFunction } from 'express'; @@ -88,9 +91,9 @@ export async function post(req: Request & { uid: n req.body.noscript = 'true'; if (!data.content) { - await helpers.noScriptErrors(req, res, '[[error:invalid-data]]', 400); - return; + return await helpers.noScriptErrors(req, res, '[[error:invalid-data]]', 400) as Promise; } + async function queueOrPost(postFn: PostFnType, data: ComposerData): Promise { // The next line calls a function in a module that has not been updated to TS yet // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call diff --git a/src/controllers/composer_original.js b/src/controllers/composer_original.js new file mode 100644 index 0000000000..1e835a39da --- /dev/null +++ b/src/controllers/composer_original.js @@ -0,0 +1,95 @@ +// This is one of the two example files included with the NodeBB repository +// It is the original (untranslated) JavaScript file of composer.ts +// This file is meant to serve as an example to assist you with your +// HW1 translation. It is *not* meant to be run. +// You do not have to keep your original JavaScript file for this assignment + +'use strict'; + +const nconf = require('nconf'); + +const user = require('../user'); +const plugins = require('../plugins'); +const topics = require('../topics'); +const posts = require('../posts'); +const helpers = require('./helpers'); + +exports.get = async function (req, res, callback) { + res.locals.metaTags = { + ...res.locals.metaTags, + name: 'robots', + content: 'noindex', + }; + + const data = await plugins.hooks.fire('filter:composer.build', { + req: req, + res: res, + next: callback, + templateData: {}, + }); + + if (res.headersSent) { + return; + } + if (!data || !data.templateData) { + return callback(new Error('[[error:invalid-data]]')); + } + + if (data.templateData.disabled) { + res.render('', { + title: '[[modules:composer.compose]]', + }); + } else { + data.templateData.title = '[[modules:composer.compose]]'; + res.render('compose', data.templateData); + } +}; + +exports.post = async function (req, res) { + const { body } = req; + const data = { + uid: req.uid, + req: req, + timestamp: Date.now(), + content: body.content, + fromQueue: false, + }; + req.body.noscript = 'true'; + + if (!data.content) { + return helpers.noScriptErrors(req, res, '[[error:invalid-data]]', 400); + } + async function queueOrPost(postFn, data) { + const shouldQueue = await posts.shouldQueue(req.uid, data); + if (shouldQueue) { + delete data.req; + return await posts.addToQueue(data); + } + return await postFn(data); + } + + try { + let result; + if (body.tid) { + data.tid = body.tid; + result = await queueOrPost(topics.reply, data); + } else if (body.cid) { + data.cid = body.cid; + data.title = body.title; + data.tags = []; + data.thumb = ''; + result = await queueOrPost(topics.post, data); + } else { + throw new Error('[[error:invalid-data]]'); + } + if (result.queued) { + return res.redirect(`${nconf.get('relative_path') || '/'}?noScriptMessage=[[success:post-queued]]`); + } + const uid = result.uid ? result.uid : result.topicData.uid; + user.updateOnlineUsers(uid); + const path = result.pid ? `/post/${result.pid}` : `/topic/${result.topicData.slug}`; + res.redirect(nconf.get('relative_path') + path); + } catch (err) { + helpers.noScriptErrors(req, res, err.message, 400); + } +}; diff --git a/src/social.js b/src/social.js index 1346bdbbd7..e1caaeb665 100644 --- a/src/social.js +++ b/src/social.js @@ -1,4 +1,6 @@ "use strict"; +// This is one of the two example TypeScript files included with the NodeBB repository +// It is meant to serve as an example to assist you with your HW1 translation var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { diff --git a/src/social.ts b/src/social.ts index 3430f72d0a..f494add3ea 100644 --- a/src/social.ts +++ b/src/social.ts @@ -1,3 +1,6 @@ +// This is one of the two example TypeScript files included with the NodeBB repository +// It is meant to serve as an example to assist you with your HW1 translation + import _ from 'lodash'; import plugins from './plugins'; import db from './database'; @@ -27,6 +30,7 @@ export async function getPostSharing(): Promise { ]; networks = await plugins.hooks.fire('filter:social.posts', networks) as Network[]; + // The next line calls a function in a module that has not been updated to TS yet // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call const activated: string[] = await db.getSetMembers('social:posts.activated') as string[]; diff --git a/src/social_original.js b/src/social_original.js new file mode 100644 index 0000000000..bb03862a44 --- /dev/null +++ b/src/social_original.js @@ -0,0 +1,58 @@ +// This is one of the two example files included with the NodeBB repository +// It is the original (untranslated) JavaScript file of social.ts +// This file is meant to serve as an example to assist you with your +// HW1 translation. It is *not* meant to be run. +// You do not have to keep your original JavaScript file for this assignment + +'use strict'; + +const _ = require('lodash'); +const plugins = require('./plugins'); +const db = require('./database'); + +const social = module.exports; + +social.postSharing = null; + +social.getPostSharing = async function () { + if (social.postSharing) { + return _.cloneDeep(social.postSharing); + } + + let networks = [ + { + id: 'facebook', + name: 'Facebook', + class: 'fa-facebook', + }, + { + id: 'twitter', + name: 'Twitter', + class: 'fa-twitter', + }, + ]; + networks = await plugins.hooks.fire('filter:social.posts', networks); + const activated = await db.getSetMembers('social:posts.activated'); + networks.forEach((network) => { + network.activated = activated.includes(network.id); + }); + + social.postSharing = networks; + return _.cloneDeep(networks); +}; + +social.getActivePostSharing = async function () { + const networks = await social.getPostSharing(); + return networks.filter(network => network && network.activated); +}; + +social.setActivePostSharingNetworks = async function (networkIDs) { + social.postSharing = null; + await db.delete('social:posts.activated'); + if (!networkIDs.length) { + return; + } + await db.setAdd('social:posts.activated', networkIDs); +}; + +require('./promisify')(social);