Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Installed and ran Flow (static analysis tool) #96

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3d490ca
add deadline
github-classroom[bot] Sep 9, 2024
db666b2
Added refactored code from p1
ericlin2 Sep 21, 2024
159603d
copied newest commit
ericlin2 Sep 21, 2024
56862d5
Merge pull request #21 from CMU-313/eric-p1-refactor
ericlin2 Sep 22, 2024
be163af
Added one emoji reaction
VeronicaPim Sep 22, 2024
46878e2
fixed previous commit
VeronicaPim Sep 23, 2024
f304f9a
fixing failures
VeronicaPim Sep 24, 2024
2e58e66
Merge pull request #25 from VeronicaPim/emoji-reactions-feature
VeronicaPim Sep 24, 2024
29c75ea
committing SonarCloud Fix refactoring code from P1
eunseokk Sep 24, 2024
ece37d7
trying to update emoji access
VeronicaPim Sep 25, 2024
364d5bb
Merge pull request #31 from CMU-313/emoji-reactions-feature
VeronicaPim Oct 11, 2024
0cf5265
UserGuide.md
VeronicaPim Oct 11, 2024
0bd5ca8
Adding UserGuide in md format
VeronicaPim Oct 11, 2024
dd6d3df
Add or update the Azure App Service build and deployment workflow config
VeronicaPim Oct 22, 2024
1a5d5b3
Delete .github/workflows/f24_team-bluesleep-db.yml
VeronicaPim Oct 22, 2024
612713c
Update azure-deploy-f24.yml
VeronicaPim Oct 22, 2024
4931dce
Update azure-deploy-f24.yml
VeronicaPim Oct 22, 2024
e5d4cba
Update azure-deploy-f24.yml
VeronicaPim Oct 22, 2024
88e2466
Update README.md
VeronicaPim Oct 22, 2024
6bba572
Update README.md
VeronicaPim Oct 22, 2024
53d803b
Rename UserGuide to UserGuide.md
VeronicaPim Oct 22, 2024
c70ed98
Update UserGuide.md
VeronicaPim Oct 22, 2024
544b7bb
updated npm
ericlin2 Oct 23, 2024
fb89824
Revert "updated npm"
ericlin2 Oct 23, 2024
1694e62
remove node modules from repo
ericlin2 Oct 23, 2024
0ccb7a5
installed and ran Flow (static analysis tool)
ericlin2 Oct 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-flow"],
"plugins": ["babel-plugin-syntax-hermes-parser"],
}
14 changes: 14 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[ignore]

[include]

[libs]

[lints]
untyped-type-import=error
internal-type=error
deprecated-type-bool=error

[options]

[strict]
14 changes: 8 additions & 6 deletions .github/workflows/azure-deploy-f24.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
./.github/workflows/test.yaml

build-and-deploy:
if: github.repository == 'cmu-313/NodeBB'
if: github.repository == 'cmu-313/nodebb-f24-bluesleep'
needs: lint-and-test

runs-on: ubuntu-latest
Expand All @@ -30,25 +30,27 @@ jobs:
- name: Set up Node.js version
uses: actions/setup-node@v3
with:
node-version: '20.x'
node-version: '20.17.0'

- name: Set up NodeBB
run: |
./nodebb setup '{"url":"https://nodebb-f24.azurewebsites.net:443",
./nodebb setup '{
"url": "https://team-bluesleep-db-g9gncbexhhhjd7em.canadacentral-01.azurewebsites.net:443",
"admin:username": "admin",
"admin:password": "${{ secrets.ADMIN_PASSWORD }}",
"admin:password:confirm": "${{ secrets.ADMIN_PASSWORD }}",
"admin:email": "[email protected]",
"database": "redis",
"redis:host": "${{ secrets.REDIS_HOST }}",
"redis:port": "6379",
"redis:password": "${{ secrets.REDIS_PASSWORD }}" }'
"redis:password": "${{ secrets.REDIS_PASSWORD }}"
}'

- name: 'Deploy to Azure Web App'
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: 'nodebb-f24'
app-name: 'team-bluesleep-db'
slot-name: 'Production'
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_BFAB97B1AB1441ACA7C63280F91AD3F3 }}
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_8DA38339FCB64169A6856C5D3423DAA8 }}
package: .
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ link-plugins.sh
test.sh

