Merge branch 'master' of github.com:NuSkooler/enigma-bbs

This commit is contained in:
Bryan Ashby 2023-09-03 11:02:29 -06:00
commit e2bb00b756
23 changed files with 198 additions and 63 deletions

9
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM library/node:lts-bookworm
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update \
&& apt install -y --no-install-recommends sudo telnet \
&& apt autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& echo "node ALL=(ALL) NOPASSWD: ALL" >/etc/sudoers.d/node \
&& chmod 0440 /etc/sudoers.d/node

View File

@ -0,0 +1,24 @@
{
"name": "Basic Node.js",
"build": { "dockerfile": "Dockerfile" },
"remoteUser": "root",
"forwardPorts": [8888, 4000],
"postCreateCommand": "gem install jekyll bundler && /bin/rm -rf node_modules && npm install && cd docs && bundle install && cd ..",
"features": {
"ghcr.io/devcontainers/features/python:1": {
"installTools": true,
"version": "latest"
},
"ghcr.io/devcontainers-contrib/features/curl-apt-get:1": {},
"ghcr.io/jungaretti/features/ripgrep:1": {},
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {},
"ghcr.io/devcontainers/features/ruby:1": {
"version": "3.1"
}
},
"customizations": {
"vscode": {
"extensions": ["ms-azuretools.vscode-docker","alexcvzz.vscode-sqlite","yzhang.markdown-all-in-one", "DavidAnson.vscode-markdownlint", "christian-kohler.npm-intellisense", "dbaeumer.vscode-eslint", "bierner.markdown-yaml-preamble"]
}
}
}

10
.gitattributes vendored
View File

@ -6,3 +6,13 @@
*.TXT eol=crlf *.TXT eol=crlf
*.diz eol=crlf *.diz eol=crlf
*.DIZ eol=crlf *.DIZ eol=crlf
# Don't mess with shell script line endings
*.sh text eol=lf
# Same thing for optutil.js which functions as a shell script
optutil.js text eol=lf
# The devcontainer is also unix
.devcontainer/Dockerfile text eol=lf
.devcontainer/devcontainer.json text eol=lf

1
.gitignore vendored
View File

@ -11,4 +11,3 @@ mail/
node_modules/ node_modules/
docs/_site/ docs/_site/
docs/.sass-cache/ docs/.sass-cache/
.vscode/

6
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"recommendations": [
"ms-vscode-remote.remote-containers",
"laktak.hjson"
]
}

17
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/main.js"
}
]
}

26
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start Jekyll (ENiGMA½ documentation server)",
"command": "cd docs && bundle exec jekyll serve",
"isBackground": true,
"type": "shell"
},
{
"label": "(re)build Jekyll bundles",
"command": "cd docs && bundle install",
"type": "shell"
},
{
"label": "(re)build node modules",
"command": "/bin/rm -rf node_modules && npm install",
"type": "shell"
},
{
"label": "ENiGMA½ new configuration",
"command": "./oputil.js config new",
"type": "shell"
}
]
}

View File

@ -9,6 +9,7 @@ This document attempts to track **major** changes and additions in ENiGMA½. For
* Finally, the system will search for `index.html` and `index.htm` in that order, if another suitable route cannot be established. * Finally, the system will search for `index.html` and `index.htm` in that order, if another suitable route cannot be established.
* CombatNet has shut down, so the module (`combatnet.js`) has been removed. * CombatNet has shut down, so the module (`combatnet.js`) has been removed.
* The Menu Flag `popParent` has been removed and `noHistory` has been updated to work as expected. In general things should "Just Work", but check your `menu.hjson` entries if you see menu stack issues. * The Menu Flag `popParent` has been removed and `noHistory` has been updated to work as expected. In general things should "Just Work", but check your `menu.hjson` entries if you see menu stack issues.
* Various New User Application (NUA) properties are now optional. If you would like to reduce the information users are required, remove optional fields from NUA artwork and collect less. These properties will be stored as "" (empty). Optional properties are as follows: Real name, Birth date, Sex, Location, Affiliations (Affils), Email, and Web address.
* Art handling has been changed to respect the art width contained in SAUCE when present in the case where the terminal width is greater than the art width. This fixes art files that assume wrapping at 80 columns on wide (mostly new utf8) terminals. * Art handling has been changed to respect the art width contained in SAUCE when present in the case where the terminal width is greater than the art width. This fixes art files that assume wrapping at 80 columns on wide (mostly new utf8) terminals.
## 0.0.13-beta ## 0.0.13-beta

View File

@ -505,9 +505,9 @@ class Achievements {
getFormatObject(info) { getFormatObject(info) {
return { return {
userName: info.user.username, userName: info.user.username,
userRealName: info.user.properties[UserProps.RealName], userRealName: info.user.realName(false) || 'N/A',
userLocation: info.user.properties[UserProps.Location], userLocation: info.user.properties[UserProps.Location] || 'N/A',
userAffils: info.user.properties[UserProps.Affiliations], userAffils: info.user.properties[UserProps.Affiliations] || 'N/A',
nodeId: info.client.node, nodeId: info.client.node,
title: info.details.title, title: info.details.title,
//text : info.global ? info.details.globalText : info.details.text, //text : info.global ? info.details.globalText : info.details.text,

View File

@ -24,7 +24,7 @@ function ANSIEscapeParser(options) {
this.graphicRendition = {}; this.graphicRendition = {};
this.parseState = { this.parseState = {
re: /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex re: /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsutEFGST])/g, // eslint-disable-line no-control-regex
}; };
options = miscUtil.valueWithDefault(options, { options = miscUtil.valueWithDefault(options, {
@ -231,7 +231,7 @@ function ANSIEscapeParser(options) {
self.parseState = { self.parseState = {
// ignore anything past EOF marker, if any // ignore anything past EOF marker, if any
buffer: input.split(String.fromCharCode(0x1a), 1)[0], buffer: input.split(String.fromCharCode(0x1a), 1)[0],
re: /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex re: /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsutEFGST])/g, // eslint-disable-line no-control-regex
stop: false, stop: false,
}; };
}; };

View File

@ -87,7 +87,7 @@ function getActiveConnectionList(
// //
entry.text = ac.user?.username || 'N/A'; entry.text = ac.user?.username || 'N/A';
entry.userName = ac.user?.username || 'N/A'; entry.userName = ac.user?.username || 'N/A';
entry.realName = ac.user?.getProperty(UserProps.RealName) || 'N/A'; entry.realName = ac.user?.realName(false) || 'N/A';
entry.location = ac.user?.getProperty(UserProps.Location) || 'N/A'; entry.location = ac.user?.getProperty(UserProps.Location) || 'N/A';
entry.affils = entry.affiliation = entry.affils = entry.affiliation =
ac.user?.getProperty(UserProps.Affiliations) || 'N/A'; ac.user?.getProperty(UserProps.Affiliations) || 'N/A';

View File

@ -982,11 +982,7 @@ exports.FullScreenEditorModule =
const area = getMessageAreaByTag(self.messageAreaTag); const area = getMessageAreaByTag(self.messageAreaTag);
if (fromView !== undefined) { if (fromView !== undefined) {
if (area && area.realNames) { if (area && area.realNames) {
fromView.setText( fromView.setText(self.client.user.realName());
self.client.user.properties[
UserProps.RealName
] || self.client.user.username
);
} else { } else {
fromView.setText(self.client.user.username); fromView.setText(self.client.user.username);
} }

View File

@ -21,10 +21,7 @@ exports.getModule = class MyMessagesModule extends MenuModule {
initSequence() { initSequence() {
const filter = { const filter = {
toUserName: [ toUserName: [this.client.user.username, this.client.user.realName()],
this.client.user.username,
this.client.user.getProperty(UserProps.RealName),
],
sort: 'modTimestamp', sort: 'modTimestamp',
resultType: 'messageList', resultType: 'messageList',
limit: 1024 * 16, // we want some sort of limit... limit: 1024 * 16, // we want some sort of limit...

View File

@ -13,6 +13,7 @@ const UserProps = require('./user_property.js');
// deps // deps
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment');
exports.moduleInfo = { exports.moduleInfo = {
name: 'NUA', name: 'NUA',
@ -95,15 +96,15 @@ exports.getModule = class NewUserAppModule extends MenuModule {
areaTag = areaTag || ''; areaTag = areaTag || '';
newUser.properties = { newUser.properties = {
[UserProps.RealName]: formData.value.realName, [UserProps.RealName]: formData.value.realName || '',
[UserProps.Birthdate]: getISOTimestampString( [UserProps.Birthdate]: getISOTimestampString(
formData.value.birthdate formData.value.birthdate || moment()
), ),
[UserProps.Sex]: formData.value.sex, [UserProps.Sex]: formData.value.sex || '',
[UserProps.Location]: formData.value.location, [UserProps.Location]: formData.value.location || '',
[UserProps.Affiliations]: formData.value.affils, [UserProps.Affiliations]: formData.value.affils || '',
[UserProps.EmailAddress]: formData.value.email, [UserProps.EmailAddress]: formData.value.email || '',
[UserProps.WebAddress]: formData.value.web, [UserProps.WebAddress]: formData.value.web || '',
[UserProps.AccountCreated]: getISOTimestampString(), [UserProps.AccountCreated]: getISOTimestampString(),
[UserProps.MessageConfTag]: confTag, [UserProps.MessageConfTag]: confTag,

View File

@ -124,12 +124,29 @@ module.exports = class User {
return isMember; return isMember;
} }
realName(withUsernameFallback = true) {
const realName = this.getProperty(UserProps.RealName);
if (realName) {
return realName;
}
if (withUsernameFallback) {
return this.username;
}
}
getSanitizedName(type = 'username') { getSanitizedName(type = 'username') {
const name = const name = 'real' === type ? this.realName(true) : this.username;
'real' === type ? this.getProperty(UserProps.RealName) : this.username;
return sanatizeFilename(name) || `user${this.userId.toString()}`; return sanatizeFilename(name) || `user${this.userId.toString()}`;
} }
emailAddress() {
const email = this.getProperty(UserProps.EmailAddress);
if (email) {
const realName = this.realName(false);
return realName ? `${realName} <${email}>` : email;
}
}
isAvailable() { isAvailable() {
return (this.statusFlags & User.StatusFlags.NotAvailable) == 0; return (this.statusFlags & User.StatusFlags.NotAvailable) == 0;
} }

View File

@ -93,9 +93,7 @@ module.exports = class User2FA_OTPWebRegister {
} }
const message = { const message = {
to: `${ to: user.emailAddress(),
user.getProperty(UserProps.RealName) || user.username
} <${user.getProperty(UserProps.EmailAddress)}>`,
// from will be filled in // from will be filled in
subject: '2-Factor Authentication Registration', subject: '2-Factor Authentication Registration',
text: textTemplate, text: textTemplate,

View File

@ -115,15 +115,15 @@ exports.getModule = class UserConfigModule extends MenuModule {
formData = _.clone(formData); formData = _.clone(formData);
const newProperties = { const newProperties = {
[UserProps.RealName]: formData.value.realName, [UserProps.RealName]: formData.value.realName || '',
[UserProps.Birthdate]: getISOTimestampString( [UserProps.Birthdate]: getISOTimestampString(
formData.value.birthdate formData.value.birthdate || moment()
), ),
[UserProps.Sex]: formData.value.sex, [UserProps.Sex]: formData.value.sex || '',
[UserProps.Location]: formData.value.location, [UserProps.Location]: formData.value.location || '',
[UserProps.Affiliations]: formData.value.affils, [UserProps.Affiliations]: formData.value.affils || '',
[UserProps.EmailAddress]: formData.value.email, [UserProps.EmailAddress]: formData.value.email || '',
[UserProps.WebAddress]: formData.value.web, [UserProps.WebAddress]: formData.value.web || '',
[UserProps.TermHeight]: formData.value.termHeight.toString(), [UserProps.TermHeight]: formData.value.termHeight.toString(),
[UserProps.ThemeId]: [UserProps.ThemeId]:
self.availThemeInfo[formData.value.theme].themeId, self.availThemeInfo[formData.value.theme].themeId,
@ -233,11 +233,7 @@ exports.getModule = class UserConfigModule extends MenuModule {
function populateViews(callback) { function populateViews(callback) {
const user = self.client.user; const user = self.client.user;
self.setViewText( self.setViewText('menu', MciCodeIds.RealName, user.realName(false) || '');
'menu',
MciCodeIds.RealName,
user.properties[UserProps.RealName]
);
self.setViewText( self.setViewText(
'menu', 'menu',
MciCodeIds.BirthDate, MciCodeIds.BirthDate,

View File

@ -143,9 +143,7 @@ class WebPasswordReset {
} }
const message = { const message = {
to: `${user.properties[UserProps.RealName] || user.username} <${ to: user.emailAddress(),
user.properties[UserProps.EmailAddress]
}>`,
// from will be filled in // from will be filled in
subject: 'Forgot Password', subject: 'Forgot Password',
text: textTemplate, text: textTemplate,

View File

@ -506,9 +506,7 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
// Current // Current
currentUserName: this.client.user.username, currentUserName: this.client.user.username,
currentUserRealName: currentUserRealName: this.client.user.realName(false) || 'N/A',
this.client.user.getProperty(UserProps.RealName) ||
this.client.user.username,
availIndicator: availIndicator, availIndicator: availIndicator,
visIndicator: visIndicator, visIndicator: visIndicator,
lastLoginUserName: lastLoginStats.userName, lastLoginUserName: lastLoginStats.userName,

View File

@ -30,3 +30,4 @@ end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem "webrick"

View File

@ -6,8 +6,8 @@ GEM
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
tzinfo (~> 2.0) tzinfo (~> 2.0)
addressable (2.8.0) addressable (2.8.5)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0) colorator (1.1.0)
concurrent-ruby (1.2.2) concurrent-ruby (1.2.2)
cssminify2 (2.0.1) cssminify2 (2.0.1)
@ -19,14 +19,14 @@ GEM
ffi (1.15.5) ffi (1.15.5)
forwardable-extended (2.6.0) forwardable-extended (2.6.0)
gemoji (3.0.1) gemoji (3.0.1)
html-pipeline (2.14.0) html-pipeline (2.14.3)
activesupport (>= 2) activesupport (>= 2)
nokogiri (>= 1.4) nokogiri (>= 1.4)
htmlcompressor (0.4.0) htmlcompressor (0.4.0)
http_parser.rb (0.8.0) http_parser.rb (0.8.0)
i18n (1.14.1) i18n (1.14.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jekyll (4.2.1) jekyll (4.2.2)
addressable (~> 2.4) addressable (~> 2.4)
colorator (~> 1.0) colorator (~> 1.0)
em-websocket (~> 0.5) em-websocket (~> 0.5)
@ -49,7 +49,7 @@ GEM
uglifier (~> 4.1) uglifier (~> 4.1)
jekyll-relative-links (0.6.1) jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0) jekyll (>= 3.3, < 5.0)
jekyll-sass-converter (2.1.0) jekyll-sass-converter (2.2.0)
sassc (> 2.0.1, < 3.0) sassc (> 2.0.1, < 3.0)
jekyll-seo-tag (2.7.1) jekyll-seo-tag (2.7.1)
jekyll (>= 3.8, < 5.0) jekyll (>= 3.8, < 5.0)
@ -64,30 +64,30 @@ GEM
gemoji (~> 3.0) gemoji (~> 3.0)
html-pipeline (~> 2.2) html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0) jekyll (>= 3.0, < 5.0)
json (2.6.1) json (2.6.3)
json-minify (0.0.3) json-minify (0.0.3)
json (> 0) json (> 0)
kramdown (2.3.1) kramdown (2.4.0)
rexml rexml
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
liquid (4.0.3) liquid (4.0.4)
listen (3.7.1) listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0) mercenary (0.4.0)
minitest (5.19.0) minitest (5.19.0)
nokogiri (1.14.3-x86_64-linux) nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (4.0.6) public_suffix (5.0.3)
racc (1.6.2) racc (1.7.1)
rb-fsevent (0.11.0) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
rexml (3.2.5) rexml (3.2.6)
rouge (3.28.0) rouge (3.30.0)
safe_yaml (1.0.5) safe_yaml (1.0.5)
sassc (2.4.0) sassc (2.4.0)
ffi (~> 1.9) ffi (~> 1.9)
@ -98,6 +98,7 @@ GEM
uglifier (4.2.0) uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
webrick (1.8.1)
PLATFORMS PLATFORMS
x86_64-linux x86_64-linux
@ -111,6 +112,7 @@ DEPENDENCIES
jekyll-theme-hacker (~> 0.2.0) jekyll-theme-hacker (~> 0.2.0)
jemoji (~> 0.12.0) jemoji (~> 0.12.0)
tzinfo-data tzinfo-data
webrick
BUNDLED WITH BUNDLED WITH
2.3.5 2.4.19

View File

@ -54,6 +54,7 @@ collections:
- installation/network.md - installation/network.md
- installation/testing.md - installation/testing.md
- installation/production.md - installation/production.md
- installation/development.md
- configuration/creating-config.md - configuration/creating-config.md
- configuration/sysop-setup.md - configuration/sysop-setup.md
- configuration/config-files.md - configuration/config-files.md

View File

@ -0,0 +1,38 @@
---
layout: page
title: Development Environment Setup
---
_Note:_ This is only useful for people who are looking to contribute to the ENiGMA½ source base itself. Those that are just setting up a new BBS system do not need this section.
The easiest way to get started with development on ENiGMA½ is via the pre-configured Visual Studio Code remote docker container environment. This setup will download and configure everything needed with minimal interaction. It also works cross-platform.
* Install [Visual Studio Code](https://code.visualstudio.com/download)
* Install [Docker](https://docs.docker.com/engine/install/)
* Clone the [ENiGMA½](https://github.com/NuSkooler/enigma-bbs) repository.
* Choose "Open Folder" from Visual Studio Code and open the location where you cloned the repository.
That's it! Visual Studio Code should prompt you for everything else that is needed, including some useful extensions for development.
## Tasks
Once it completes, there are a few tasks and run-configs that are useful. Open up the command pallete and search/choose "Tasks> Run Task". From there you can run the following tasks:
### Start Jekyll (ENiGMA½ documentation server)
This task will start the Jekyll server to perform local testing of changes to documentation. After running this task, open a browser to (http://localhost:4000/enigma-bbs/) to see the documentation.
### (re)build Jekyll bundles
When the image is created the Jekyll bundles are installed, so in general there shouldn't be much need to run this task. This is available however in case soemthing goes wrong or you are working on the Jekyll setup itself.
### (re)build node modules
Used to re-generate the node modules. Generally shouldn't be necessary unless something is broken or you are adding/changing versions of dependencies.
### ENiGMA½ new configuration
This task executes `oputil.js` in order to create a new BBS configuration (useful if you have just checked out the code and haven't setup any configuration yet.)
## Run / Debug config
There is also a default "Launch Program" config (hotkey access via F5 / Ctrl-Shift-D.) This will launch ENiGMA½. Once it has launched, access the system via telnet, port 8888 as usual.