.docker/**
!**/.gitkeep
!**/.gitkeep
node_modules
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/ithVU1OO)
# ![NodeBB](public/images/sm-card.png)

[![Workflow](https://github.com/CMU-313/NodeBB/actions/workflows/test.yaml/badge.svg)](https://github.com/CMU-313/NodeBB/actions/workflows/test.yaml)
Expand Down
155 changes: 155 additions & 0 deletions UserGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# User Guide
**Authors**: Angela Kim, Veronica Pimenova, Crystal Cheng, Eric Lin

This is the user documentation for our project. It explains the features, setup instructions, and usage details.

## Table of Contents
- [Feature Descriptions](#feature-descriptions)
- [Feature 1: Anonymous Posting](#feature-1-anonymous-posting)
- [Feature 2: isAnswered](#feature-2-isanswered)
- [Feature 3: Emoji Reactions](#feature-3-emoji-reactions)
- [Implementation Details](#implementation-details)
- [Feature 1: Anonymous Posting](#implementation-details-feature-1-anonymous-posting)
- [Feature 2: isAnswered](#implementation-details-feature-2-isanswered)
- [Feature 3: Emoji Reactions](#implementation-details-feature-3-emoji-reactions)
- [Testing](#testing)
- [Feature 1: Anonymous Posting](#testing-feature-1-anonymous-posting)
- [Feature 2: isAnswered](#testing-feature-2-isanswered)
- [Feature 3: Emoji Reactions](#testing-feature-3-emoji-reactions)
- [Usage](#usage)
- [Feature 1: Anonymous Posting](#usage-feature-1-anonymous-posting)
- [Feature 2: isAnswered](#usage-feature-2-isanswered)
- [Feature 3: Emoji Reactions](#usage-feature-3-emoji-reactions)
- [Automated Tests](#automated-tests)

---

## Feature Descriptions

### Feature 1: Anonymous Posting
The Anonymous Posting feature allows users to make a post without having their name/profile attached to the post publicly. This feature provides more flexibility for users, giving them the option to post anonymously.

**Key Functionality**:
- **Mark post as anonymous**: When drafting a post, users will see a checkbox that allows them to modify the anonymous status of the post.
- **Post displays as anonymous**: Upon posting, the post shows up without a profile picture or name associated with the post author.

### Feature 2: isAnswered
The `isAnswered` feature helps track the status of topics in the forum, allowing users to identify which topics have received a satisfactory response. It is useful for Q&A or support forums where users focus on unanswered queries.

**Key Functionality**:
- **Mark Topics as Answered**: Users with the necessary privileges can mark a topic as answered.
- **Display Answer Status**: The status (`Answered` or `Unanswered`) is displayed on the topic list view.
- **Restrict Marking Privileges**: The ability to toggle the `isAnswered` status can be restricted to specific users.

### Feature 3: Emoji Reactions
The Emoji Reactions feature lets users express their sentiments on posts by reacting with emojis. This provides a lightweight and intuitive way for users to engage with posts.

**Key Functionality**:
- **React to Posts**: Users can choose from a set of emojis to react to a post.
- **View Reactions**: The total number of reactions and specific emojis used are displayed under each post.
- **Remove Reactions**: Users can remove their reactions if they no longer wish to associate them with a post.

---

## Implementation Details

### Implementation Details: Feature 1 (Anonymous Posting)
- **Backend Field Handling**: A field `isAnonymous` is added to handle the server requests associated with a post’s anonymity.
- Relevant Files: `src/posts/summary.js`, `src/topics/create.js`
- **Backend Schema**: The schema was expanded to include the `isAnonymous` field to match the response body of operations.
- **Frontend UI**: A checkbox shows up next to the topic title for users to handle the anonymous state of each post.
- **Integration**: When the checkbox is checked and a post is published, a frontend trigger reflects this change.

### Implementation Details: Feature 2 (isAnswered)
- **Backend Schema**: A new column `isAnswered` was added to the `topics` table in the database to store the status of each topic.
- **API Endpoint (Internal)**:
- Endpoint: `/post/:pid/answered`
- Method: `PUT`
- Body: `{ "isAnswered": true }`
- **Frontend UI**:
- Checkbox Display: A checkbox is added next to each post. It is checked if the post is answered and unchecked if unanswered.
- UI Placement: The checkbox is visible both in the main topic list and within the post detail view.

### Implementation Details: Feature 3 (Emoji Reactions)
- **Backend Schema**: No new column was added. Emoji reactions leverage NodeBB’s existing reaction system.
- **API Endpoints**:
- **Add Reaction**:
- Endpoint: `/post/:pid/reaction`
- Method: `PUT`
- Body: `{ "reaction": "😊" }`
- **Remove Reaction**:
- Endpoint: `/post/:pid/reaction`
- Method: `DELETE`
- Body: `{ "reaction": "😊" }`
- **Frontend UI**:
- Emoji Picker: Users see an emoji reaction icon next to the post content. Clicking it opens the emoji picker.
- Displaying Reactions: Selected emojis and counts are displayed below the post content.

---

## Testing

### Testing: Feature 1 (Anonymous Posting)
- **Front-End Testing**:
- Verify that the anonymous checkbox is visible in the post creation UI.
- Ensure the checkbox hides the user’s name and profile picture when posting.
- **Back-End Testing**:
- Test that the `isAnonymous` field updates correctly in the database.
- Verify the anonymous status persists across sessions.

### Testing: Feature 2 (isAnswered)
- **Front-End Testing**:
- Ensure the checkbox toggles between "answered" and "unanswered" states.
- Verify the status is reflected correctly in both topic list and detail views.
- **Back-End Testing**:
- Test the API to confirm it updates the `isAnswered` status correctly.
- Verify that the database saves and retrieves the correct status.

### Testing: Feature 3 (Emoji Reactions)
- **Front-End Testing**:
- Ensure the emoji picker appears and allows users to react to posts.
- Test that the reactions display correctly under the post and can be removed.
- **Back-End Testing**:
- Test the API to confirm reactions are added and removed correctly in the database.
- Verify that emoji counts update correctly when multiple users react.

---

## Usage

### Usage: Feature 1 (Anonymous Posting)
1. **Setting a post as anonymous**:
- Under a discussion, click "Create Topic".
- Check the "anonymous" checkbox near the post title.
- Submit the post.
2. **Viewing Anonymous Posts**:
- The post will display as any other post but without the author’s name or profile picture.
3. **Permissions**:
- Only the original poster can toggle the anonymous status.

### Usage: Feature 2 (isAnswered)
1. **Marking a Post as Answered or Unanswered**:
- Navigate to the post, locate the checkbox next to the title, and toggle it.
2. **Viewing Status**:
- In the topic list and post view, the checkbox will indicate the post's current status.
3. **Permissions**:
- Only the original poster or users with permissions can toggle the answer status.

### Usage: Feature 3 (Emoji Reactions)
1. **Adding a Reaction**:
- Navigate to the post and click the emoji icon.
- Select an emoji from the picker, and it will display under the post.
2. **Removing a Reaction**:
- Click on your selected emoji to remove it.
3. **Viewing Reactions**:
- Reactions and counts are visible under the post content.

---

## Automated Tests
- The automated tests for these features can be found in the `/tests/` directory. These tests cover both front-end and back-end functionality for the Anonymous Posting, isAnswered, and Emoji Reactions features.

**Testing Coverage**:
- The tests include basic functionality, user interaction scenarios, and edge cases, ensuring the robustness of the features. We believe the tests are sufficient as they verify key user interactions, API calls, and data persistence across sessions.

---
Binary file added UserGuide.pdf
Binary file not shown.
96 changes: 96 additions & 0 deletions lib/admin/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict';

const fs = require('fs');
const path = require('path');
const sanitizeHTML = require('sanitize-html');
const nconf = require('nconf');
const winston = require('winston');
const file = require('../file');

Check failure on line 8 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Unable to resolve path to module '../file'

Check failure on line 8 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Missing file extension for "../file"
const {
Translator

Check failure on line 10 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 1 tab but found 2 spaces

Check failure on line 10 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Missing trailing comma
} = require('../translator');

Check failure on line 11 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Expected 1 empty line after require statement not followed by another require

Check failure on line 11 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Unable to resolve path to module '../translator'

Check failure on line 11 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Missing file extension for "../translator"
function filterDirectories(directories) {
return directories.map(dir => dir.replace(/^.*(admin.*?).tpl$/, '$1').split(path.sep).join('/')).filter(dir => !dir.endsWith('.js') && !dir.includes('/partials/') && /\/.*\//.test(dir) && !/manage\/(category|group|category-analytics)$/.test(dir));

Check failure on line 13 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 1 tab but found 2 spaces
}
async function getAdminNamespaces() {
const directories = await file.walk(path.resolve(nconf.get('views_dir'), 'admin'));

Check failure on line 16 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 1 tab but found 2 spaces
return filterDirectories(directories);

Check failure on line 17 in lib/admin/search.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 1 tab but found 2 spaces
}
function sanitize(html) {
return sanitizeHTML(html, {
allowedTags: [],
allowedAttributes: []
});
}
function simplify(translations) {
return translations.replace(/(?:\{{1,2}[^}]*?\}{1,2})/g, '').replace(/(?:[ \t]*[\n\r]+[ \t]*)+/g, '\n').replace(/[\t ]+/g, ' ');
}
function nsToTitle(namespace) {
return namespace.replace('admin/', '').split('/').map(str => str[0].toUpperCase() + str.slice(1)).join(' > ').replace(/[^a-zA-Z> ]/g, ' ');
}
const fallbackCache = {};
async function initFallback(namespace) {
const template = await fs.promises.readFile(path.resolve(nconf.get('views_dir'), `${namespace}.tpl`), 'utf8');
const title = nsToTitle(namespace);
let translations = sanitize(template);
translations = Translator.removePatterns(translations);
translations = simplify(translations);
translations += `\n${title}`;
return {
namespace: namespace,
translations: translations,
title: title
};
}
async function fallback(namespace) {
if (fallbackCache[namespace]) {
return fallbackCache[namespace];
}
const params = await initFallback(namespace);
fallbackCache[namespace] = params;
return params;
}
async function initDict(language) {
const namespaces = await getAdminNamespaces();
return await Promise.all(namespaces.map(ns => buildNamespace(language, ns)));
}
async function buildNamespace(language, namespace) {
const translator = Translator.create(language);
try {
const translations = await translator.getTranslation(namespace);
if (!translations || !Object.keys(translations).length) {
return await fallback(namespace);
}
let str = Object.keys(translations).map(key => translations[key]).join('\n');
str = sanitize(str);
let title = namespace;
title = title.match(/admin\/(.+?)\/(.+?)$/);
title = `[[admin/menu:section-${title[1] === 'development' ? 'advanced' : title[1]}]]${title[2] ? ` > [[admin/menu:${title[1]}/${title[2]}]]` : ''}`;
title = await translator.translate(title);
return {
namespace: namespace,
translations: `${str}\n${title}`,
title: title
};
} catch (err) {
winston.error(err.stack);
return {
namespace: namespace,
translations: ''
};
}
}
const cache = {};
async function getDictionary(language) {
if (cache[language]) {
return cache[language];
}
const params = await initDict(language);
cache[language] = params;
return params;
}
module.exports.getDictionary = getDictionary;
module.exports.filterDirectories = filterDirectories;
module.exports.simplify = simplify;
module.exports.sanitize = sanitize;
require('../promisify')(module.exports);
40 changes: 40 additions & 0 deletions lib/admin/versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const request = require('../request');
const meta = require('../meta');
let versionCache = '';
let versionCacheLastModified = '';
const isPrerelease = /^v?\d+\.\d+\.\d+-.+$/;
const latestReleaseUrl = 'https://api.github.com/repos/NodeBB/NodeBB/releases/latest';
async function getLatestVersion() {
const headers = {
Accept: 'application/vnd.github.v3+json',
'User-Agent': encodeURIComponent(`NodeBB Admin Control Panel/${meta.config.title}`)
};
if (versionCacheLastModified) {
headers['If-Modified-Since'] = versionCacheLastModified;
}
const {
body: latestRelease,
response
} = await request.get(latestReleaseUrl, {
headers: headers,
timeout: 2000
});
if (response.statusCode === 304) {
return versionCache;
}
if (response.statusCode !== 200) {
throw new Error(response.statusText);
}
if (!latestRelease || !latestRelease.tag_name) {
throw new Error('[[error:cant-get-latest-release]]');
}
const tagName = latestRelease.tag_name.replace(/^v/, '');
versionCache = tagName;
versionCacheLastModified = response.headers['last-modified'];
return versionCache;
}
exports.getLatestVersion = getLatestVersion;
exports.isPrerelease = isPrerelease;
require('../promisify')(exports);
7 changes: 7 additions & 0 deletions lib/als.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

const {
AsyncLocalStorage
} = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
module.exports = asyncLocalStorage;
Loading