Compare commits
4 Commits
v3.2.1
...
protocol-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
494447741a | ||
|
|
7b174d25cb | ||
|
|
bbb03a08e9 | ||
|
|
b884b0be01 |
@@ -10,21 +10,13 @@ checks:
|
|||||||
threshold: 500
|
threshold: 500
|
||||||
method-lines:
|
method-lines:
|
||||||
config:
|
config:
|
||||||
threshold: 75
|
threshold: 50
|
||||||
method-complexity:
|
method-complexity:
|
||||||
config:
|
config:
|
||||||
threshold: 10
|
threshold: 10
|
||||||
similar-code:
|
similar-code:
|
||||||
config:
|
config:
|
||||||
threshold: 65
|
threshold: 65
|
||||||
plugins:
|
|
||||||
duplication:
|
|
||||||
enabled: true
|
|
||||||
config:
|
|
||||||
languages:
|
|
||||||
javascript:
|
|
||||||
mass_threshold: 110
|
|
||||||
count_threshold: 3
|
|
||||||
exclude_paths:
|
exclude_paths:
|
||||||
- "public/vendor/*"
|
- "public/vendor/*"
|
||||||
- "test/*"
|
- "test/*"
|
||||||
@@ -7,6 +7,7 @@ node_modules/
|
|||||||
logs/
|
logs/
|
||||||
/public/templates
|
/public/templates
|
||||||
/public/uploads
|
/public/uploads
|
||||||
|
/public/sounds
|
||||||
/public/vendor
|
/public/vendor
|
||||||
/public/src/modules/string.js
|
/public/src/modules/string.js
|
||||||
.idea/
|
.idea/
|
||||||
|
|||||||
139
.eslintrc
@@ -1,3 +1,140 @@
|
|||||||
{
|
{
|
||||||
"extends": "nodebb"
|
"extends": "airbnb-base",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "script"
|
||||||
|
},
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
// Customized
|
||||||
|
"handle-callback-err": [ "error","^(e$|(e|(.*(_e|E)))rr)" ],
|
||||||
|
"comma-dangle": ["error", {
|
||||||
|
"arrays": "always-multiline",
|
||||||
|
"objects": "always-multiline",
|
||||||
|
"imports": "always-multiline",
|
||||||
|
"exports": "always-multiline",
|
||||||
|
"functions": "never"
|
||||||
|
}],
|
||||||
|
"no-return-await": "off",
|
||||||
|
"no-constant-condition": "off",
|
||||||
|
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||||
|
"no-underscore-dangle": "off",
|
||||||
|
"no-console": "off",
|
||||||
|
"no-mixed-operators": ["error", { "allowSamePrecedence": true }],
|
||||||
|
"strict": ["error", "global"],
|
||||||
|
"consistent-return": "off",
|
||||||
|
"func-names": "off",
|
||||||
|
"no-tabs": "off",
|
||||||
|
"indent": ["error", "tab"],
|
||||||
|
"no-eq-null": "off",
|
||||||
|
"camelcase": "off",
|
||||||
|
"no-new": "off",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"no-use-before-define": ["error", "nofunc"],
|
||||||
|
"no-prototype-builtins": "off",
|
||||||
|
"new-cap": "off",
|
||||||
|
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
|
||||||
|
"object-curly-newline": "off",
|
||||||
|
"no-restricted-globals": "off",
|
||||||
|
"function-paren-newline": "off",
|
||||||
|
"import/no-unresolved": "error",
|
||||||
|
"quotes": ["error", "single", {
|
||||||
|
"avoidEscape": true,
|
||||||
|
"allowTemplateLiterals": true
|
||||||
|
}],
|
||||||
|
"no-else-return": [ "error", { "allowElseIf": true } ],
|
||||||
|
"operator-linebreak": [ "error", "after" ],
|
||||||
|
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
|
||||||
|
|
||||||
|
// ES6
|
||||||
|
"prefer-rest-params": "off",
|
||||||
|
"prefer-spread": "off",
|
||||||
|
"prefer-arrow-callback": "off",
|
||||||
|
"prefer-template": "off",
|
||||||
|
"no-var": "off",
|
||||||
|
"object-shorthand": "off",
|
||||||
|
"vars-on-top": "off",
|
||||||
|
"prefer-destructuring": "off",
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
"import/no-extraneous-dependencies": "off",
|
||||||
|
"import/no-dynamic-require": "off",
|
||||||
|
"import/newline-after-import": "off",
|
||||||
|
"no-bitwise": "off",
|
||||||
|
"global-require": "off",
|
||||||
|
"max-len": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
|
"no-restricted-syntax": "off",
|
||||||
|
"no-script-url": "off",
|
||||||
|
"default-case": "off",
|
||||||
|
"linebreak-style": "off",
|
||||||
|
|
||||||
|
// "no-multi-assign": "off",
|
||||||
|
// "one-var": "off",
|
||||||
|
// "no-undef": "off",
|
||||||
|
// "max-nested-callbacks": "off",
|
||||||
|
// "no-mixed-requires": "off",
|
||||||
|
// "brace-style": "off",
|
||||||
|
// "max-statements-per-line": "off",
|
||||||
|
// "no-unused-vars": "off",
|
||||||
|
// "no-mixed-spaces-and-tabs": "off",
|
||||||
|
// "no-useless-concat": "off",
|
||||||
|
// "require-jsdoc": "off",
|
||||||
|
// "eqeqeq": "off",
|
||||||
|
// "no-negated-condition": "off",
|
||||||
|
// "one-var-declaration-per-line": "off",
|
||||||
|
// "no-lonely-if": "off",
|
||||||
|
// "radix": "off",
|
||||||
|
// "no-else-return": "off",
|
||||||
|
// "no-useless-escape": "off",
|
||||||
|
// "block-scoped-var": "off",
|
||||||
|
// "operator-assignment": "off",
|
||||||
|
// "yoda": "off",
|
||||||
|
// "no-loop-func": "off",
|
||||||
|
// "no-void": "off",
|
||||||
|
// "valid-jsdoc": "off",
|
||||||
|
// "no-cond-assign": "off",
|
||||||
|
// "no-redeclare": "off",
|
||||||
|
// "no-unreachable": "off",
|
||||||
|
// "no-nested-ternary": "off",
|
||||||
|
// "operator-linebreak": "off",
|
||||||
|
// "guard-for-in": "off",
|
||||||
|
// "no-unneeded-ternary": "off",
|
||||||
|
// "no-sequences": "off",
|
||||||
|
// "no-extend-native": "off",
|
||||||
|
// "no-shadow-restricted-names": "off",
|
||||||
|
// "no-extra-boolean-cast": "off",
|
||||||
|
// "no-path-concat": "off",
|
||||||
|
// "no-unused-expressions": "off",
|
||||||
|
// "no-return-assign": "off",
|
||||||
|
// "no-restricted-modules": "off",
|
||||||
|
// "object-curly-spacing": "off",
|
||||||
|
// "indent": "off",
|
||||||
|
// "padded-blocks": "off",
|
||||||
|
// "eol-last": "off",
|
||||||
|
// "lines-around-directive": "off",
|
||||||
|
// "strict": "off",
|
||||||
|
// "comma-dangle": "off",
|
||||||
|
// "no-multi-spaces": "off",
|
||||||
|
// "quotes": "off",
|
||||||
|
// "keyword-spacing": "off",
|
||||||
|
// "no-mixed-operators": "off",
|
||||||
|
// "comma-spacing": "off",
|
||||||
|
// "no-trailing-spaces": "off",
|
||||||
|
// "key-spacing": "off",
|
||||||
|
// "no-multiple-empty-lines": "off",
|
||||||
|
// "spaced-comment": "off",
|
||||||
|
// "space-in-parens": "off",
|
||||||
|
// "block-spacing": "off",
|
||||||
|
// "quote-props": "off",
|
||||||
|
// "space-unary-ops": "off",
|
||||||
|
// "no-empty": "off",
|
||||||
|
// "dot-notation": "off",
|
||||||
|
// "func-call-spacing": "off",
|
||||||
|
// "array-bracket-spacing": "off",
|
||||||
|
// "object-property-newline": "off",
|
||||||
|
// "no-continue": "off",
|
||||||
|
// "no-extra-semi": "off",
|
||||||
|
// "no-spaced-func": "off",
|
||||||
|
// "no-useless-return": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!--
|
||||||
|
== Github Issues are for bug reports and feature requests only ==
|
||||||
|
== Please visit https://community.nodebb.org for other support ==
|
||||||
|
== Found a security exploit? Please email us at security@nodebb.org instead for immediate attention ==
|
||||||
|
== → DO NOT SUBMIT VULNERABILITIES TO THE PUBLIC BUG TRACKER ==
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- ++ Please include the following information when submitting a bug report ++ -->
|
||||||
|
|
||||||
|
- **NodeBB version:**
|
||||||
|
- **NodeBB git hash:**
|
||||||
|
<!-- (to find your git hash, execute `git rev-parse HEAD` from the main NodeBB directory) -->
|
||||||
|
- **Installed NodeBB Plugins:**
|
||||||
|
<!-- (to find installed plugins run ./nodebb plugins) -->
|
||||||
|
- **Database type:** mongo, redis, or postgres
|
||||||
|
- **Database version:**
|
||||||
|
<!-- `mongod --version`, `redis-server --version`, or `postgres --version` -->
|
||||||
|
- **Exact steps to cause this issue:**
|
||||||
|
<!--
|
||||||
|
1. First I did this...
|
||||||
|
2. Then, I clicked on this item...
|
||||||
|
-->
|
||||||
|
- **What you expected:**
|
||||||
|
<!-- e.g. I expected *abc* to *xyz* -->
|
||||||
|
- **What happened instead:**
|
||||||
|
<!-- e.g. Instead, I got *zyx* and NodeBB set fire to my house -->
|
||||||
|
|
||||||
|
<!-- Thank you! -->
|
||||||
81
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,81 +0,0 @@
|
|||||||
name: Bug Report
|
|
||||||
description: File a bug report
|
|
||||||
labels: ["bug"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Github Issues are for bug reports and feature requests only
|
|
||||||
Please visit https://community.nodebb.org for other support
|
|
||||||
Found a security exploit? Please email us at security@nodebb.org instead for immediate attention
|
|
||||||
### → DO NOT SUBMIT VULNERABILITIES TO THE PUBLIC BUG TRACKER ←
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: NodeBB version
|
|
||||||
placeholder: e.g. v2.2.0
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: NodeBB git hash
|
|
||||||
description: to find your git hash, execute `git rev-parse HEAD` from the main NodeBB directory
|
|
||||||
placeholder: e.g. 783250ee6f8c51cdc243ce3b8d9f1a080517247e
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: NodeJS version
|
|
||||||
placeholder: e.g. v16.15.1
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Installed NodeBB plugins
|
|
||||||
description: to find installed plugins run `./nodebb plugins`
|
|
||||||
placeholder: |
|
|
||||||
e.g.
|
|
||||||
* nodebb-plugin-2factor@5.0.1 (installed, disabled)
|
|
||||||
* nodebb-plugin-composer-default@8.0.0 (installed, enabled)
|
|
||||||
* nodebb-plugin-dbsearch@5.1.5 (installed, disabled)
|
|
||||||
* nodebb-plugin-emoji@4.0.4 (installed, enabled)
|
|
||||||
* nodebb-plugin-emoji-android@3.0.0 (installed, enabled)
|
|
||||||
* nodebb-plugin-markdown@10.0.0 (installed, enabled)
|
|
||||||
* nodebb-plugin-mentions@3.0.11 (installed, enabled)
|
|
||||||
* nodebb-plugin-spam-be-gone@1.0.0 (installed, disabled)
|
|
||||||
* nodebb-rewards-essentials@0.2.1 (installed, enabled)
|
|
||||||
* nodebb-theme-lavender@6.0.0 (installed, disabled)
|
|
||||||
* nodebb-theme-persona@12.0.11 (installed, enabled)
|
|
||||||
* nodebb-theme-slick@2.0.2 (installed, disabled)
|
|
||||||
* nodebb-theme-vanilla@12.1.18 (installed, disabled)
|
|
||||||
* nodebb-widget-essentials@6.0.0 (installed, enabled)
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Database type
|
|
||||||
multiple: true
|
|
||||||
options:
|
|
||||||
- MongoDB
|
|
||||||
- Redis
|
|
||||||
- PostgreSQL
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Database version
|
|
||||||
description: "`mongod --version`, `redis-server --version`, or `postgres --version`"
|
|
||||||
placeholder: e.g. v5.0.9
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Exact steps to cause this issue
|
|
||||||
placeholder: |
|
|
||||||
1. First I did this...
|
|
||||||
2. Then, I clicked on this item...
|
|
||||||
|
|
||||||
A quick note: MP4 and MOV formatted video files are now allowed to be uploaded to GH.
|
|
||||||
Please upload if reproduction steps are hard to describe or reproduce reliably.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What you expected
|
|
||||||
placeholder: e.g. I expected *abc* to *xyz*
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What happened instead
|
|
||||||
placeholder: e.g. Instead, I got *zyx* and NodeBB set fire to my house
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Anything else?
|
|
||||||
description: Any additional context about the issue you're encountering
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "**Thank you!**"
|
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
|||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
|
||||||
- name: Community Forum
|
|
||||||
url: https://community.nodebb.org
|
|
||||||
about: Github Issues are for bug reports and feature requests only, please use community forum for other support
|
|
||||||
24
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,24 +0,0 @@
|
|||||||
name: Feature Request
|
|
||||||
description: Suggest a new feature
|
|
||||||
labels: ["enhancement"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Github Issues are for bug reports and feature requests only
|
|
||||||
Please visit https://community.nodebb.org for other support
|
|
||||||
Found a security exploit? Please email us at security@nodebb.org instead for immediate attention
|
|
||||||
### → DO NOT SUBMIT VULNERABILITIES TO THE PUBLIC BUG TRACKER ←
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Community forum reference
|
|
||||||
description: If this feature was already discussed on the Community Forum link it here
|
|
||||||
placeholder: https://community.nodebb.org/topic/0/example-feature-request
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "**Thank you!**"
|
|
||||||
9
.github/SECURITY.md
vendored
@@ -1,9 +0,0 @@
|
|||||||
# Reporting a security vulnerability
|
|
||||||
|
|
||||||
NodeBB's security policy is based around a private bug bounty program. Users are invited to explore NodeBB for vulnerabilities, and report them to the NodeBB team so that they can be patched.
|
|
||||||
|
|
||||||
If you have found a security vulnerability, **do not post it onto our GitHub tracker**. Some security vulnerabilities are quite severe and discretion is recommended. Email the NodeBB Security Team at security@nodebb.org, instead, even if you are not sure whether something qualifies.
|
|
||||||
|
|
||||||
# Bug Bounty Program
|
|
||||||
|
|
||||||
Security vulnerability reports may be eligible for a bounty based on severity and confirmation from NodeBB team members. For full details regarding our bug bounty program, including the bounty amounts, please consult the [dedicated page for our Bug Bounty Program](https://nodebb.org/bounty).
|
|
||||||
61
.github/workflows/docker.yml
vendored
@@ -1,61 +0,0 @@
|
|||||||
name: Run Docker
|
|
||||||
|
|
||||||
# Controls when the workflow will run
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- 'v*.x'
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository }}
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}.x
|
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
|
||||||
type=ref,event=branch,enable=${{ github.event.repository.default_branch != github.ref }}
|
|
||||||
|
|
||||||
- name: Build and push Docker images
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
214
.github/workflows/test.yaml
vendored
@@ -1,214 +0,0 @@
|
|||||||
name: Lint and test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
- bootstrap5
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
- bootstrap5
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
permissions:
|
|
||||||
checks: write # for coverallsapp/github-action to create new checks
|
|
||||||
contents: read # for actions/checkout to fetch code
|
|
||||||
name: Lint and test
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
node: [16, 18]
|
|
||||||
database: [mongo-dev, mongo, redis, postgres]
|
|
||||||
include:
|
|
||||||
# only run coverage once
|
|
||||||
- os: ubuntu-latest
|
|
||||||
node: 16
|
|
||||||
coverage: true
|
|
||||||
# test under development once
|
|
||||||
- database: mongo-dev
|
|
||||||
test_env: development
|
|
||||||
# only run eslint once
|
|
||||||
- os: ubuntu-latest
|
|
||||||
node: 16
|
|
||||||
database: mongo-dev
|
|
||||||
lint: true
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
TEST_ENV: ${{ matrix.test_env || 'production' }}
|
|
||||||
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: 'postgres:15-alpine'
|
|
||||||
env:
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
# Set health checks to wait until postgres has started
|
|
||||||
options: >-
|
|
||||||
--health-cmd pg_isready
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
ports:
|
|
||||||
# Maps port 5432 on service container to the host
|
|
||||||
- 5432:5432
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: 'redis:7.0.11'
|
|
||||||
# Set health checks to wait until redis has started
|
|
||||||
options: >-
|
|
||||||
--health-cmd "redis-cli ping"
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
ports:
|
|
||||||
# Maps port 6379 on service container to the host
|
|
||||||
- 6379:6379
|
|
||||||
|
|
||||||
mongo:
|
|
||||||
image: 'mongo:3.7'
|
|
||||||
ports:
|
|
||||||
# Maps port 27017 on service container to the host
|
|
||||||
- 27017:27017
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- run: cp install/package.json package.json
|
|
||||||
|
|
||||||
- name: Install Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
|
|
||||||
- name: NPM Install
|
|
||||||
uses: bahmutov/npm-install@v1
|
|
||||||
with:
|
|
||||||
useLockFile: false
|
|
||||||
|
|
||||||
- name: Setup on MongoDB
|
|
||||||
if: startsWith(matrix.database, 'mongo')
|
|
||||||
env:
|
|
||||||
SETUP: >-
|
|
||||||
{
|
|
||||||
"url": "http://127.0.0.1:4567",
|
|
||||||
"secret": "abcdef",
|
|
||||||
"admin:username": "admin",
|
|
||||||
"admin:email": "test@example.org",
|
|
||||||
"admin:password": "hAN3Eg8W",
|
|
||||||
"admin:password:confirm": "hAN3Eg8W",
|
|
||||||
|
|
||||||
"database": "mongo",
|
|
||||||
"mongo:host": "127.0.0.1",
|
|
||||||
"mongo:port": 27017,
|
|
||||||
"mongo:username": "",
|
|
||||||
"mongo:password": "",
|
|
||||||
"mongo:database": "nodebb"
|
|
||||||
}
|
|
||||||
CI: >-
|
|
||||||
{
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 27017,
|
|
||||||
"database": "ci_test"
|
|
||||||
}
|
|
||||||
run: |
|
|
||||||
node app --setup="${SETUP}" --ci="${CI}"
|
|
||||||
|
|
||||||
- name: Setup on PostgreSQL
|
|
||||||
if: startsWith(matrix.database, 'postgres')
|
|
||||||
env:
|
|
||||||
SETUP: >-
|
|
||||||
{
|
|
||||||
"url": "http://127.0.0.1:4567",
|
|
||||||
"secret": "abcdef",
|
|
||||||
"admin:username": "admin",
|
|
||||||
"admin:email": "test@example.org",
|
|
||||||
"admin:password": "hAN3Eg8W",
|
|
||||||
"admin:password:confirm": "hAN3Eg8W",
|
|
||||||
|
|
||||||
"database": "postgres",
|
|
||||||
"postgres:host": "127.0.0.1",
|
|
||||||
"postgres:port": 5432,
|
|
||||||
"postgres:username": "postgres",
|
|
||||||
"postgres:password": "postgres",
|
|
||||||
"postgres:database": "nodebb"
|
|
||||||
}
|
|
||||||
CI: >-
|
|
||||||
{
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"database": "ci_test",
|
|
||||||
"port": 5432,
|
|
||||||
"username": "postgres",
|
|
||||||
"password": "postgres"
|
|
||||||
}
|
|
||||||
run: |
|
|
||||||
node -e "const { Client } = require('pg'); const c = new Client({ host: '127.0.0.1', port: 5432, user: 'postgres', password: 'postgres' }); c.connect().then(() => c.query('CREATE DATABASE nodebb')).then(() => c.query('CREATE DATABASE ci_test')).then(() => c.end())"
|
|
||||||
node app --setup="${SETUP}" --ci="${CI}"
|
|
||||||
|
|
||||||
- name: Setup on Redis
|
|
||||||
if: startsWith(matrix.database, 'redis')
|
|
||||||
env:
|
|
||||||
SETUP: >-
|
|
||||||
{
|
|
||||||
"url": "http://127.0.0.1:4567/forum",
|
|
||||||
"secret": "abcdef",
|
|
||||||
"admin:username": "admin",
|
|
||||||
"admin:email": "test@example.org",
|
|
||||||
"admin:password": "hAN3Eg8W",
|
|
||||||
"admin:password:confirm": "hAN3Eg8W",
|
|
||||||
|
|
||||||
"database": "redis",
|
|
||||||
"redis:host": "127.0.0.1",
|
|
||||||
"redis:port": 6379,
|
|
||||||
"redis:password": "",
|
|
||||||
"redis:database": 0
|
|
||||||
}
|
|
||||||
CI: >-
|
|
||||||
{
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"database": 1,
|
|
||||||
"port": 6379
|
|
||||||
}
|
|
||||||
run: |
|
|
||||||
node app --setup="${SETUP}" --ci="${CI}"
|
|
||||||
|
|
||||||
- name: Run ESLint
|
|
||||||
if: matrix.lint
|
|
||||||
run: npm run lint
|
|
||||||
|
|
||||||
- name: Node tests
|
|
||||||
run: npm test
|
|
||||||
|
|
||||||
- name: Extract coverage info
|
|
||||||
run: npm run coverage
|
|
||||||
|
|
||||||
- name: Test coverage
|
|
||||||
uses: coverallsapp/github-action@v2.2.0
|
|
||||||
if: matrix.coverage
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
flag-name: ${{ matrix.os }}-node-${{ matrix.node }}-db-${{ matrix.database }}
|
|
||||||
parallel: true
|
|
||||||
|
|
||||||
finish:
|
|
||||||
permissions:
|
|
||||||
checks: write # for coverallsapp/github-action to create new checks
|
|
||||||
needs: test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Coveralls Finished
|
|
||||||
uses: coverallsapp/github-action@v2.2.0
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
parallel-finished: true
|
|
||||||
6
.gitignore
vendored
@@ -1,4 +1,3 @@
|
|||||||
dist/
|
|
||||||
yarn.lock
|
yarn.lock
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -28,6 +27,7 @@ pidfile
|
|||||||
# templates
|
# templates
|
||||||
/public/templates
|
/public/templates
|
||||||
|
|
||||||
|
/public/sounds
|
||||||
/public/uploads
|
/public/uploads
|
||||||
/test/uploads
|
/test/uploads
|
||||||
|
|
||||||
@@ -40,7 +40,6 @@ pidfile
|
|||||||
/public/acp.min.js.map
|
/public/acp.min.js.map
|
||||||
/public/installer.css
|
/public/installer.css
|
||||||
/public/installer.min.js
|
/public/installer.min.js
|
||||||
/public/bootstrap.min.css
|
|
||||||
/public/logo.png
|
/public/logo.png
|
||||||
|
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||||
@@ -67,6 +66,3 @@ test/files/normalise.jpg.png
|
|||||||
test/files/normalise-resized.jpg
|
test/files/normalise-resized.jpg
|
||||||
package-lock.json
|
package-lock.json
|
||||||
/package.json
|
/package.json
|
||||||
*.mongodb
|
|
||||||
link-plugins.sh
|
|
||||||
test.sh
|
|
||||||
1
.husky/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
_
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx --no-install commitlint --edit $1
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx --no-install lint-staged
|
|
||||||
17
.jsbeautifyrc
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"indent_size": 4,
|
||||||
|
"indent_char": " ",
|
||||||
|
"indent_level": 0,
|
||||||
|
"indent_with_tabs": true,
|
||||||
|
"preserve_newlines": true,
|
||||||
|
"max_preserve_newlines": 10,
|
||||||
|
"jslint_happy": true,
|
||||||
|
"brace_style": "collapse",
|
||||||
|
"keep_array_indentation": false,
|
||||||
|
"keep_function_indentation": false,
|
||||||
|
"space_before_conditional": true,
|
||||||
|
"break_chained_methods": false,
|
||||||
|
"eval_code": false,
|
||||||
|
"unescape_strings": false,
|
||||||
|
"wrap_line_length": 0
|
||||||
|
}
|
||||||
91
.jshintrc
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
// JSHint Default Configuration File (as on JSHint website)
|
||||||
|
// See http://jshint.com/docs/ for more details
|
||||||
|
|
||||||
|
"maxerr" : 50, // {int} Maximum error before stopping
|
||||||
|
|
||||||
|
"esversion": 6,
|
||||||
|
|
||||||
|
// Enforcing
|
||||||
|
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||||
|
"camelcase" : false, // true: Identifiers must be in camelCase
|
||||||
|
"curly" : true, // true: Require {} for every new block or scope
|
||||||
|
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||||
|
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||||
|
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
|
||||||
|
"indent" : 4, // {int} Number of spaces to use for indentation
|
||||||
|
"latedef" : false, // true: Require variables/functions to be defined before being used
|
||||||
|
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
|
||||||
|
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
|
||||||
|
"noempty" : true, // true: Prohibit use of empty blocks
|
||||||
|
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
|
||||||
|
"plusplus" : false, // true: Prohibit use of `++` & `--`
|
||||||
|
"quotmark" : false, // Quotation mark consistency:
|
||||||
|
// false : do nothing (default)
|
||||||
|
// true : ensure whatever is used is consistent
|
||||||
|
// "single" : require single quotes
|
||||||
|
// "double" : require double quotes
|
||||||
|
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
|
||||||
|
"unused" : true, // true: Require all defined variables be used
|
||||||
|
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
|
||||||
|
"trailing" : false, // true: Prohibit trailing whitespaces
|
||||||
|
"maxparams" : false, // {int} Max number of formal params allowed per function
|
||||||
|
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
|
||||||
|
"maxstatements" : false, // {int} Max number statements per function
|
||||||
|
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
|
||||||
|
"maxlen" : false, // {int} Max number of characters per line
|
||||||
|
|
||||||
|
// Relaxing
|
||||||
|
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
|
||||||
|
"boss" : false, // true: Tolerate assignments where comparisons would be expected
|
||||||
|
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||||
|
"eqnull" : false, // true: Tolerate use of `== null`
|
||||||
|
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
|
||||||
|
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||||
|
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||||
|
// (ex: `for each`, multiple try/catch, function expression…)
|
||||||
|
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||||
|
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
|
||||||
|
"funcscope" : false, // true: Tolerate defining variables inside control statements"
|
||||||
|
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
|
||||||
|
"iterator" : false, // true: Tolerate using the `__iterator__` property
|
||||||
|
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
|
||||||
|
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
|
||||||
|
"laxcomma" : false, // true: Tolerate comma-first style coding
|
||||||
|
"loopfunc" : false, // true: Tolerate functions being defined in loops
|
||||||
|
"multistr" : false, // true: Tolerate multi-line strings
|
||||||
|
"proto" : false, // true: Tolerate using the `__proto__` property
|
||||||
|
"scripturl" : false, // true: Tolerate script-targeted URLs
|
||||||
|
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
|
||||||
|
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
|
||||||
|
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
|
||||||
|
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
|
||||||
|
"validthis" : false, // true: Tolerate using this in a non-constructor function
|
||||||
|
|
||||||
|
// Environments
|
||||||
|
"browser" : true, // Web Browser (window, document, etc)
|
||||||
|
"couch" : false, // CouchDB
|
||||||
|
"devel" : true, // Development/debugging (alert, confirm, etc)
|
||||||
|
"dojo" : false, // Dojo Toolkit
|
||||||
|
"jquery" : true, // jQuery
|
||||||
|
"mootools" : false, // MooTools
|
||||||
|
"node" : true, // Node.js
|
||||||
|
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
|
||||||
|
"prototypejs" : false, // Prototype and Scriptaculous
|
||||||
|
"rhino" : false, // Rhino
|
||||||
|
"worker" : false, // Web Workers
|
||||||
|
"wsh" : false, // Windows Scripting Host
|
||||||
|
"yui" : false, // Yahoo User Interface
|
||||||
|
"mocha": true,
|
||||||
|
|
||||||
|
// Legacy
|
||||||
|
"nomen" : false, // true: Prohibit dangling `_` in variables
|
||||||
|
"onevar" : false, // true: Allow only one `var` statement per function
|
||||||
|
"passfail" : false, // true: Stop on first error
|
||||||
|
"white" : false, // true: Check against strict whitespace and indentation rules
|
||||||
|
|
||||||
|
// Custom Globals
|
||||||
|
"globals" : {
|
||||||
|
"Promise": true
|
||||||
|
} // additional predefined global variables
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
reporter: dot
|
|
||||||
timeout: 25000
|
|
||||||
exit: true
|
|
||||||
bail: true
|
|
||||||
47
.travis.yml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
services:
|
||||||
|
- mongodb
|
||||||
|
- redis-server
|
||||||
|
- postgresql
|
||||||
|
before_install:
|
||||||
|
- cp install/package.json package.json
|
||||||
|
- sudo apt-get update
|
||||||
|
- sudo apt-get --yes remove postgresql\*
|
||||||
|
- sudo apt-get install -y postgresql-12 postgresql-client-12
|
||||||
|
- sudo cp /etc/postgresql/{9.6,12}/main/pg_hba.conf
|
||||||
|
- sudo service postgresql restart 12
|
||||||
|
before_script:
|
||||||
|
- sleep 15 # wait for mongodb to be ready
|
||||||
|
- "mongo mydb_test --eval 'db.createUser({user:\"travis\", pwd: \"test\", roles: []});'"
|
||||||
|
- sh -c "if [ '$DB' = 'mongodb' ]; then node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"mongo\\\",\\\"mongo:host\\\":\\\"127.0.0.1\\\",\\\"mongo:port\\\":27017,\\\"mongo:username\\\":\\\"\\\",\\\"mongo:password\\\":\\\"\\\",\\\"mongo:database\\\":0,\\\"admin:username\\\":\\\"admin\\\",\\\"admin:email\\\":\\\"test@example.org\\\",\\\"admin:password\\\":\\\"hAN3Eg8W\\\",\\\"admin:password:confirm\\\":\\\"hAN3Eg8W\\\"}\" --ci=\"{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":27017,\\\"database\\\":\\\"travis_ci_test\\\"}\"; fi"
|
||||||
|
- sh -c "if [ '$DB' = 'redis' ]; then node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567/forum\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"redis\\\",\\\"redis:host\\\":\\\"127.0.0.1\\\",\\\"redis:port\\\":6379,\\\"redis:password\\\":\\\"\\\",\\\"redis:database\\\":0,\\\"admin:username\\\":\\\"admin\\\",\\\"admin:email\\\":\\\"test@example.org\\\",\\\"admin:password\\\":\\\"hAN3Eg8W\\\",\\\"admin:password:confirm\\\":\\\"hAN3Eg8W\\\"}\" --ci=\"{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":6379,\\\"database\\\":1}\"; fi"
|
||||||
|
- sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'create database nodebb;' -U postgres; psql -c 'create database travis_ci_test;' -U postgres; node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"postgres\\\",\\\"postgres:host\\\":\\\"127.0.0.1\\\",\\\"postgres:port\\\":5432,\\\"postgres:password\\\":\\\"\\\",\\\"postgres:database\\\":\\\"nodebb\\\",\\\"admin:username\\\":\\\"admin\\\",\\\"admin:email\\\":\\\"test@example.org\\\",\\\"admin:password\\\":\\\"hAN3Eg8W\\\",\\\"admin:password:confirm\\\":\\\"hAN3Eg8W\\\"}\" --ci=\"{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":5432,\\\"username\\\":\\\"postgres\\\",\\\"database\\\":\\\"travis_ci_test\\\"}\"; fi"
|
||||||
|
after_success:
|
||||||
|
- "npm run coveralls"
|
||||||
|
language: node_js
|
||||||
|
sudo: false
|
||||||
|
dist: xenial
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- PGUSER=postgres
|
||||||
|
- PGPORT=5432
|
||||||
|
- CXX=g++-4.8
|
||||||
|
jobs:
|
||||||
|
- "DB=mongodb TEST_ENV=production"
|
||||||
|
- "DB=mongodb TEST_ENV=development"
|
||||||
|
- "DB=redis TEST_ENV=production"
|
||||||
|
- "DB=postgres TEST_ENV=production"
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- mongodb-4.0-xenial
|
||||||
|
packages:
|
||||||
|
- g++-4.8
|
||||||
|
- mongodb-org-server
|
||||||
|
node_js:
|
||||||
|
- "12"
|
||||||
|
- "10"
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
7206
.tx/config
7357
CHANGELOG.md
30
Dockerfile
@@ -1,36 +1,17 @@
|
|||||||
FROM --platform=$BUILDPLATFORM node:lts as npm
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/build && \
|
|
||||||
chown -R node:node /usr/src/build
|
|
||||||
WORKDIR /usr/src/build
|
|
||||||
|
|
||||||
ARG NODE_ENV
|
|
||||||
ENV NODE_ENV $NODE_ENV
|
|
||||||
|
|
||||||
COPY --chown=node:node install/package.json /usr/src/build/package.json
|
|
||||||
|
|
||||||
USER node
|
|
||||||
|
|
||||||
RUN npm install --omit=dev
|
|
||||||
|
|
||||||
|
|
||||||
FROM node:lts
|
FROM node:lts
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app && \
|
RUN mkdir -p /usr/src/app
|
||||||
chown -R node:node /usr/src/app
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
ARG NODE_ENV
|
ARG NODE_ENV
|
||||||
ENV NODE_ENV $NODE_ENV
|
ENV NODE_ENV $NODE_ENV
|
||||||
|
|
||||||
COPY --chown=node:node --from=npm /usr/src/build /usr/src/app
|
COPY install/package.json /usr/src/app/package.json
|
||||||
|
|
||||||
USER node
|
RUN npm install --only=prod && \
|
||||||
|
|
||||||
RUN npm rebuild && \
|
|
||||||
npm cache clean --force
|
npm cache clean --force
|
||||||
|
|
||||||
COPY --chown=node:node . /usr/src/app
|
COPY . /usr/src/app
|
||||||
|
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
daemon=false \
|
daemon=false \
|
||||||
@@ -38,4 +19,5 @@ ENV NODE_ENV=production \
|
|||||||
|
|
||||||
EXPOSE 4567
|
EXPOSE 4567
|
||||||
|
|
||||||
CMD test -n "${SETUP}" && ./nodebb setup || node ./nodebb build; node ./nodebb start
|
CMD ./nodebb start
|
||||||
|
|
||||||
|
|||||||
289
Gruntfile.js
@@ -1,35 +1,95 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const nconf = require('nconf');
|
|
||||||
|
|
||||||
nconf.argv().env({
|
var async = require('async');
|
||||||
separator: '__',
|
var fork = require('child_process').fork;
|
||||||
});
|
var env = process.env;
|
||||||
const winston = require('winston');
|
var worker;
|
||||||
const { fork } = require('child_process');
|
var updateWorker;
|
||||||
|
var initWorker;
|
||||||
const { env } = process;
|
var incomplete = [];
|
||||||
let worker;
|
var running = 0;
|
||||||
|
|
||||||
env.NODE_ENV = env.NODE_ENV || 'development';
|
env.NODE_ENV = env.NODE_ENV || 'development';
|
||||||
|
|
||||||
const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');
|
|
||||||
const prestart = require('./src/prestart');
|
|
||||||
|
|
||||||
prestart.loadConfig(configFile);
|
var nconf = require('nconf');
|
||||||
|
nconf.file({
|
||||||
|
file: 'config.json',
|
||||||
|
});
|
||||||
|
|
||||||
const db = require('./src/database');
|
nconf.defaults({
|
||||||
const plugins = require('./src/plugins');
|
base_dir: __dirname,
|
||||||
|
views_dir: './build/public/templates',
|
||||||
|
});
|
||||||
|
var winston = require('winston');
|
||||||
|
winston.configure({
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
handleExceptions: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
var db = require('./src/database');
|
||||||
|
|
||||||
module.exports = function (grunt) {
|
module.exports = function (grunt) {
|
||||||
const args = [];
|
var args = [];
|
||||||
|
var initArgs = ['--build'];
|
||||||
if (!grunt.option('verbose')) {
|
if (!grunt.option('verbose')) {
|
||||||
args.push('--log-level=info');
|
args.push('--log-level=info');
|
||||||
nconf.set('log-level', 'info');
|
initArgs.push('--log-level=info');
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(action, filepath, target) {
|
||||||
|
var updateArgs = args.slice();
|
||||||
|
var compiling;
|
||||||
|
var time = Date.now();
|
||||||
|
|
||||||
|
if (target === 'lessUpdated_Client') {
|
||||||
|
compiling = 'clientCSS';
|
||||||
|
} else if (target === 'lessUpdated_Admin') {
|
||||||
|
compiling = 'acpCSS';
|
||||||
|
} else if (target === 'clientUpdated') {
|
||||||
|
compiling = 'js';
|
||||||
|
} else if (target === 'templatesUpdated') {
|
||||||
|
compiling = 'tpl';
|
||||||
|
} else if (target === 'langUpdated') {
|
||||||
|
compiling = 'lang';
|
||||||
|
} else if (target === 'serverUpdated') {
|
||||||
|
// Do nothing, just restart
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compiling && !incomplete.includes(compiling)) {
|
||||||
|
incomplete.push(compiling);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateArgs.push('--build');
|
||||||
|
updateArgs.push(incomplete.join(','));
|
||||||
|
|
||||||
|
worker.kill();
|
||||||
|
if (updateWorker) {
|
||||||
|
updateWorker.kill('SIGKILL');
|
||||||
|
}
|
||||||
|
updateWorker = fork('app.js', updateArgs, { env: env });
|
||||||
|
running += 1;
|
||||||
|
updateWorker.on('exit', function () {
|
||||||
|
running -= 1;
|
||||||
|
if (running === 0) {
|
||||||
|
worker = fork('app.js', args, {
|
||||||
|
env: env,
|
||||||
|
});
|
||||||
|
worker.on('message', function () {
|
||||||
|
if (incomplete.length) {
|
||||||
|
incomplete = [];
|
||||||
|
|
||||||
|
if (grunt.option('verbose')) {
|
||||||
|
grunt.log.writeln('NodeBB restarted in ' + (Date.now() - time) + ' ms');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
prestart.setupWinston();
|
|
||||||
|
|
||||||
grunt.initConfig({
|
grunt.initConfig({
|
||||||
watch: {},
|
watch: {},
|
||||||
@@ -39,76 +99,84 @@ module.exports = function (grunt) {
|
|||||||
|
|
||||||
grunt.registerTask('default', ['watch']);
|
grunt.registerTask('default', ['watch']);
|
||||||
|
|
||||||
grunt.registerTask('init', async function () {
|
grunt.registerTask('init', function () {
|
||||||
const done = this.async();
|
var done = this.async();
|
||||||
let pluginList = [];
|
async.waterfall([
|
||||||
if (!process.argv.includes('--core')) {
|
function (next) {
|
||||||
await db.init();
|
db.init(next);
|
||||||
pluginList = await plugins.getActive();
|
},
|
||||||
addBaseThemes(pluginList);
|
function (next) {
|
||||||
if (!pluginList.includes('nodebb-plugin-composer-default')) {
|
db.getSortedSetRange('plugins:active', 0, -1, next);
|
||||||
pluginList.push('nodebb-plugin-composer-default');
|
},
|
||||||
}
|
function (plugins, next) {
|
||||||
if (!pluginList.includes('nodebb-theme-persona')) {
|
addBaseThemes(plugins, next);
|
||||||
pluginList.push('nodebb-theme-persona');
|
},
|
||||||
}
|
function (plugins, next) {
|
||||||
|
if (!plugins.includes('nodebb-plugin-composer-default')) {
|
||||||
|
plugins.push('nodebb-plugin-composer-default');
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleUpdated_Client = pluginList.map(p => `node_modules/${p}/*.scss`)
|
if (process.argv.includes('--core')) {
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/*.css`))
|
plugins = [];
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/+(public|static|scss)/**/*.scss`))
|
}
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/+(public|static)/**/*.css`));
|
|
||||||
|
|
||||||
const clientUpdated = pluginList.map(p => `node_modules/${p}/+(public|static)/**/*.js`);
|
const lessUpdated_Client = plugins.map(p => 'node_modules/' + p + '/**/*.less');
|
||||||
const serverUpdated = pluginList.map(p => `node_modules/${p}/*.js`)
|
const lessUpdated_Admin = plugins.map(p => 'node_modules/' + p + '/**/*.less');
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/+(lib|src)/**/*.js`));
|
const clientUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.js');
|
||||||
|
const templatesUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.tpl');
|
||||||
|
const langUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.json');
|
||||||
|
|
||||||
const templatesUpdated = pluginList.map(p => `node_modules/${p}/+(public|static|templates)/**/*.tpl`);
|
|
||||||
const langUpdated = pluginList.map(p => `node_modules/${p}/+(public|static|languages)/**/*.json`);
|
|
||||||
const interval = 100;
|
|
||||||
grunt.config(['watch'], {
|
grunt.config(['watch'], {
|
||||||
styleUpdated: {
|
lessUpdated_Client: {
|
||||||
files: [
|
files: [
|
||||||
'public/scss/**/*.scss',
|
'public/less/*.less',
|
||||||
...styleUpdated_Client,
|
'!public/less/admin/**/*.less',
|
||||||
|
...lessUpdated_Client,
|
||||||
|
'!node_modules/nodebb-*/node_modules/**',
|
||||||
|
'!node_modules/nodebb-*/.git/**',
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lessUpdated_Admin: {
|
||||||
|
files: [
|
||||||
|
'public/less/admin/**/*.less',
|
||||||
|
...lessUpdated_Admin,
|
||||||
|
'!node_modules/nodebb-*/node_modules/**',
|
||||||
|
'!node_modules/nodebb-*/.git/**',
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
clientUpdated: {
|
clientUpdated: {
|
||||||
files: [
|
files: [
|
||||||
'public/src/**/*.js',
|
'public/src/**/*.js',
|
||||||
'public/vendor/**/*.js',
|
|
||||||
...clientUpdated,
|
...clientUpdated,
|
||||||
|
'!node_modules/nodebb-*/node_modules/**',
|
||||||
'node_modules/benchpressjs/build/benchpress.js',
|
'node_modules/benchpressjs/build/benchpress.js',
|
||||||
|
'!node_modules/nodebb-*/.git/**',
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serverUpdated: {
|
serverUpdated: {
|
||||||
files: [
|
files: ['*.js', 'install/*.js', 'src/**/*.js', '!src/upgrades/**'],
|
||||||
'app.js',
|
|
||||||
'install/*.js',
|
|
||||||
'src/**/*.js',
|
|
||||||
'public/src/modules/translator.common.js',
|
|
||||||
'public/src/modules/helpers.common.js',
|
|
||||||
'public/src/utils.common.js',
|
|
||||||
serverUpdated,
|
|
||||||
'!src/upgrades/**',
|
|
||||||
],
|
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
templatesUpdated: {
|
templatesUpdated: {
|
||||||
files: [
|
files: [
|
||||||
'src/views/**/*.tpl',
|
'src/views/**/*.tpl',
|
||||||
...templatesUpdated,
|
...templatesUpdated,
|
||||||
|
'!node_modules/nodebb-*/node_modules/**',
|
||||||
|
'!node_modules/nodebb-*/.git/**',
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
langUpdated: {
|
langUpdated: {
|
||||||
@@ -116,86 +184,63 @@ module.exports = function (grunt) {
|
|||||||
'public/language/en-GB/*.json',
|
'public/language/en-GB/*.json',
|
||||||
'public/language/en-GB/**/*.json',
|
'public/language/en-GB/**/*.json',
|
||||||
...langUpdated,
|
...langUpdated,
|
||||||
|
'!node_modules/nodebb-*/node_modules/**',
|
||||||
|
'!node_modules/nodebb-*/.git/**',
|
||||||
|
'!node_modules/nodebb-*/plugin.json',
|
||||||
|
'!node_modules/nodebb-*/package.json',
|
||||||
|
'!node_modules/nodebb-*/theme.json',
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const build = require('./src/meta/build');
|
next();
|
||||||
if (!grunt.option('skip')) {
|
},
|
||||||
await build.build(true, { watch: true });
|
], done);
|
||||||
}
|
|
||||||
run();
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function run() {
|
|
||||||
if (worker) {
|
|
||||||
worker.kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
const execArgv = [];
|
|
||||||
const inspect = process.argv.find(a => a.startsWith('--inspect'));
|
|
||||||
|
|
||||||
if (inspect) {
|
|
||||||
execArgv.push(inspect);
|
|
||||||
}
|
|
||||||
|
|
||||||
worker = fork('app.js', args, {
|
|
||||||
env,
|
|
||||||
execArgv,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
grunt.task.run('init');
|
grunt.task.run('init');
|
||||||
|
|
||||||
grunt.event.removeAllListeners('watch');
|
env.NODE_ENV = 'development';
|
||||||
grunt.event.on('watch', (action, filepath, target) => {
|
|
||||||
let compiling;
|
if (grunt.option('skip')) {
|
||||||
if (target === 'styleUpdated') {
|
worker = fork('app.js', args, {
|
||||||
compiling = ['clientCSS', 'acpCSS'];
|
env: env,
|
||||||
} else if (target === 'clientUpdated') {
|
});
|
||||||
compiling = ['js'];
|
} else {
|
||||||
} else if (target === 'templatesUpdated') {
|
initWorker = fork('app.js', initArgs, {
|
||||||
compiling = ['tpl'];
|
env: env,
|
||||||
} else if (target === 'langUpdated') {
|
});
|
||||||
compiling = ['lang'];
|
|
||||||
} else if (target === 'serverUpdated') {
|
initWorker.on('exit', function () {
|
||||||
// empty require cache
|
worker = fork('app.js', args, {
|
||||||
const paths = ['./src/meta/build.js', './src/meta/index.js'];
|
env: env,
|
||||||
paths.forEach(p => delete require.cache[require.resolve(p)]);
|
});
|
||||||
return run();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
require('./src/meta/build').build(compiling, { webpack: false }, (err) => {
|
grunt.event.on('watch', update);
|
||||||
if (err) {
|
|
||||||
winston.error(err.stack);
|
|
||||||
}
|
|
||||||
if (worker) {
|
|
||||||
worker.send({ compiling: compiling });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function addBaseThemes(pluginList) {
|
function addBaseThemes(plugins, callback) {
|
||||||
let themeId = pluginList.find(p => p.includes('nodebb-theme-'));
|
const themeId = plugins.find(p => p.startsWith('nodebb-theme-'));
|
||||||
if (!themeId) {
|
if (!themeId) {
|
||||||
return pluginList;
|
return setImmediate(callback, null, plugins);
|
||||||
}
|
}
|
||||||
let baseTheme;
|
function getBaseRecursive(themeId) {
|
||||||
do {
|
|
||||||
try {
|
try {
|
||||||
baseTheme = require(`${themeId}/theme`).baseTheme;
|
const baseTheme = require(themeId + '/theme').baseTheme;
|
||||||
|
|
||||||
|
if (baseTheme) {
|
||||||
|
plugins.push(baseTheme);
|
||||||
|
getBaseRecursive(baseTheme);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseTheme) {
|
|
||||||
pluginList.push(baseTheme);
|
|
||||||
themeId = baseTheme;
|
|
||||||
}
|
}
|
||||||
} while (baseTheme);
|
|
||||||
return pluginList;
|
getBaseRecursive(themeId);
|
||||||
|
callback(null, plugins);
|
||||||
}
|
}
|
||||||
|
|||||||
46
README.md
@@ -1,15 +1,21 @@
|
|||||||
# 
|
# <img alt="NodeBB" src="http://i.imgur.com/mYxPPtB.png" />
|
||||||
|
|
||||||
[](https://github.com/NodeBB/NodeBB/actions/workflows/test.yaml)
|
[](https://travis-ci.org/NodeBB/NodeBB)
|
||||||
[](https://coveralls.io/github/NodeBB/NodeBB?branch=master)
|
[](https://coveralls.io/github/NodeBB/NodeBB?branch=master)
|
||||||
|
[](https://david-dm.org/nodebb/nodebb?path=install)
|
||||||
[](https://codeclimate.com/github/NodeBB/NodeBB)
|
[](https://codeclimate.com/github/NodeBB/NodeBB)
|
||||||
[](https://discord.gg/p6YKPXu7er)
|
|
||||||
|
|
||||||
[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and supports either Redis, MongoDB, or a PostgreSQL database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB takes the best of the modern web: real-time streaming discussions, mobile responsiveness, and rich RESTful read/write APIs, while staying true to the original bulletin board/forum format → categorical hierarchies, local user accounts, and asynchronous messaging.
|
[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB has many modern features out of the box such as social network integration and streaming discussions, while still making sure to be compatible with older browsers.
|
||||||
|
|
||||||
NodeBB by itself contains a "common core" of basic functionality, while additional functionality and integrations are enabled through the use of third-party plugins.
|
Additional functionality is enabled through the use of third-party plugins.
|
||||||
|
|
||||||
### [Try it now](//try.nodebb.org) | [Documentation](//docs.nodebb.org)
|
* [Demo & Meta Discussion](http://community.nodebb.org)
|
||||||
|
* [Documentation & Installation Instructions](http://docs.nodebb.org)
|
||||||
|
* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/)
|
||||||
|
* [NodeBB Blog](http://blog.nodebb.org)
|
||||||
|
* [Premium Hosting for NodeBB](http://www.nodebb.org/ "NodeBB")
|
||||||
|
* [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
|
||||||
|
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
@@ -24,24 +30,26 @@ NodeBB's theming engine is highly flexible and does not restrict your design cho
|
|||||||
[](http://i.imgur.com/LmHtPho.png)
|
[](http://i.imgur.com/LmHtPho.png)
|
||||||
[](http://i.imgur.com/paiJPJk.jpg)
|
[](http://i.imgur.com/paiJPJk.jpg)
|
||||||
|
|
||||||
Our minimalist "Harmony" theme gets you going right away, no coding experience required.
|
Our minimalist "Persona" theme gets you going right away, no coding experience required.
|
||||||
|
|
||||||
|
[](http://i.imgur.com/HwNEXGu.png)
|
||||||
|
[](http://i.imgur.com/II1byYs.png)
|
||||||
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## How can I follow along/contribute?
|
## How can I follow along/contribute?
|
||||||
|
|
||||||
* If you are a developer, feel free to check out the source and submit pull requests. We also have a wide array of [plugins](http://community.nodebb.org/category/7/nodebb-plugins) which would be a great starting point for learning the codebase.
|
* If you are a developer, feel free to check out the source and submit pull requests. We also have a wide array of [plugins](http://community.nodebb.org/category/7/nodebb-plugins) which would be a great starting point for learning the codebase.
|
||||||
* If you are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extension of the base templates as well as styling via SCSS or CSS. NodeBB's base theme utilizes [Bootstrap 5](http://getbootstrap.com/) as a frontend toolkit.
|
* If you are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extension of the base templates as well as styling via LESS or CSS. NodeBB's base theme utilizes [Bootstrap 3](http://getbootstrap.com/) but themes can choose to use a different framework altogether.
|
||||||
* If you know languages other than English you can help us translate NodeBB. We use [Transifex](https://explore.transifex.com/nodebb/nodebb/) for internationalization.
|
* If you know languages other than English you can help us translate NodeBB. We use [Transifex](https://www.transifex.com/projects/p/nodebb/) for internationalization.
|
||||||
* Please don't forget to **like**, **follow**, and **star our repo**! Join our growing [community](http://community.nodebb.org) to keep up to date with the latest NodeBB development.
|
* Please don't forget to **like**, **follow**, and **star our repo**! Join our growing [community](http://community.nodebb.org) to keep up to date with the latest NodeBB development.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
NodeBB requires the following software to be installed:
|
NodeBB requires the following software to be installed:
|
||||||
|
|
||||||
* A version of Node.js at least 16 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
|
* A version of Node.js at least 8 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
|
||||||
* MongoDB, version 3.6 or greater **or** Redis, version 2.8.9 or greater
|
* Redis, version 2.8.9 or greater **or** MongoDB, version 2.6 or greater
|
||||||
* If you are using [clustering](https://docs.nodebb.org/configuring/scaling/) you need Redis installed and configured.
|
|
||||||
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
|
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -68,15 +76,3 @@ Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.node
|
|||||||
NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html).
|
NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html).
|
||||||
|
|
||||||
Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org.
|
Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org.
|
||||||
|
|
||||||
## More Information/Links
|
|
||||||
|
|
||||||
* [Demo](https://try.nodebb.org)
|
|
||||||
* [Developer Community](http://community.nodebb.org)
|
|
||||||
* [Documentation & Installation Instructions](https://docs.nodebb.org)
|
|
||||||
* [Help translate NodeBB](https://explore.transifex.com/nodebb/nodebb/)
|
|
||||||
* [NodeBB Blog](https://nodebb.org/blog)
|
|
||||||
* [Premium Hosting for NodeBB](https://www.nodebb.org/ "NodeBB")
|
|
||||||
* Unofficial IRC community – channel `#nodebb` on Libera.chat
|
|
||||||
* [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
|
|
||||||
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
|
||||||
|
|||||||
9
app.js
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
NodeBB - A better forum platform for the modern web
|
NodeBB - A better forum platform for the modern web
|
||||||
https://github.com/NodeBB/NodeBB/
|
https://github.com/NodeBB/NodeBB/
|
||||||
Copyright (C) 2013-2021 NodeBB Inc.
|
Copyright (C) 2013-2017 NodeBB Inc.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
require('./require-main');
|
require('./require-main');
|
||||||
|
|
||||||
const nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
|
|
||||||
nconf.argv().env({
|
nconf.argv().env({
|
||||||
separator: '__',
|
separator: '__',
|
||||||
});
|
});
|
||||||
@@ -32,7 +31,6 @@ const path = require('path');
|
|||||||
|
|
||||||
const file = require('./src/file');
|
const file = require('./src/file');
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|
||||||
global.env = process.env.NODE_ENV || 'production';
|
global.env = process.env.NODE_ENV || 'production';
|
||||||
|
|
||||||
// Alternate configuration file support
|
// Alternate configuration file support
|
||||||
@@ -41,7 +39,6 @@ const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'c
|
|||||||
const configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database'));
|
const configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database'));
|
||||||
|
|
||||||
const prestart = require('./src/prestart');
|
const prestart = require('./src/prestart');
|
||||||
|
|
||||||
prestart.loadConfig(configFile);
|
prestart.loadConfig(configFile);
|
||||||
prestart.setupWinston();
|
prestart.setupWinston();
|
||||||
prestart.versionCheck();
|
prestart.versionCheck();
|
||||||
@@ -49,7 +46,7 @@ winston.verbose('* using configuration stored in: %s', configFile);
|
|||||||
|
|
||||||
if (!process.send) {
|
if (!process.send) {
|
||||||
// If run using `node app`, log GNU copyright info along with server info
|
// If run using `node app`, log GNU copyright info along with server info
|
||||||
winston.info(`NodeBB v${nconf.get('version')} Copyright (C) 2013-${(new Date()).getFullYear()} NodeBB Inc.`);
|
winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-' + (new Date()).getFullYear() + ' NodeBB Inc.');
|
||||||
winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
|
winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||||
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
|
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||||
winston.info('');
|
winston.info('');
|
||||||
@@ -71,7 +68,7 @@ if (nconf.get('setup') || nconf.get('install')) {
|
|||||||
});
|
});
|
||||||
} else if (nconf.get('activate')) {
|
} else if (nconf.get('activate')) {
|
||||||
require('./src/cli/manage').activate(nconf.get('activate'));
|
require('./src/cli/manage').activate(nconf.get('activate'));
|
||||||
} else if (nconf.get('plugins') && typeof nconf.get('plugins') !== 'object') {
|
} else if (nconf.get('plugins')) {
|
||||||
require('./src/cli/manage').listPlugins();
|
require('./src/cli/manage').listPlugins();
|
||||||
} else if (nconf.get('build')) {
|
} else if (nconf.get('build')) {
|
||||||
require('./src/cli/manage').build(nconf.get('build'));
|
require('./src/cli/manage').build(nconf.get('build'));
|
||||||
|
|||||||
@@ -1,26 +1,3 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = { extends: ['@commitlint/config-angular'] };
|
||||||
extends: ['@commitlint/config-angular'],
|
|
||||||
rules: {
|
|
||||||
'header-max-length': [1, 'always', 72],
|
|
||||||
'type-enum': [
|
|
||||||
2,
|
|
||||||
'always',
|
|
||||||
[
|
|
||||||
'breaking',
|
|
||||||
'build',
|
|
||||||
'chore',
|
|
||||||
'ci',
|
|
||||||
'docs',
|
|
||||||
'feat',
|
|
||||||
'fix',
|
|
||||||
'perf',
|
|
||||||
'refactor',
|
|
||||||
'revert',
|
|
||||||
'style',
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"description": "Announcements regarding our community",
|
"description": "Announcements regarding our community",
|
||||||
"descriptionParsed": "<p>Announcements regarding our community</p>\n",
|
"descriptionParsed": "<p>Announcements regarding our community</p>\n",
|
||||||
"bgColor": "#fda34b",
|
"bgColor": "#fda34b",
|
||||||
"color": "#ffffff",
|
"color": "#fff",
|
||||||
"icon" : "fa-bullhorn",
|
"icon" : "fa-bullhorn",
|
||||||
"order": 1
|
"order": 1
|
||||||
},
|
},
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"description": "A place to talk about whatever you want",
|
"description": "A place to talk about whatever you want",
|
||||||
"descriptionParsed": "<p>A place to talk about whatever you want</p>\n",
|
"descriptionParsed": "<p>A place to talk about whatever you want</p>\n",
|
||||||
"bgColor": "#59b3d0",
|
"bgColor": "#59b3d0",
|
||||||
"color": "#ffffff",
|
"color": "#fff",
|
||||||
"icon" : "fa-comments-o",
|
"icon" : "fa-comments-o",
|
||||||
"order": 2
|
"order": 2
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"description": "Blog posts from individual members",
|
"description": "Blog posts from individual members",
|
||||||
"descriptionParsed": "<p>Blog posts from individual members</p>\n",
|
"descriptionParsed": "<p>Blog posts from individual members</p>\n",
|
||||||
"bgColor": "#86ba4b",
|
"bgColor": "#86ba4b",
|
||||||
"color": "#ffffff",
|
"color": "#fff",
|
||||||
"icon" : "fa-newspaper-o",
|
"icon" : "fa-newspaper-o",
|
||||||
"order": 4
|
"order": 4
|
||||||
},
|
},
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"description": "Got a question? Ask away!",
|
"description": "Got a question? Ask away!",
|
||||||
"descriptionParsed": "<p>Got a question? Ask away!</p>\n",
|
"descriptionParsed": "<p>Got a question? Ask away!</p>\n",
|
||||||
"bgColor": "#e95c5a",
|
"bgColor": "#e95c5a",
|
||||||
"color": "#ffffff",
|
"color": "#fff",
|
||||||
"icon" : "fa-question",
|
"icon" : "fa-question",
|
||||||
"order": 3
|
"order": 3
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
"defaultLang": "en-GB",
|
"defaultLang": "en-GB",
|
||||||
"loginDays": 14,
|
"loginDays": 14,
|
||||||
"loginSeconds": 0,
|
"loginSeconds": 0,
|
||||||
"sessionDuration": 0,
|
|
||||||
"loginAttempts": 5,
|
"loginAttempts": 5,
|
||||||
"lockoutDuration": 60,
|
"lockoutDuration": 60,
|
||||||
"adminReloginDuration": 60,
|
"adminReloginDuration": 60,
|
||||||
@@ -15,45 +14,37 @@
|
|||||||
"newbiePostEditDuration": 3600,
|
"newbiePostEditDuration": 3600,
|
||||||
"postDeleteDuration": 0,
|
"postDeleteDuration": 0,
|
||||||
"enablePostHistory": 1,
|
"enablePostHistory": 1,
|
||||||
"topicBacklinks": 1,
|
"postCacheSize": 10485760,
|
||||||
"postCacheSize": 20971520,
|
|
||||||
"disableChat": 0,
|
"disableChat": 0,
|
||||||
"chatEditDuration": 0,
|
"chatEditDuration": 0,
|
||||||
"chatDeleteDuration": 0,
|
"chatDeleteDuration": 0,
|
||||||
"chatMessageDelay": 200,
|
"chatMessageDelay": 200,
|
||||||
"notificationSendDelay": 60,
|
|
||||||
"newbiePostDelayThreshold": 3,
|
"newbiePostDelayThreshold": 3,
|
||||||
"postQueue": 0,
|
|
||||||
"postQueueReputationThreshold": 0,
|
"postQueueReputationThreshold": 0,
|
||||||
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"],
|
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"],
|
||||||
"groupsExemptFromMaintenanceMode": ["administrators", "Global Moderators"],
|
|
||||||
"minimumPostLength": 8,
|
"minimumPostLength": 8,
|
||||||
"maximumPostLength": 32767,
|
"maximumPostLength": 32767,
|
||||||
"systemTags": "",
|
|
||||||
"minimumTagsPerTopic": 0,
|
"minimumTagsPerTopic": 0,
|
||||||
"maximumTagsPerTopic": 5,
|
"maximumTagsPerTopic": 5,
|
||||||
"minimumTagLength": 3,
|
"minimumTagLength": 3,
|
||||||
"maximumTagLength": 15,
|
"maximumTagLength": 15,
|
||||||
"undoTimeout": 10000,
|
"allowTopicsThumbnail": 0,
|
||||||
"allowTopicsThumbnail": 1,
|
|
||||||
"registrationType": "normal",
|
"registrationType": "normal",
|
||||||
"registrationApprovalType": "normal",
|
"registrationApprovalType": "normal",
|
||||||
"allowAccountDelete": 1,
|
"allowAccountDelete": 1,
|
||||||
|
"allowFileUploads": 0,
|
||||||
"privateUploads": 0,
|
"privateUploads": 0,
|
||||||
"allowedFileExtensions": "png,jpg,bmp,txt",
|
"allowedFileExtensions": "png,jpg,bmp",
|
||||||
"uploadRateLimitThreshold": 10,
|
|
||||||
"uploadRateLimitCooldown": 60,
|
|
||||||
"allowUserHomePage": 1,
|
"allowUserHomePage": 1,
|
||||||
"allowMultipleBadges": 0,
|
"allowMultipleBadges": 0,
|
||||||
"maximumFileSize": 2048,
|
"maximumFileSize": 2048,
|
||||||
"stripEXIFData": 1,
|
"stripEXIFData": 1,
|
||||||
"orphanExpiryDays": 0,
|
|
||||||
"resizeImageWidthThreshold": 2000,
|
"resizeImageWidthThreshold": 2000,
|
||||||
"resizeImageWidth": 760,
|
"resizeImageWidth": 760,
|
||||||
"rejectImageWidth": 5000,
|
"rejectImageWidth": 5000,
|
||||||
"rejectImageHeight": 5000,
|
"rejectImageHeight": 5000,
|
||||||
"resizeImageQuality": 80,
|
"resizeImageQuality": 80,
|
||||||
"topicThumbSize": 512,
|
"topicThumbSize": 120,
|
||||||
"minimumTitleLength": 3,
|
"minimumTitleLength": 3,
|
||||||
"maximumTitleLength": 255,
|
"maximumTitleLength": 255,
|
||||||
"minimumUsernameLength": 2,
|
"minimumUsernameLength": 2,
|
||||||
@@ -69,10 +60,10 @@
|
|||||||
"profileImageDimension": 200,
|
"profileImageDimension": 200,
|
||||||
"profile:convertProfileImageToPNG": 0,
|
"profile:convertProfileImageToPNG": 0,
|
||||||
"profile:keepAllUserImages": 0,
|
"profile:keepAllUserImages": 0,
|
||||||
|
"requireEmailConfirmation": 0,
|
||||||
"gdpr_enabled": 1,
|
"gdpr_enabled": 1,
|
||||||
"allowProfileImageUploads": 1,
|
"allowProfileImageUploads": 1,
|
||||||
"teaserPost": "last-reply",
|
"teaserPost": "last-reply",
|
||||||
"showPostPreviewsOnHover": 1,
|
|
||||||
"allowPrivateGroups": 1,
|
"allowPrivateGroups": 1,
|
||||||
"unreadCutoff": 2,
|
"unreadCutoff": 2,
|
||||||
"bookmarkThreshold": 5,
|
"bookmarkThreshold": 5,
|
||||||
@@ -80,34 +71,19 @@
|
|||||||
"reputation:disabled": 0,
|
"reputation:disabled": 0,
|
||||||
"downvote:disabled": 0,
|
"downvote:disabled": 0,
|
||||||
"disableSignatures": 0,
|
"disableSignatures": 0,
|
||||||
"signatures:hideDuplicates": 0,
|
|
||||||
"upvotesPerDay": 20,
|
|
||||||
"upvotesPerUserPerDay": 6,
|
|
||||||
"downvotesPerDay": 10,
|
|
||||||
"downvotesPerUserPerDay": 3,
|
|
||||||
"min:rep:chat": 0,
|
|
||||||
"min:rep:downvote": 0,
|
"min:rep:downvote": 0,
|
||||||
"min:rep:upvote": 0,
|
|
||||||
"min:rep:flag": 0,
|
"min:rep:flag": 0,
|
||||||
"min:rep:profile-picture": 0,
|
"min:rep:profile-picture": 0,
|
||||||
"min:rep:cover-picture": 0,
|
"min:rep:cover-picture": 0,
|
||||||
"min:rep:website": 0,
|
"min:rep:website": 0,
|
||||||
"min:rep:aboutme": 0,
|
"min:rep:aboutme": 0,
|
||||||
"min:rep:signature": 0,
|
"min:rep:signature": 0,
|
||||||
"flags:limitPerTarget": 0,
|
|
||||||
"flags:autoFlagOnDownvoteThreshold": 0,
|
|
||||||
"flags:actionOnResolve": "rescind",
|
|
||||||
"flags:actionOnReject": "rescind",
|
|
||||||
"notificationType_upvote": "notification",
|
"notificationType_upvote": "notification",
|
||||||
"notificationType_new-topic": "notification",
|
"notificationType_new-topic": "notification",
|
||||||
"notificationType_new-reply": "notification",
|
"notificationType_new-reply": "notification",
|
||||||
"notificationType_post-edit": "notification",
|
|
||||||
"notificationType_follow": "notification",
|
"notificationType_follow": "notification",
|
||||||
"notificationType_new-chat": "notification",
|
"notificationType_new-chat": "notification",
|
||||||
"notificationType_new-group-chat": "notification",
|
|
||||||
"notificationType_group-invite": "notification",
|
"notificationType_group-invite": "notification",
|
||||||
"notificationType_group-leave": "notification",
|
|
||||||
"notificationType_group-request-membership": "notification",
|
|
||||||
"notificationType_mention": "notification",
|
"notificationType_mention": "notification",
|
||||||
"notificationType_new-register": "notification",
|
"notificationType_new-register": "notification",
|
||||||
"notificationType_post-queue": "notification",
|
"notificationType_post-queue": "notification",
|
||||||
@@ -118,16 +94,11 @@
|
|||||||
"maxPostsPerPage": 20,
|
"maxPostsPerPage": 20,
|
||||||
"topicsPerPage": 20,
|
"topicsPerPage": 20,
|
||||||
"postsPerPage": 20,
|
"postsPerPage": 20,
|
||||||
"categoriesPerPage": 50,
|
|
||||||
"userSearchResultsPerPage": 50,
|
"userSearchResultsPerPage": 50,
|
||||||
"searchDefaultSortBy": "relevance",
|
|
||||||
"searchDefaultIn": "titlesposts",
|
|
||||||
"searchDefaultInQuick": "titles",
|
|
||||||
"maximumGroupNameLength": 255,
|
"maximumGroupNameLength": 255,
|
||||||
"maximumGroupTitleLength": 40,
|
"maximumGroupTitleLength": 40,
|
||||||
"preventTopicDeleteAfterReplies": 0,
|
"preventTopicDeleteAfterReplies": 0,
|
||||||
"feeds:disableSitemap": 0,
|
"feeds:disableSitemap": 0,
|
||||||
"feeds:disableRSS": 0,
|
|
||||||
"sitemapTopics": 500,
|
"sitemapTopics": 500,
|
||||||
"maintenanceMode": 0,
|
"maintenanceMode": 0,
|
||||||
"maintenanceModeStatus": 503,
|
"maintenanceModeStatus": 503,
|
||||||
@@ -135,33 +106,17 @@
|
|||||||
"maximumInvites": 0,
|
"maximumInvites": 0,
|
||||||
"username:disableEdit": 0,
|
"username:disableEdit": 0,
|
||||||
"email:disableEdit": 0,
|
"email:disableEdit": 0,
|
||||||
"email:smtpTransport:pool": 0,
|
"email:sendmail:rateLimit": 2,
|
||||||
|
"email:sendmail:rateDelta": 1000,
|
||||||
"hideFullname": 0,
|
"hideFullname": 0,
|
||||||
"hideEmail": 0,
|
|
||||||
"showFullnameAsDisplayName": 0,
|
|
||||||
"allowGuestHandles": 0,
|
"allowGuestHandles": 0,
|
||||||
"guestsIncrementTopicViews": 1,
|
|
||||||
"allowGuestReplyNotifications": 1,
|
|
||||||
"incrementTopicViewsInterval": 60,
|
|
||||||
"recentMaxTopics": 200,
|
|
||||||
"disableRecentCategoryFilter": 0,
|
"disableRecentCategoryFilter": 0,
|
||||||
"maximumRelatedTopics": 0,
|
"maximumRelatedTopics": 0,
|
||||||
"disableEmailSubscriptions": 0,
|
"disableEmailSubscriptions": 0,
|
||||||
"emailConfirmInterval": 10,
|
"emailConfirmInterval": 10,
|
||||||
"emailConfirmExpiry": 24,
|
|
||||||
"removeEmailNotificationImages": 0,
|
|
||||||
"sendValidationEmail": 1,
|
|
||||||
"includeUnverifiedEmails": 0,
|
|
||||||
"emailPrompt": 1,
|
|
||||||
"sendEmailToBanned": 0,
|
|
||||||
"requireEmailAddress": 0,
|
|
||||||
"inviteExpiration": 7,
|
"inviteExpiration": 7,
|
||||||
"dailyDigestFreq": "off",
|
|
||||||
"digestHour": 17,
|
"digestHour": 17,
|
||||||
"passwordExpiryDays": 0,
|
"passwordExpiryDays": 0,
|
||||||
"cross-origin-embedder-policy": 0,
|
|
||||||
"cross-origin-opener-policy": "same-origin",
|
|
||||||
"cross-origin-resource-policy": "same-origin",
|
|
||||||
"hsts-maxage": 31536000,
|
"hsts-maxage": 31536000,
|
||||||
"hsts-subdomains": 0,
|
"hsts-subdomains": 0,
|
||||||
"hsts-preload": 0,
|
"hsts-preload": 0,
|
||||||
@@ -173,15 +128,5 @@
|
|||||||
"timeagoCutoff": 30,
|
"timeagoCutoff": 30,
|
||||||
"necroThreshold": 7,
|
"necroThreshold": 7,
|
||||||
"categoryWatchState": "watching",
|
"categoryWatchState": "watching",
|
||||||
"submitPluginUsage": 1,
|
"submitPluginUsage": 1
|
||||||
"showAverageApprovalTime": 1,
|
|
||||||
"autoApproveTime": 0,
|
|
||||||
"maxUserSessions": 10,
|
|
||||||
"useCompression": 0,
|
|
||||||
"updateUrlWithPostIndex": 1,
|
|
||||||
"composer:showHelpTab": 1,
|
|
||||||
"composer:allowPluginHelp": 1,
|
|
||||||
"maxReconnectionAttempts": 5,
|
|
||||||
"reconnectionDelay": 1500,
|
|
||||||
"disableCustomUserSkins": 0
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"widget": "html",
|
"widget": "html",
|
||||||
"data" : {
|
"data" : {
|
||||||
"html": "<footer id=\"footer\" class=\"container footer d-flex flex-column align-items-center gap-1 mb-2\">\n\t<span>Powered by <a class=\"link-secondary text-decoration-underline\" target=\"_blank\" href=\"https://nodebb.org\">NodeBB</a></span>\n\t<span><a class=\"link-secondary\" target=\"_blank\" href=\"//github.com/NodeBB/NodeBB/graphs/contributors\"><i class=\"fa fa-users\"></i> <span class=\"text-decoration-underline\">Contributors<span></a></span>\n</footer>",
|
"html": "<footer id=\"footer\" class=\"container footer\">\r\n\t<div>\r\n\t\tPowered by <a target=\"_blank\" href=\"https://nodebb.org\">NodeBB</a> | <a target=\"_blank\" href=\"//github.com/NodeBB/NodeBB/graphs/contributors\">Contributors</a>\r\n\t</div>\r\n</footer>",
|
||||||
"title":"",
|
"title":"",
|
||||||
"container":""
|
"container":""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"title": "[[global:header.categories]]",
|
"title": "[[global:header.categories]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-list",
|
"iconClass": "fa-list",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.categories]]"
|
"text": "[[global:header.categories]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"title": "[[global:header.unread]]",
|
"title": "[[global:header.unread]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-inbox",
|
"iconClass": "fa-inbox",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.unread]]",
|
"text": "[[global:header.unread]]",
|
||||||
"groups": ["registered-users"]
|
"groups": ["registered-users"]
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"title": "[[global:header.recent]]",
|
"title": "[[global:header.recent]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-clock-o",
|
"iconClass": "fa-clock-o",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.recent]]"
|
"text": "[[global:header.recent]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"title": "[[global:header.tags]]",
|
"title": "[[global:header.tags]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-tags",
|
"iconClass": "fa-tags",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.tags]]"
|
"text": "[[global:header.tags]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"title": "[[global:header.popular]]",
|
"title": "[[global:header.popular]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-fire",
|
"iconClass": "fa-fire",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.popular]]"
|
"text": "[[global:header.popular]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"title": "[[global:header.users]]",
|
"title": "[[global:header.users]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-user",
|
"iconClass": "fa-user",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.users]]"
|
"text": "[[global:header.users]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
"title": "[[global:header.groups]]",
|
"title": "[[global:header.groups]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-group",
|
"iconClass": "fa-group",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.groups]]"
|
"text": "[[global:header.groups]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -62,8 +62,11 @@
|
|||||||
"title": "[[global:header.admin]]",
|
"title": "[[global:header.admin]]",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"iconClass": "fa-cogs",
|
"iconClass": "fa-cogs",
|
||||||
"textClass": "d-lg-none",
|
"textClass": "visible-xs-inline",
|
||||||
"text": "[[global:header.admin]]",
|
"text": "[[global:header.admin]]",
|
||||||
"groups": ["administrators"]
|
"groups": ["administrators"],
|
||||||
|
"properties": {
|
||||||
|
"targetBlank": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
### Welcome to your brand new NodeBB forum!
|
# Welcome to your brand new NodeBB forum!
|
||||||
|
|
||||||
This is what a topic and post looks like. As an administrator, you can edit the post\'s title and content.
|
This is what a topic and post looks like. As an administrator, you can edit the post\'s title and content.
|
||||||
To customise your forum, go to the [Administrator Control Panel](../../admin). You can modify all aspects of your forum there, including installation of third-party plugins.
|
To customise your forum, go to the [Administrator Control Panel](../../admin). You can modify all aspects of your forum there, including installation of third-party plugins.
|
||||||
|
|
||||||
#### Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
* [NodeBB Documentation](https://docs.nodebb.org)
|
* [NodeBB Documentation](https://docs.nodebb.org)
|
||||||
* [Community Support Forum](https://community.nodebb.org)
|
* [Community Support Forum](https://community.nodebb.org)
|
||||||
|
|||||||
@@ -1,47 +1,58 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const prompt = require('prompt');
|
var async = require('async');
|
||||||
const winston = require('winston');
|
var prompt = require('prompt');
|
||||||
|
var winston = require('winston');
|
||||||
|
|
||||||
const questions = {
|
var questions = {
|
||||||
redis: require('../src/database/redis').questions,
|
redis: require('../src/database/redis').questions,
|
||||||
mongo: require('../src/database/mongo').questions,
|
mongo: require('../src/database/mongo').questions,
|
||||||
postgres: require('../src/database/postgres').questions,
|
postgres: require('../src/database/postgres').questions,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = async function (config) {
|
module.exports = function (config, callback) {
|
||||||
winston.info(`\nNow configuring ${config.database} database:`);
|
async.waterfall([
|
||||||
const databaseConfig = await getDatabaseConfig(config);
|
function (next) {
|
||||||
return saveDatabaseConfig(config, databaseConfig);
|
winston.info('\nNow configuring ' + config.database + ' database:');
|
||||||
|
getDatabaseConfig(config, next);
|
||||||
|
},
|
||||||
|
function (databaseConfig, next) {
|
||||||
|
saveDatabaseConfig(config, databaseConfig, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getDatabaseConfig(config) {
|
function getDatabaseConfig(config, callback) {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error('invalid config, aborted');
|
return callback(new Error('aborted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.database === 'redis') {
|
if (config.database === 'redis') {
|
||||||
if (config['redis:host'] && config['redis:port']) {
|
if (config['redis:host'] && config['redis:port']) {
|
||||||
return config;
|
callback(null, config);
|
||||||
|
} else {
|
||||||
|
prompt.get(questions.redis, callback);
|
||||||
}
|
}
|
||||||
return await prompt.get(questions.redis);
|
|
||||||
} else if (config.database === 'mongo') {
|
} else if (config.database === 'mongo') {
|
||||||
if ((config['mongo:host'] && config['mongo:port']) || config['mongo:uri']) {
|
if ((config['mongo:host'] && config['mongo:port']) || config['mongo:uri']) {
|
||||||
return config;
|
callback(null, config);
|
||||||
|
} else {
|
||||||
|
prompt.get(questions.mongo, callback);
|
||||||
}
|
}
|
||||||
return await prompt.get(questions.mongo);
|
|
||||||
} else if (config.database === 'postgres') {
|
} else if (config.database === 'postgres') {
|
||||||
if (config['postgres:host'] && config['postgres:port']) {
|
if (config['postgres:host'] && config['postgres:port']) {
|
||||||
return config;
|
callback(null, config);
|
||||||
|
} else {
|
||||||
|
prompt.get(questions.postgres, callback);
|
||||||
}
|
}
|
||||||
return await prompt.get(questions.postgres);
|
} else {
|
||||||
|
return callback(new Error('unknown database : ' + config.database));
|
||||||
}
|
}
|
||||||
throw new Error(`unknown database : ${config.database}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveDatabaseConfig(config, databaseConfig) {
|
function saveDatabaseConfig(config, databaseConfig, callback) {
|
||||||
if (!databaseConfig) {
|
if (!databaseConfig) {
|
||||||
throw new Error('invalid config, aborted');
|
return callback(new Error('aborted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate redis properties into redis object
|
// Translate redis properties into redis object
|
||||||
@@ -75,13 +86,13 @@ function saveDatabaseConfig(config, databaseConfig) {
|
|||||||
ssl: databaseConfig['postgres:ssl'],
|
ssl: databaseConfig['postgres:ssl'],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`unknown database : ${config.database}`);
|
return callback(new Error('unknown database : ' + config.database));
|
||||||
}
|
}
|
||||||
|
|
||||||
const allQuestions = questions.redis.concat(questions.mongo).concat(questions.postgres);
|
var allQuestions = questions.redis.concat(questions.mongo).concat(questions.postgres);
|
||||||
for (let x = 0; x < allQuestions.length; x += 1) {
|
for (var x = 0; x < allQuestions.length; x += 1) {
|
||||||
delete config[allQuestions[x].name];
|
delete config[allQuestions[x].name];
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
callback(null, config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"name": "nodebb",
|
"name": "nodebb",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"description": "NodeBB Forum",
|
"description": "NodeBB Forum",
|
||||||
"version": "3.2.1",
|
"version": "1.13.0",
|
||||||
"homepage": "https://www.nodebb.org",
|
"homepage": "http://www.nodebb.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/NodeBB/NodeBB/"
|
"url": "https://github.com/NodeBB/NodeBB/"
|
||||||
@@ -12,177 +12,153 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node loader.js",
|
"start": "node loader.js",
|
||||||
"lint": "eslint --cache ./nodebb .",
|
"lint": "eslint --cache ./nodebb .",
|
||||||
|
"pretest": "npm run lint",
|
||||||
"test": "nyc --reporter=html --reporter=text-summary mocha",
|
"test": "nyc --reporter=html --reporter=text-summary mocha",
|
||||||
"coverage": "nyc report --reporter=text-lcov > ./coverage/lcov.info",
|
|
||||||
"coveralls": "nyc report --reporter=text-lcov | coveralls && rm -r coverage"
|
"coveralls": "nyc report --reporter=text-lcov | coveralls && rm -r coverage"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"husky": {
|
||||||
"exclude": [
|
"hooks": {
|
||||||
"src/upgrades/*",
|
"pre-commit": "lint-staged",
|
||||||
"test/*"
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.js": [
|
"*.js": [
|
||||||
"eslint --fix"
|
"eslint --fix",
|
||||||
|
"git add"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adactive/bootstrap-tagsinput": "0.8.2",
|
"ace-builds": "^1.2.9",
|
||||||
"@fontsource/inter": "5.0.3",
|
"archiver": "^3.0.0",
|
||||||
"@fontsource/poppins": "5.0.3",
|
"async": "^3.0.1",
|
||||||
"@isaacs/ttlcache": "1.4.0",
|
"autoprefixer": "^9.4.6",
|
||||||
"@popperjs/core": "2.11.8",
|
|
||||||
"ace-builds": "1.23.1",
|
|
||||||
"archiver": "5.3.1",
|
|
||||||
"async": "3.2.4",
|
|
||||||
"autoprefixer": "10.4.14",
|
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"benchpressjs": "2.5.1",
|
"benchpressjs": "^2.0.0",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "^1.18.2",
|
||||||
"bootbox": "6.0.0",
|
"bootstrap": "^3.4.0",
|
||||||
"bootstrap": "5.2.3",
|
"bootswatch": "git://github.com/thomaspark/bootswatch.git#c41a8f066feb8950c6f9c6bcf5a3c37d1085404e",
|
||||||
"bootswatch": "5.2.3",
|
"chart.js": "^2.7.1",
|
||||||
"chalk": "4.1.2",
|
"cli-graph": "^3.2.2",
|
||||||
"chart.js": "2.9.4",
|
"clipboard": "^2.0.1",
|
||||||
"cli-graph": "3.2.2",
|
"colors": "^1.1.2",
|
||||||
"clipboard": "2.0.11",
|
"commander": "^3.0.0",
|
||||||
"colors": "1.4.0",
|
"compression": "^1.7.1",
|
||||||
"commander": "11.0.0",
|
"connect-ensure-login": "^0.1.1",
|
||||||
"compare-versions": "5.0.3",
|
"connect-flash": "^0.1.1",
|
||||||
"compression": "1.7.4",
|
"connect-mongo": "3.2.0",
|
||||||
"connect-flash": "0.1.1",
|
"connect-multiparty": "^2.1.0",
|
||||||
"connect-mongo": "5.0.0",
|
"connect-pg-simple": "^6.0.0",
|
||||||
"connect-multiparty": "2.2.0",
|
"connect-redis": "4.0.3",
|
||||||
"connect-pg-simple": "9.0.0",
|
"cookie-parser": "^1.4.3",
|
||||||
"connect-redis": "7.1.0",
|
"cron": "^1.3.0",
|
||||||
"cookie-parser": "1.4.6",
|
"cropperjs": "^1.2.2",
|
||||||
"cron": "2.3.1",
|
"csurf": "^1.9.0",
|
||||||
"cropperjs": "1.5.13",
|
"daemon": "^1.1.0",
|
||||||
"csrf-sync": "4.0.1",
|
"diff": "^4.0.1",
|
||||||
"daemon": "1.1.0",
|
"express": "^4.16.2",
|
||||||
"diff": "5.1.0",
|
"express-session": "^1.15.6",
|
||||||
"esbuild": "0.18.10",
|
"express-useragent": "^1.0.12",
|
||||||
"express": "4.18.2",
|
"graceful-fs": "^4.1.11",
|
||||||
"express-session": "1.17.3",
|
"helmet": "^3.11.0",
|
||||||
"express-useragent": "1.0.15",
|
"html-to-text": "^5.0.0",
|
||||||
"file-loader": "6.2.0",
|
"ipaddr.js": "^1.5.4",
|
||||||
"fs-extra": "11.1.1",
|
"jquery": "^3.2.1",
|
||||||
"graceful-fs": "4.2.11",
|
"jsesc": "2.5.2",
|
||||||
"helmet": "7.0.0",
|
"json-2-csv": "^3.0.0",
|
||||||
"html-to-text": "9.0.5",
|
"jsonwebtoken": "^8.4.0",
|
||||||
"ipaddr.js": "2.1.0",
|
"less": "^3.10.3",
|
||||||
"jquery": "3.7.0",
|
"lodash": "^4.17.15",
|
||||||
"jquery-deserialize": "2.0.0",
|
"logrotate-stream": "^0.2.5",
|
||||||
"jquery-form": "4.3.0",
|
"lru-cache": "5.1.1",
|
||||||
"jquery-serializeobject": "1.0.0",
|
"material-design-lite": "^1.3.0",
|
||||||
"jquery-ui": "1.13.2",
|
"mime": "^2.2.0",
|
||||||
"jsesc": "3.0.2",
|
"mkdirp": "^0.5.1",
|
||||||
"json2csv": "5.0.7",
|
"mongodb": "3.4.0",
|
||||||
"jsonwebtoken": "9.0.0",
|
"morgan": "^1.9.1",
|
||||||
"less": "4.1.3",
|
"mousetrap": "^1.6.1",
|
||||||
"lodash": "4.17.21",
|
"mubsub-nbb": "^1.5.1",
|
||||||
"logrotate-stream": "0.2.9",
|
"nconf": "^0.10.0",
|
||||||
"lru-cache": "10.0.0",
|
"nodebb-plugin-composer-default": "6.3.20",
|
||||||
"mime": "3.0.0",
|
"nodebb-plugin-dbsearch": "4.0.7",
|
||||||
"mkdirp": "3.0.1",
|
"nodebb-plugin-emoji": "^3.0.0",
|
||||||
"mongodb": "5.6.0",
|
"nodebb-plugin-emoji-android": "2.0.0",
|
||||||
"morgan": "1.10.0",
|
"nodebb-plugin-markdown": "8.11.0",
|
||||||
"mousetrap": "1.6.5",
|
"nodebb-plugin-mentions": "2.7.3",
|
||||||
"multiparty": "4.2.3",
|
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||||
"nconf": "0.12.0",
|
"nodebb-plugin-spam-be-gone": "0.6.7",
|
||||||
"nodebb-plugin-2factor": "7.1.3",
|
"nodebb-rewards-essentials": "0.1.2",
|
||||||
"nodebb-plugin-composer-default": "10.2.4",
|
"nodebb-theme-lavender": "5.0.11",
|
||||||
"nodebb-plugin-dbsearch": "6.1.0",
|
"nodebb-theme-persona": "10.1.30",
|
||||||
"nodebb-plugin-emoji": "5.1.3",
|
"nodebb-theme-slick": "1.2.28",
|
||||||
"nodebb-plugin-emoji-android": "4.0.0",
|
"nodebb-theme-vanilla": "11.1.12",
|
||||||
"nodebb-plugin-markdown": "12.1.4",
|
"nodebb-widget-essentials": "4.0.17",
|
||||||
"nodebb-plugin-mentions": "4.2.0",
|
"nodemailer": "^6.0.0",
|
||||||
"nodebb-plugin-ntfy": "1.0.15",
|
"passport": "^0.4.0",
|
||||||
"nodebb-plugin-spam-be-gone": "2.1.0",
|
|
||||||
"nodebb-rewards-essentials": "0.2.3",
|
|
||||||
"nodebb-theme-harmony": "1.0.65",
|
|
||||||
"nodebb-theme-lavender": "7.1.1",
|
|
||||||
"nodebb-theme-peace": "2.0.32",
|
|
||||||
"nodebb-theme-persona": "13.1.6",
|
|
||||||
"nodebb-widget-essentials": "7.0.13",
|
|
||||||
"nodemailer": "6.9.3",
|
|
||||||
"nprogress": "0.2.0",
|
|
||||||
"passport": "0.6.0",
|
|
||||||
"passport-http-bearer": "1.0.1",
|
|
||||||
"passport-local": "1.0.0",
|
"passport-local": "1.0.0",
|
||||||
"pg": "8.11.1",
|
"pg": "^7.4.0",
|
||||||
"pg-cursor": "2.10.1",
|
"pg-cursor": "^2.0.0",
|
||||||
"postcss": "8.4.24",
|
"postcss": "7.0.21",
|
||||||
"postcss-clean": "1.2.0",
|
"postcss-clean": "1.1.0",
|
||||||
"progress-webpack-plugin": "1.0.16",
|
"promise-polyfill": "^8.0.0",
|
||||||
"prompt": "1.3.0",
|
"prompt": "^1.0.0",
|
||||||
"ioredis": "5.3.2",
|
"redis": "2.8.0",
|
||||||
"request": "2.88.2",
|
"request": "2.88.0",
|
||||||
"request-promise-native": "1.0.9",
|
"rimraf": "3.0.0",
|
||||||
"rimraf": "5.0.1",
|
"rss": "^1.2.2",
|
||||||
"rss": "1.2.2",
|
"sanitize-html": "^1.16.3",
|
||||||
"rtlcss": "4.1.0",
|
"semver": "^7.0.0",
|
||||||
"sanitize-html": "2.11.0",
|
"serve-favicon": "^2.4.5",
|
||||||
"sass": "1.63.6",
|
"sharp": "0.23.4",
|
||||||
"semver": "7.5.3",
|
"sitemap": "^5.0.0",
|
||||||
"serve-favicon": "2.5.0",
|
"socket.io": "2.3.0",
|
||||||
"sharp": "0.32.1",
|
"socket.io-adapter-cluster": "^1.0.1",
|
||||||
"sitemap": "7.1.1",
|
"socket.io-adapter-mongo": "^2.0.4",
|
||||||
"slideout": "1.0.1",
|
"socket.io-adapter-postgres": "^1.2.1",
|
||||||
"socket.io": "4.7.1",
|
"socket.io-client": "2.3.0",
|
||||||
"socket.io-client": "4.7.1",
|
"socket.io-redis": "5.2.0",
|
||||||
"@socket.io/redis-adapter": "8.2.1",
|
"socketio-wildcard": "2.0.0",
|
||||||
"sortablejs": "1.15.0",
|
"spdx-license-list": "^6.0.0",
|
||||||
"spdx-license-list": "6.6.0",
|
"spider-detector": "2.0.0",
|
||||||
"spider-detector": "2.0.1",
|
"textcomplete": "^0.17.1",
|
||||||
"terser-webpack-plugin": "5.3.9",
|
"textcomplete.contenteditable": "^0.1.1",
|
||||||
"textcomplete": "0.18.2",
|
"toobusy-js": "^0.5.1",
|
||||||
"textcomplete.contenteditable": "0.1.1",
|
"uglify-es": "^3.3.9",
|
||||||
"timeago": "1.6.7",
|
"validator": "12.1.0",
|
||||||
"tinycon": "0.6.8",
|
"winston": "3.2.1",
|
||||||
"toobusy-js": "0.5.1",
|
"xml": "^1.0.1",
|
||||||
"validator": "13.9.0",
|
"xregexp": "^4.1.1",
|
||||||
"webpack": "5.88.0",
|
"zxcvbn": "^4.4.2"
|
||||||
"webpack-merge": "5.9.0",
|
|
||||||
"winston": "3.9.0",
|
|
||||||
"xml": "1.0.1",
|
|
||||||
"xregexp": "5.1.1",
|
|
||||||
"yargs": "17.7.2",
|
|
||||||
"zxcvbn": "4.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@apidevtools/swagger-parser": "10.1.0",
|
"@commitlint/cli": "8.2.0",
|
||||||
"@commitlint/cli": "17.6.6",
|
"@commitlint/config-angular": "8.2.0",
|
||||||
"@commitlint/config-angular": "17.6.6",
|
"coveralls": "3.0.9",
|
||||||
"coveralls": "3.1.1",
|
"eslint": "6.7.0",
|
||||||
"eslint": "8.43.0",
|
"eslint-config-airbnb-base": "14.0.0",
|
||||||
"eslint-config-nodebb": "0.2.1",
|
"eslint-plugin-import": "2.18.2",
|
||||||
"eslint-plugin-import": "2.27.5",
|
"grunt": "1.0.4",
|
||||||
"grunt": "1.6.1",
|
|
||||||
"grunt-contrib-watch": "1.1.0",
|
"grunt-contrib-watch": "1.1.0",
|
||||||
"husky": "8.0.3",
|
"husky": "3.1.0",
|
||||||
"jsdom": "22.1.0",
|
"jsdom": "15.2.1",
|
||||||
"lint-staged": "13.2.3",
|
"lint-staged": "9.4.2",
|
||||||
"mocha": "10.2.0",
|
"mocha": "6.2.2",
|
||||||
"mocha-lcov-reporter": "1.3.0",
|
"mocha-lcov-reporter": "1.3.0",
|
||||||
"mockdate": "3.0.5",
|
"nyc": "14.1.1",
|
||||||
"nyc": "15.1.0",
|
"smtp-server": "3.5.0"
|
||||||
"smtp-server": "3.12.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"sass-embedded": "1.63.6"
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"*/jquery": "3.7.0"
|
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/NodeBB/NodeBB/issues"
|
"url": "https://github.com/NodeBB/NodeBB/issues"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Andrew Rodrigues",
|
||||||
|
"email": "andrew@nodebb.org",
|
||||||
|
"url": "https://github.com/psychobunny"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Julian Lam",
|
"name": "Julian Lam",
|
||||||
"email": "julian@nodebb.org",
|
"email": "julian@nodebb.org",
|
||||||
|
|||||||
333
install/web.js
@@ -1,30 +1,27 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const winston = require('winston');
|
var winston = require('winston');
|
||||||
const express = require('express');
|
var express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
var bodyParser = require('body-parser');
|
||||||
const fs = require('fs');
|
var fs = require('fs');
|
||||||
const path = require('path');
|
var path = require('path');
|
||||||
const childProcess = require('child_process');
|
var childProcess = require('child_process');
|
||||||
|
var less = require('less');
|
||||||
|
var async = require('async');
|
||||||
|
var uglify = require('uglify-es');
|
||||||
|
var nconf = require('nconf');
|
||||||
|
var Benchpress = require('benchpressjs');
|
||||||
|
|
||||||
const webpack = require('webpack');
|
var app = express();
|
||||||
const nconf = require('nconf');
|
var server;
|
||||||
|
|
||||||
const Benchpress = require('benchpressjs');
|
var formats = [
|
||||||
const { mkdirp } = require('mkdirp');
|
|
||||||
const { paths } = require('../src/constants');
|
|
||||||
const sass = require('../src/utils').getSass();
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
let server;
|
|
||||||
|
|
||||||
const formats = [
|
|
||||||
winston.format.colorize(),
|
winston.format.colorize(),
|
||||||
];
|
];
|
||||||
|
|
||||||
const timestampFormat = winston.format((info) => {
|
const timestampFormat = winston.format((info) => {
|
||||||
const dateString = `${new Date().toISOString()} [${global.process.pid}]`;
|
var dateString = new Date().toISOString() + ' [' + global.process.pid + ']';
|
||||||
info.level = `${dateString} - ${info.level}`;
|
info.level = dateString + ' - ' + info.level;
|
||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
formats.push(timestampFormat());
|
formats.push(timestampFormat());
|
||||||
@@ -45,59 +42,55 @@ winston.configure({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const web = module.exports;
|
var web = module.exports;
|
||||||
let installing = false;
|
|
||||||
let success = false;
|
|
||||||
let error = false;
|
|
||||||
let launchUrl;
|
|
||||||
let timeStart = 0;
|
|
||||||
const totalTime = 1000 * 60 * 3;
|
|
||||||
|
|
||||||
|
var scripts = [
|
||||||
|
'node_modules/jquery/dist/jquery.js',
|
||||||
|
'public/vendor/xregexp/xregexp.js',
|
||||||
|
'public/vendor/xregexp/unicode/unicode-base.js',
|
||||||
|
'public/src/utils.js',
|
||||||
|
'public/src/installer/install.js',
|
||||||
|
'node_modules/zxcvbn/dist/zxcvbn.js',
|
||||||
|
];
|
||||||
|
|
||||||
const viewsDir = path.join(paths.baseDir, 'build/public/templates');
|
var installing = false;
|
||||||
|
var success = false;
|
||||||
|
var error = false;
|
||||||
|
var launchUrl;
|
||||||
|
|
||||||
web.install = async function (port) {
|
web.install = function (port) {
|
||||||
port = port || 4567;
|
port = port || 4567;
|
||||||
winston.info(`Launching web installer on port ${port}`);
|
winston.info('Launching web installer on port ' + port);
|
||||||
|
|
||||||
app.use(express.static('public', {}));
|
app.use(express.static('public', {}));
|
||||||
app.use('/assets', express.static(path.join(__dirname, '../build/public'), {}));
|
app.engine('tpl', function (filepath, options, callback) {
|
||||||
|
async.waterfall([
|
||||||
app.engine('tpl', (filepath, options, callback) => {
|
function (next) {
|
||||||
filepath = filepath.replace(/\.tpl$/, '.js');
|
fs.readFile(filepath, 'utf-8', next);
|
||||||
|
},
|
||||||
Benchpress.__express(filepath, options, callback);
|
function (buffer, next) {
|
||||||
|
Benchpress.compileParse(buffer.toString(), options, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
});
|
});
|
||||||
app.set('view engine', 'tpl');
|
app.set('view engine', 'tpl');
|
||||||
app.set('views', viewsDir);
|
app.set('views', path.join(__dirname, '../src/views'));
|
||||||
app.use(bodyParser.urlencoded({
|
app.use(bodyParser.urlencoded({
|
||||||
extended: true,
|
extended: true,
|
||||||
}));
|
}));
|
||||||
try {
|
|
||||||
await Promise.all([
|
async.parallel([compileLess, compileJS, copyCSS, loadDefaults], function (err) {
|
||||||
compileTemplate(),
|
if (err) {
|
||||||
compileSass(),
|
winston.error(err);
|
||||||
runWebpack(),
|
}
|
||||||
copyCSS(),
|
|
||||||
loadDefaults(),
|
|
||||||
]);
|
|
||||||
setupRoutes();
|
setupRoutes();
|
||||||
launchExpress(port);
|
launchExpress(port);
|
||||||
} catch (err) {
|
});
|
||||||
winston.error(err.stack);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function runWebpack() {
|
|
||||||
const util = require('util');
|
|
||||||
const webpackCfg = require('../webpack.installer');
|
|
||||||
const compiler = webpack(webpackCfg);
|
|
||||||
const webpackRun = util.promisify(compiler.run).bind(compiler);
|
|
||||||
await webpackRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
function launchExpress(port) {
|
function launchExpress(port) {
|
||||||
server = app.listen(port, () => {
|
server = app.listen(port, function () {
|
||||||
winston.info('Web installer listening on http://%s:%s', '0.0.0.0', port);
|
winston.info('Web installer listening on http://%s:%s', '0.0.0.0', port);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -105,40 +98,21 @@ function launchExpress(port) {
|
|||||||
function setupRoutes() {
|
function setupRoutes() {
|
||||||
app.get('/', welcome);
|
app.get('/', welcome);
|
||||||
app.post('/', install);
|
app.post('/', install);
|
||||||
app.get('/testdb', testDatabase);
|
app.post('/launch', launch);
|
||||||
app.get('/ping', ping);
|
app.get('/ping', ping);
|
||||||
app.get('/sping', ping);
|
app.get('/sping', ping);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testDatabase(req, res) {
|
|
||||||
let db;
|
|
||||||
try {
|
|
||||||
const keys = Object.keys(req.query);
|
|
||||||
const dbName = keys[0].split(':')[0];
|
|
||||||
db = require(`../src/database/${dbName}`);
|
|
||||||
|
|
||||||
const opts = {};
|
|
||||||
keys.forEach((key) => {
|
|
||||||
opts[key.replace(`${dbName}:`, '')] = req.query[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.init(opts);
|
|
||||||
const global = await db.getObject('global');
|
|
||||||
await db.close();
|
|
||||||
res.json({ success: 1, dbfull: !!global });
|
|
||||||
} catch (err) {
|
|
||||||
res.json({ error: err.stack });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ping(req, res) {
|
function ping(req, res) {
|
||||||
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
|
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
|
||||||
}
|
}
|
||||||
|
|
||||||
function welcome(req, res) {
|
function welcome(req, res) {
|
||||||
const dbs = ['mongo', 'redis', 'postgres'];
|
var dbs = ['redis', 'mongo', 'postgres'];
|
||||||
const databases = dbs.map((databaseName) => {
|
var databases = dbs.map(function (databaseName) {
|
||||||
const questions = require(`../src/database/${databaseName}`).questions.filter(question => question && !question.hideOnWebInstall);
|
var questions = require('../src/database/' + databaseName).questions.filter(function (question) {
|
||||||
|
return question && !question.hideOnWebInstall;
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: databaseName,
|
name: databaseName,
|
||||||
@@ -146,9 +120,10 @@ function welcome(req, res) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaults = require('./data/defaults.json');
|
var defaults = require('./data/defaults');
|
||||||
|
|
||||||
res.render('install/index', {
|
res.render('install/index', {
|
||||||
url: nconf.get('url') || (`${req.protocol}://${req.get('host')}`),
|
url: nconf.get('url') || (req.protocol + '://' + req.get('host')),
|
||||||
launchUrl: launchUrl,
|
launchUrl: launchUrl,
|
||||||
skipGeneralSetup: !!nconf.get('url'),
|
skipGeneralSetup: !!nconf.get('url'),
|
||||||
databases: databases,
|
databases: databases,
|
||||||
@@ -159,7 +134,6 @@ function welcome(req, res) {
|
|||||||
minimumPasswordLength: defaults.minimumPasswordLength,
|
minimumPasswordLength: defaults.minimumPasswordLength,
|
||||||
minimumPasswordStrength: defaults.minimumPasswordStrength,
|
minimumPasswordStrength: defaults.minimumPasswordStrength,
|
||||||
installing: installing,
|
installing: installing,
|
||||||
percentInstalled: installing ? ((Date.now() - timeStart) / totalTime * 100).toFixed(2) : 0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,51 +141,50 @@ function install(req, res) {
|
|||||||
if (installing) {
|
if (installing) {
|
||||||
return welcome(req, res);
|
return welcome(req, res);
|
||||||
}
|
}
|
||||||
timeStart = Date.now();
|
|
||||||
req.setTimeout(0);
|
req.setTimeout(0);
|
||||||
installing = true;
|
installing = true;
|
||||||
|
var setupEnvVars = nconf.get();
|
||||||
|
for (var i in req.body) {
|
||||||
|
if (req.body.hasOwnProperty(i) && !process.env.hasOwnProperty(i)) {
|
||||||
|
setupEnvVars[i.replace(':', '__')] = req.body[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const database = nconf.get('database') || req.body.database || 'mongo';
|
// Flatten any objects in setupEnvVars
|
||||||
const setupEnvVars = {
|
const pushToRoot = function (parentKey, key) {
|
||||||
...process.env,
|
setupEnvVars[parentKey + '__' + key] = setupEnvVars[parentKey][key];
|
||||||
NODEBB_URL: nconf.get('url') || req.body.url || (`${req.protocol}://${req.get('host')}`),
|
|
||||||
NODEBB_PORT: nconf.get('port') || 4567,
|
|
||||||
NODEBB_ADMIN_USERNAME: nconf.get('admin:username') || req.body['admin:username'],
|
|
||||||
NODEBB_ADMIN_PASSWORD: nconf.get('admin:password') || req.body['admin:password'],
|
|
||||||
NODEBB_ADMIN_EMAIL: nconf.get('admin:email') || req.body['admin:email'],
|
|
||||||
NODEBB_DB: database,
|
|
||||||
NODEBB_DB_HOST: nconf.get(`${database}:host`) || req.body[`${database}:host`],
|
|
||||||
NODEBB_DB_PORT: nconf.get(`${database}:port`) || req.body[`${database}:port`],
|
|
||||||
NODEBB_DB_USER: nconf.get(`${database}:username`) || req.body[`${database}:username`],
|
|
||||||
NODEBB_DB_PASSWORD: nconf.get(`${database}:password`) || req.body[`${database}:password`],
|
|
||||||
NODEBB_DB_NAME: nconf.get(`${database}:database`) || req.body[`${database}:database`],
|
|
||||||
NODEBB_DB_SSL: nconf.get(`${database}:ssl`) || req.body[`${database}:ssl`],
|
|
||||||
defaultPlugins: JSON.stringify(nconf.get('defaultplugins') || nconf.get('defaultPlugins') || []),
|
|
||||||
};
|
};
|
||||||
|
for (var j in setupEnvVars) {
|
||||||
|
if (setupEnvVars.hasOwnProperty(j) && typeof setupEnvVars[j] === 'object' && setupEnvVars[j] !== null && !Array.isArray(setupEnvVars[j])) {
|
||||||
|
Object.keys(setupEnvVars[j]).forEach(pushToRoot.bind(null, j));
|
||||||
|
delete setupEnvVars[j];
|
||||||
|
} else if (Array.isArray(setupEnvVars[j])) {
|
||||||
|
setupEnvVars[j] = JSON.stringify(setupEnvVars[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
winston.info('Starting setup process');
|
winston.info('Starting setup process');
|
||||||
launchUrl = setupEnvVars.NODEBB_URL;
|
winston.info(setupEnvVars);
|
||||||
|
launchUrl = setupEnvVars.url;
|
||||||
|
|
||||||
const child = require('child_process').fork('app', ['--setup'], {
|
var child = require('child_process').fork('app', ['--setup'], {
|
||||||
env: setupEnvVars,
|
env: setupEnvVars,
|
||||||
});
|
});
|
||||||
child.on('error', (err) => {
|
|
||||||
error = true;
|
child.on('close', function (data) {
|
||||||
success = false;
|
installing = false;
|
||||||
winston.error(err.stack);
|
|
||||||
});
|
|
||||||
child.on('close', (data) => {
|
|
||||||
success = data === 0;
|
success = data === 0;
|
||||||
error = data !== 0;
|
error = data !== 0;
|
||||||
launch();
|
|
||||||
});
|
|
||||||
welcome(req, res);
|
welcome(req, res);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launch() {
|
function launch(req, res) {
|
||||||
try {
|
res.json({});
|
||||||
server.close();
|
server.close();
|
||||||
let child;
|
req.setTimeout(0);
|
||||||
|
var child;
|
||||||
|
|
||||||
if (!nconf.get('launchCmd')) {
|
if (!nconf.get('launchCmd')) {
|
||||||
child = childProcess.spawn('node', ['loader.js'], {
|
child = childProcess.spawn('node', ['loader.js'], {
|
||||||
@@ -231,87 +204,93 @@ async function launch() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const filesToDelete = [
|
var filesToDelete = [
|
||||||
path.join(__dirname, '../public', 'installer.css'),
|
'installer.css',
|
||||||
path.join(__dirname, '../public', 'bootstrap.min.css'),
|
'installer.min.js',
|
||||||
path.join(__dirname, '../build/public', 'installer.min.js'),
|
'bootstrap.min.css',
|
||||||
];
|
];
|
||||||
try {
|
|
||||||
await Promise.all(
|
async.each(filesToDelete, function (filename, next) {
|
||||||
filesToDelete.map(
|
fs.unlink(path.join(__dirname, '../public', filename), next);
|
||||||
filename => fs.promises.unlink(filename)
|
}, function (err) {
|
||||||
)
|
if (err) {
|
||||||
);
|
winston.warn('Unable to remove installer files');
|
||||||
} catch (err) {
|
|
||||||
console.log(err.stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
child.unref();
|
child.unref();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} catch (err) {
|
|
||||||
winston.error(err.stack);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is necessary because otherwise the compiled templates won't be available on a clean install
|
|
||||||
async function compileTemplate() {
|
|
||||||
const sourceFile = path.join(__dirname, '../src/views/install/index.tpl');
|
|
||||||
const destTpl = path.join(viewsDir, 'install/index.tpl');
|
|
||||||
const destJs = path.join(viewsDir, 'install/index.js');
|
|
||||||
|
|
||||||
const source = await fs.promises.readFile(sourceFile, 'utf8');
|
|
||||||
|
|
||||||
const [compiled] = await Promise.all([
|
|
||||||
Benchpress.precompile(source, { filename: 'install/index.tpl' }),
|
|
||||||
mkdirp(path.dirname(destJs)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
fs.promises.writeFile(destJs, compiled),
|
|
||||||
fs.promises.writeFile(destTpl, source),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function compileSass() {
|
|
||||||
try {
|
|
||||||
const installSrc = path.join(__dirname, '../public/scss/install.scss');
|
|
||||||
const style = await fs.promises.readFile(installSrc);
|
|
||||||
const scssOutput = sass.compileString(String(style), {
|
|
||||||
loadPaths: [
|
|
||||||
path.join(__dirname, '../public/scss'),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await fs.promises.writeFile(path.join(__dirname, '../public/installer.css'), scssOutput.css.toString());
|
function compileLess(callback) {
|
||||||
} catch (err) {
|
fs.readFile(path.join(__dirname, '../public/less/install.less'), function (err, style) {
|
||||||
winston.error(`Unable to compile SASS: \n${err.stack}`);
|
if (err) {
|
||||||
throw err;
|
return winston.error('Unable to read LESS install file: ', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
less.render(style.toString(), function (err, css) {
|
||||||
|
if (err) {
|
||||||
|
return winston.error('Unable to compile LESS: ', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFile(path.join(__dirname, '../public/installer.css'), css.css, callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyCSS() {
|
function compileJS(callback) {
|
||||||
await fs.promises.copyFile(
|
var code = '';
|
||||||
path.join(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'),
|
async.eachSeries(scripts, function (srcPath, next) {
|
||||||
path.join(__dirname, '../public/bootstrap.min.css'),
|
fs.readFile(path.join(__dirname, '..', srcPath), function (err, buffer) {
|
||||||
);
|
if (err) {
|
||||||
}
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadDefaults() {
|
code += buffer.toString();
|
||||||
const setupDefaultsPath = path.join(__dirname, '../setup.json');
|
next();
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-bitwise
|
var minified = uglify.minify(code, {
|
||||||
await fs.promises.access(setupDefaultsPath, fs.constants.F_OK | fs.constants.R_OK);
|
compress: false,
|
||||||
} catch (err) {
|
});
|
||||||
|
if (!minified.code) {
|
||||||
|
return callback(new Error('[[error:failed-to-minify]]'));
|
||||||
|
}
|
||||||
|
fs.writeFile(path.join(__dirname, '../public/installer.min.js'), minified.code, callback);
|
||||||
|
} catch (e) {
|
||||||
|
callback(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyCSS(next) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
fs.readFile(path.join(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'), 'utf8', next);
|
||||||
|
},
|
||||||
|
function (src, next) {
|
||||||
|
fs.writeFile(path.join(__dirname, '../public/bootstrap.min.css'), src, next);
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDefaults(next) {
|
||||||
|
var setupDefaultsPath = path.join(__dirname, '../setup.json');
|
||||||
|
fs.access(setupDefaultsPath, fs.constants.F_OK | fs.constants.R_OK, function (err) {
|
||||||
|
if (err) {
|
||||||
// setup.json not found or inaccessible, proceed with no defaults
|
// setup.json not found or inaccessible, proceed with no defaults
|
||||||
if (err.code !== 'ENOENT') {
|
return setImmediate(next);
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
winston.info('[installer] Found setup.json, populating default values');
|
winston.info('[installer] Found setup.json, populating default values');
|
||||||
nconf.file({
|
nconf.file({
|
||||||
file: setupDefaultsPath,
|
file: setupDefaultsPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
145
loader.js
@@ -1,61 +1,82 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
const fs = require('fs');
|
var fs = require('fs');
|
||||||
const url = require('url');
|
var url = require('url');
|
||||||
const path = require('path');
|
var path = require('path');
|
||||||
const { fork } = require('child_process');
|
var fork = require('child_process').fork;
|
||||||
const logrotate = require('logrotate-stream');
|
var async = require('async');
|
||||||
const { mkdirp } = require('mkdirp');
|
var logrotate = require('logrotate-stream');
|
||||||
|
var mkdirp = require('mkdirp');
|
||||||
|
|
||||||
const file = require('./src/file');
|
var file = require('./src/file');
|
||||||
const pkg = require('./package.json');
|
var pkg = require('./package.json');
|
||||||
|
|
||||||
const pathToConfig = path.resolve(__dirname, process.env.CONFIG || 'config.json');
|
var pathToConfig = path.resolve(__dirname, process.env.CONFIG || 'config.json');
|
||||||
|
|
||||||
nconf.argv().env().file({
|
nconf.argv().env().file({
|
||||||
file: pathToConfig,
|
file: pathToConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
const pidFilePath = path.join(__dirname, 'pidfile');
|
var pidFilePath = path.join(__dirname, 'pidfile');
|
||||||
|
|
||||||
const outputLogFilePath = path.join(__dirname, nconf.get('logFile') || 'logs/output.log');
|
var outputLogFilePath = path.join(__dirname, nconf.get('logFile') || 'logs/output.log');
|
||||||
|
|
||||||
const logDir = path.dirname(outputLogFilePath);
|
var logDir = path.dirname(outputLogFilePath);
|
||||||
if (!fs.existsSync(logDir)) {
|
if (!fs.existsSync(logDir)) {
|
||||||
mkdirp.sync(path.dirname(outputLogFilePath));
|
mkdirp.sync(path.dirname(outputLogFilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
var output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
||||||
const silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
|
var silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
|
||||||
let numProcs;
|
var numProcs;
|
||||||
const workers = [];
|
var workers = [];
|
||||||
const Loader = {};
|
var Loader = {
|
||||||
const appPath = path.join(__dirname, 'app.js');
|
timesStarted: 0,
|
||||||
|
};
|
||||||
|
var appPath = path.join(__dirname, 'app.js');
|
||||||
|
|
||||||
Loader.init = function () {
|
Loader.init = function (callback) {
|
||||||
if (silent) {
|
if (silent) {
|
||||||
console.log = (...args) => {
|
console.log = function () {
|
||||||
output.write(`${args.join(' ')}\n`);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
output.write(args.join(' ') + '\n');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('SIGHUP', Loader.restart);
|
process.on('SIGHUP', Loader.restart);
|
||||||
process.on('SIGTERM', Loader.stop);
|
process.on('SIGTERM', Loader.stop);
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
Loader.displayStartupMessages = function () {
|
Loader.displayStartupMessages = function (callback) {
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(`NodeBB v${pkg.version} Copyright (C) 2013-${(new Date()).getFullYear()} NodeBB Inc.`);
|
console.log('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 NodeBB Inc.');
|
||||||
console.log('This program comes with ABSOLUTELY NO WARRANTY.');
|
console.log('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||||
console.log('This is free software, and you are welcome to redistribute it under certain conditions.');
|
console.log('This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||||
console.log('For the full license, please visit: http://www.gnu.org/copyleft/gpl.html');
|
console.log('For the full license, please visit: http://www.gnu.org/copyleft/gpl.html');
|
||||||
console.log('');
|
console.log('');
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
Loader.addWorkerEvents = function (worker) {
|
Loader.addWorkerEvents = function (worker) {
|
||||||
worker.on('exit', (code, signal) => {
|
worker.on('exit', function (code, signal) {
|
||||||
console.log(`[cluster] Child Process (${worker.pid}) has exited (code: ${code}, signal: ${signal})`);
|
if (code !== 0) {
|
||||||
|
if (Loader.timesStarted < numProcs * 3) {
|
||||||
|
Loader.timesStarted += 1;
|
||||||
|
if (Loader.crashTimer) {
|
||||||
|
clearTimeout(Loader.crashTimer);
|
||||||
|
}
|
||||||
|
Loader.crashTimer = setTimeout(function () {
|
||||||
|
Loader.timesStarted = 0;
|
||||||
|
}, 10000);
|
||||||
|
} else {
|
||||||
|
console.log((numProcs * 3) + ' restarts in 10 seconds, most likely an error on startup. Halting.');
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[cluster] Child Process (' + worker.pid + ') has exited (code: ' + code + ', signal: ' + signal + ')');
|
||||||
if (!(worker.suicide || code === 0)) {
|
if (!(worker.suicide || code === 0)) {
|
||||||
console.log('[cluster] Spinning up another process...');
|
console.log('[cluster] Spinning up another process...');
|
||||||
|
|
||||||
@@ -63,7 +84,7 @@ Loader.addWorkerEvents = function (worker) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('message', (message) => {
|
worker.on('message', function (message) {
|
||||||
if (message && typeof message === 'object' && message.action) {
|
if (message && typeof message === 'object' && message.action) {
|
||||||
switch (message.action) {
|
switch (message.action) {
|
||||||
case 'restart':
|
case 'restart':
|
||||||
@@ -71,12 +92,12 @@ Loader.addWorkerEvents = function (worker) {
|
|||||||
Loader.restart();
|
Loader.restart();
|
||||||
break;
|
break;
|
||||||
case 'pubsub':
|
case 'pubsub':
|
||||||
workers.forEach((w) => {
|
workers.forEach(function (w) {
|
||||||
w.send(message);
|
w.send(message);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'socket.io':
|
case 'socket.io':
|
||||||
workers.forEach((w) => {
|
workers.forEach(function (w) {
|
||||||
if (w !== worker) {
|
if (w !== worker) {
|
||||||
w.send(message);
|
w.send(message);
|
||||||
}
|
}
|
||||||
@@ -87,28 +108,32 @@ Loader.addWorkerEvents = function (worker) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Loader.start = function () {
|
Loader.start = function (callback) {
|
||||||
numProcs = getPorts().length;
|
numProcs = getPorts().length;
|
||||||
console.log(`Clustering enabled: Spinning up ${numProcs} process(es).\n`);
|
console.log('Clustering enabled: Spinning up ' + numProcs + ' process(es).\n');
|
||||||
|
|
||||||
for (let x = 0; x < numProcs; x += 1) {
|
for (var x = 0; x < numProcs; x += 1) {
|
||||||
forkWorker(x, x === 0);
|
forkWorker(x, x === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function forkWorker(index, isPrimary) {
|
function forkWorker(index, isPrimary) {
|
||||||
const ports = getPorts();
|
var ports = getPorts();
|
||||||
const args = [];
|
var args = [];
|
||||||
|
|
||||||
if (!ports[index]) {
|
if (!ports[index]) {
|
||||||
return console.log(`[cluster] invalid port for worker : ${index} ports: ${ports.length}`);
|
return console.log('[cluster] invalid port for worker : ' + index + ' ports: ' + ports.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
process.env.isPrimary = isPrimary;
|
process.env.isPrimary = isPrimary;
|
||||||
process.env.isCluster = nconf.get('isCluster') || ports.length > 1;
|
process.env.isCluster = nconf.get('isCluster') || ports.length > 1;
|
||||||
process.env.port = ports[index];
|
process.env.port = ports[index];
|
||||||
|
|
||||||
const worker = fork(appPath, args, {
|
var worker = fork(appPath, args, {
|
||||||
silent: silent,
|
silent: silent,
|
||||||
env: process.env,
|
env: process.env,
|
||||||
});
|
});
|
||||||
@@ -121,20 +146,20 @@ function forkWorker(index, isPrimary) {
|
|||||||
Loader.addWorkerEvents(worker);
|
Loader.addWorkerEvents(worker);
|
||||||
|
|
||||||
if (silent) {
|
if (silent) {
|
||||||
const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
var output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
||||||
worker.stdout.pipe(output);
|
worker.stdout.pipe(output);
|
||||||
worker.stderr.pipe(output);
|
worker.stderr.pipe(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPorts() {
|
function getPorts() {
|
||||||
const _url = nconf.get('url');
|
var _url = nconf.get('url');
|
||||||
if (!_url) {
|
if (!_url) {
|
||||||
console.log('[cluster] url is undefined, please check your config.json');
|
console.log('[cluster] url is undefined, please check your config.json');
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
const urlObject = url.parse(_url);
|
var urlObject = url.parse(_url);
|
||||||
let port = nconf.get('PORT') || nconf.get('port') || urlObject.port || 4567;
|
var port = nconf.get('PORT') || nconf.get('port') || urlObject.port || 4567;
|
||||||
if (!Array.isArray(port)) {
|
if (!Array.isArray(port)) {
|
||||||
port = [port];
|
port = [port];
|
||||||
}
|
}
|
||||||
@@ -147,13 +172,13 @@ Loader.restart = function () {
|
|||||||
nconf.remove('file');
|
nconf.remove('file');
|
||||||
nconf.use('file', { file: pathToConfig });
|
nconf.use('file', { file: pathToConfig });
|
||||||
|
|
||||||
fs.readFile(pathToConfig, { encoding: 'utf-8' }, (err, configFile) => {
|
fs.readFile(pathToConfig, { encoding: 'utf-8' }, function (err, configFile) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error reading config');
|
console.error('Error reading config');
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const conf = JSON.parse(configFile);
|
var conf = JSON.parse(configFile);
|
||||||
|
|
||||||
nconf.stores.env.readOnly = false;
|
nconf.stores.env.readOnly = false;
|
||||||
nconf.set('url', conf.url);
|
nconf.set('url', conf.url);
|
||||||
@@ -176,13 +201,13 @@ Loader.stop = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function killWorkers() {
|
function killWorkers() {
|
||||||
workers.forEach((worker) => {
|
workers.forEach(function (worker) {
|
||||||
worker.suicide = true;
|
worker.suicide = true;
|
||||||
worker.kill();
|
worker.kill();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.open(pathToConfig, 'r', (err) => {
|
fs.open(pathToConfig, 'r', function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
// No config detected, kickstart web installer
|
// No config detected, kickstart web installer
|
||||||
fork('app');
|
fork('app');
|
||||||
@@ -191,26 +216,13 @@ fs.open(pathToConfig, 'r', (err) => {
|
|||||||
|
|
||||||
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
|
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
|
||||||
if (file.existsSync(pidFilePath)) {
|
if (file.existsSync(pidFilePath)) {
|
||||||
let pid = 0;
|
|
||||||
try {
|
try {
|
||||||
pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' });
|
var pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' });
|
||||||
if (pid) {
|
|
||||||
process.kill(pid, 0);
|
process.kill(pid, 0);
|
||||||
console.info(`Process "${pid}" from pidfile already running, exiting`);
|
|
||||||
process.exit();
|
process.exit();
|
||||||
} else {
|
} catch (e) {
|
||||||
console.info(`Invalid pid "${pid}" from pidfile, deleting pidfile`);
|
|
||||||
fs.unlinkSync(pidFilePath);
|
fs.unlinkSync(pidFilePath);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
if (err.code === 'ESRCH') {
|
|
||||||
console.info(`Process "${pid}" from pidfile not found, deleting pidfile`);
|
|
||||||
fs.unlinkSync(pidFilePath);
|
|
||||||
} else {
|
|
||||||
console.error(err.stack);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
require('daemon')({
|
require('daemon')({
|
||||||
@@ -219,14 +231,17 @@ fs.open(pathToConfig, 'r', (err) => {
|
|||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(pidFilePath, String(process.pid));
|
fs.writeFileSync(pidFilePath, process.pid);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
Loader.init();
|
async.series([
|
||||||
Loader.displayStartupMessages();
|
Loader.init,
|
||||||
Loader.start();
|
Loader.displayStartupMessages,
|
||||||
} catch (err) {
|
Loader.start,
|
||||||
|
], function (err) {
|
||||||
|
if (err) {
|
||||||
console.error('[loader] Error during startup');
|
console.error('[loader] Error during startup');
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,57 @@
|
|||||||
{
|
{
|
||||||
"extends": "nodebb/public"
|
"globals": {
|
||||||
|
"app": true,
|
||||||
|
"io": true,
|
||||||
|
"socket": true,
|
||||||
|
"ajaxify": true,
|
||||||
|
"config": true,
|
||||||
|
"RELATIVE_PATH": true,
|
||||||
|
"utils": true,
|
||||||
|
"overrides": true,
|
||||||
|
"componentHandler": true,
|
||||||
|
"bootbox": true,
|
||||||
|
"Visibility": true,
|
||||||
|
"Tinycon": true,
|
||||||
|
"Promise": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"jquery": true,
|
||||||
|
"amd": true,
|
||||||
|
"browser": true,
|
||||||
|
"es6": false
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-dupe-class-members": "off",
|
||||||
|
"no-var": "off",
|
||||||
|
"object-shorthand": "off",
|
||||||
|
"prefer-arrow-callback": "off",
|
||||||
|
"prefer-spread": "off",
|
||||||
|
"prefer-reflect": "off",
|
||||||
|
"prefer-template": "off"
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 5,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"arrowFunctions": false,
|
||||||
|
"classes": false,
|
||||||
|
"defaultParams": false,
|
||||||
|
"destructuring": false,
|
||||||
|
"experimentalObjectRestSpread": false,
|
||||||
|
"blockBindings": false,
|
||||||
|
"forOf": false,
|
||||||
|
"generators": false,
|
||||||
|
"globalReturn": false,
|
||||||
|
"jsx": false,
|
||||||
|
"modules": false,
|
||||||
|
"objectLiteralComputedProperties": false,
|
||||||
|
"objectLiteralDuplicateProperties": false,
|
||||||
|
"objectLiteralShorthandMethods": false,
|
||||||
|
"objectLiteralShorthandProperties": false,
|
||||||
|
"impliedStrict": false,
|
||||||
|
"restParams": false,
|
||||||
|
"spread": false,
|
||||||
|
"superInFunctions": false,
|
||||||
|
"templateStrings": false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
public/.jshintrc
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"maxerr" : 50, // {int} Maximum error before stopping
|
||||||
|
|
||||||
|
// Enforcing
|
||||||
|
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||||
|
"camelcase" : false, // true: Identifiers must be in camelCase
|
||||||
|
"curly" : true, // true: Require {} for every new block or scope
|
||||||
|
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||||
|
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||||
|
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
|
||||||
|
"indent" : 4, // {int} Number of spaces to use for indentation
|
||||||
|
"latedef" : false, // true: Require variables/functions to be defined before being used
|
||||||
|
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
|
||||||
|
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
|
||||||
|
"noempty" : true, // true: Prohibit use of empty blocks
|
||||||
|
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
|
||||||
|
"plusplus" : false, // true: Prohibit use of `++` & `--`
|
||||||
|
"quotmark" : false, // Quotation mark consistency:
|
||||||
|
// false : do nothing (default)
|
||||||
|
// true : ensure whatever is used is consistent
|
||||||
|
// "single" : require single quotes
|
||||||
|
// "double" : require double quotes
|
||||||
|
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
|
||||||
|
"unused" : true, // true: Require all defined variables be used
|
||||||
|
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
|
||||||
|
"trailing" : false, // true: Prohibit trailing whitespaces
|
||||||
|
"maxparams" : false, // {int} Max number of formal params allowed per function
|
||||||
|
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
|
||||||
|
"maxstatements" : false, // {int} Max number statements per function
|
||||||
|
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
|
||||||
|
"maxlen" : false, // {int} Max number of characters per line
|
||||||
|
|
||||||
|
// Relaxing
|
||||||
|
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
|
||||||
|
"boss" : false, // true: Tolerate assignments where comparisons would be expected
|
||||||
|
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||||
|
"eqnull" : false, // true: Tolerate use of `== null`
|
||||||
|
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
|
||||||
|
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||||
|
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||||
|
// (ex: `for each`, multiple try/catch, function expression…)
|
||||||
|
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||||
|
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
|
||||||
|
"funcscope" : false, // true: Tolerate defining variables inside control statements"
|
||||||
|
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
|
||||||
|
"iterator" : false, // true: Tolerate using the `__iterator__` property
|
||||||
|
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
|
||||||
|
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
|
||||||
|
"laxcomma" : false, // true: Tolerate comma-first style coding
|
||||||
|
"loopfunc" : false, // true: Tolerate functions being defined in loops
|
||||||
|
"multistr" : false, // true: Tolerate multi-line strings
|
||||||
|
"proto" : false, // true: Tolerate using the `__proto__` property
|
||||||
|
"scripturl" : false, // true: Tolerate script-targeted URLs
|
||||||
|
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
|
||||||
|
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
|
||||||
|
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
|
||||||
|
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
|
||||||
|
"validthis" : false, // true: Tolerate using this in a non-constructor function
|
||||||
|
|
||||||
|
"globals": {
|
||||||
|
"app": true,
|
||||||
|
"io": true,
|
||||||
|
"socket": true,
|
||||||
|
"ajaxify": true,
|
||||||
|
"config": true,
|
||||||
|
"RELATIVE_PATH": true,
|
||||||
|
"utils": true,
|
||||||
|
"overrides": true,
|
||||||
|
"componentHandler": true,
|
||||||
|
"bootbox": true,
|
||||||
|
"templates": true,
|
||||||
|
"Visibility": true,
|
||||||
|
"Tinycon": true,
|
||||||
|
"require": true,
|
||||||
|
"define": true,
|
||||||
|
"ace": true,
|
||||||
|
"Sortable": true,
|
||||||
|
"Slideout": true,
|
||||||
|
"NProgress": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"jquery": true,
|
||||||
|
"browser": true
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Excessive Load Warning</title>
|
<title>Excessive Load Warning</title>
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Ubuntu:400,500,700' rel='stylesheet' type='text/css'>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
background: #00A9EA;
|
background: #00A9EA;
|
||||||
color: white;
|
color: white;
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
font-family: 'Ubuntu', sans-serif;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
-webkit-transform-style: preserve-3d;
|
-webkit-transform-style: preserve-3d;
|
||||||
-moz-transform-style: preserve-3d;
|
-moz-transform-style: preserve-3d;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -1,16 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="321" height="75" viewBox="0 0 321 75">
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="nodebb-a" cx="65.599%" cy="0%" r="169.036%" fx="65.599%" fy="0%" gradientTransform="matrix(-.5183 .5916 -.38031 -.80624 .996 -.388)">
|
|
||||||
<stop offset="0%" stop-color="#2A6CBE"/>
|
|
||||||
<stop offset="38.688%" stop-color="#2062BC"/>
|
|
||||||
<stop offset="49.182%" stop-color="#1F5FBC"/>
|
|
||||||
<stop offset="66.583%" stop-color="#1C5ABD"/>
|
|
||||||
<stop offset="85.354%" stop-color="#1956BC"/>
|
|
||||||
<stop offset="100%" stop-color="#1851BE"/>
|
|
||||||
</radialGradient>
|
|
||||||
</defs>
|
|
||||||
<g fill="none" transform="translate(1 1)">
|
|
||||||
<path fill="#000306" stroke="#000306" stroke-width=".275" d="M195.810017,20 C210.691849,20 221.256813,28.7272272 221.484017,45.845679 C221.490864,46.3615394 221.461776,47.1194739 221.396754,48.1194827 C221.328352,49.1714504 220.455158,49.9897119 219.400969,49.9897119 L179.110557,49.9897119 C179.110557,56.6336943 182.723978,64.2757202 196.378026,64.2757202 C204.088827,64.2757202 211.625406,61.928339 218.987763,57.2335766 L219.553745,56.8677968 C220.246813,56.4139645 221.17656,56.6079034 221.630392,57.3009716 C221.757029,57.4943637 221.837254,57.7144077 221.864806,57.943925 C222.131013,60.1615294 222.004084,61.5832211 221.484017,62.2089999 C220.452051,63.4507301 210.901843,73 196.378026,73 C180.58738,73 169,61.8765432 169,46.3909465 C169,31.3415638 180.019372,20 195.810017,20 Z M162.92733,0 C164.031899,-4.2495073e-16 164.92733,0.8954305 164.92733,2 L164.928075,44.1266361 C164.975781,44.8118124 165,45.5058194 165,46.2079256 C165,61.5334332 153.460581,73 138,73 C122.427386,73 111,61.5334332 111,46.2079256 C111,30.882418 122.427386,19.4158511 138,19.4158511 C144.981278,19.4158511 151.163029,21.7538895 155.843826,25.7295173 L155.843557,4.54188628 C155.843557,2.03347175 157.877029,4.60788274e-16 160.385444,0 L162.92733,0 Z M81,20 C96.4605809,20 108,31.3415638 108,46.5 C108,61.6584362 96.4605809,73 81,73 C65.4273859,73 54,61.6584362 54,46.5 C54,31.3415638 65.4273859,20 81,20 Z M27.324263,20 C41.6099773,20 50,29.1649049 50,41.7801268 L50,67.5 C50,69.4329966 48.4329966,71 46.5,71 L42.2494331,71 C41.1448636,71 40.2494331,70.1045695 40.2494331,69 L40.2494331,42.5348837 C40.2494331,34.3403805 35.8276644,28.6257928 26.3038549,28.6257928 C10.0816327,28.6257928 9.75718821,35.7795705 9.75069932,44.6831843 L9.75056689,71 L2,71 C0.8954305,71 1.3527075e-16,70.1045695 0,69 L0,35.2932975 L0,35.2932975 C0,24.3935718 19.1609977,20 27.324263,20 Z M81,28.7242798 C70.8049793,28.7242798 63.6348548,36.4670782 63.6348548,46.5 C63.6348548,56.3148148 70.8049793,64.2757202 81,64.2757202 C91.1950207,64.2757202 98.3651452,56.3148148 98.3651452,46.5 C98.3651452,36.4670782 91.1950207,28.7242798 81,28.7242798 Z M138,28.2362872 C127.804979,28.2362872 120.634855,36.0644241 120.634855,46.2079256 C120.634855,56.1309161 127.804979,64.179564 138,64.179564 C148.195021,64.179564 155.365145,56.1309161 155.365145,46.2079256 C155.365145,36.0644241 148.195021,28.2362872 138,28.2362872 Z M195.810017,28.7242798 C186.721876,28.7242798 180.473779,33.9588477 179.224159,41.8106996 L211.259858,41.8106996 C210.010238,33.9588477 204.443752,28.7242798 195.810017,28.7242798 Z"/>
|
|
||||||
<path fill="url(#nodebb-a)" d="M277,19 L276.999615,57.7303365 C276.99251,64.180962 276.88949,68.6242617 276.88949,73 L276.88949,73 L255.649055,73 C242.962262,73 236,67.06 236,57.7257143 C236,51.4771429 239.558488,46.9257143 245.1283,45.0742857 C240.564149,43.1457143 237.779243,38.8257143 237.779243,33.5028571 C237.779243,24.8628571 244.664149,19 256.732074,19 L256.732074,19 L277,19 Z M299.267926,19 C311.335851,19 318.220757,24.8628571 318.220757,33.5028571 C318.220757,38.8257143 315.435851,43.1457143 310.8717,45.0742857 C316.441512,46.9257143 320,51.4771429 320,57.7257143 C320,67.06 313.037738,73 300.350945,73 L279.11051,73 L279.11051,73 C279.11051,68.6242617 279.00749,64.180962 279.000385,57.7303365 L279,19 L299.267926,19 Z M266.092452,49.8571429 L256.499999,49.8571429 C250.156602,49.8571429 246.984904,52.2485714 246.984904,56.9542857 C246.984904,61.8914286 249.924527,64.36 255.726414,64.36 L255.726414,64.36 L260.754716,64.36 C264.777358,64.36 266.092452,62.4314286 266.092452,56.5685714 L266.092452,56.5685714 L266.092452,49.8571429 Z M299.500001,49.8571429 L289.907548,49.8571429 L289.907548,56.5685714 C289.907548,62.4314286 291.222642,64.36 295.245284,64.36 L300.273586,64.36 C306.075473,64.36 309.015096,61.8914286 309.015096,56.9542857 C309.015096,52.2485714 305.843398,49.8571429 299.500001,49.8571429 Z M266.169811,27.64 L257.350942,27.64 C251.703772,27.64 248.764149,30.1857143 248.764149,34.5057143 C248.764149,38.8257143 251.703772,41.3714286 257.350942,41.3714286 L257.350942,41.3714286 L266.169811,41.3714286 L266.169811,27.64 Z M298.649058,27.64 L289.830189,27.64 L289.830189,41.3714286 L298.649058,41.3714286 C304.296228,41.3714286 307.235851,38.8257143 307.235851,34.5057143 C307.235851,30.1857143 304.296228,27.64 298.649058,27.64 Z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 86 B |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
3
public/js-enabled.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*
|
||||||
|
The following stylesheet is only included on pages that can execute javascript
|
||||||
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
The files here are read-only and overwritten daily (if there are changes) by the
|
The files here are read-only and overwritten daily (if there are changes) by the
|
||||||
helper bot [Misty](https://github.com/nodebb-misty).
|
helper bot [Misty](https://github.com/nodebb-misty).
|
||||||
|
|
||||||
Our localisation efforts are handled via [our Transifex Project](https://explore.transifex.com/nodebb/nodebb/),
|
Our localisation efforts are handled via [our Transifex Project](https://www.transifex.com/nodebb/nodebb/),
|
||||||
and any pull requests made to this directory will be automatically closed because
|
and any pull requests made to this directory will be automatically closed because
|
||||||
localisations can go out-of-sync when edited directly.
|
localisations can go out-of-sync when edited directly.
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# The files here are not meant to be edited directly
|
|
||||||
|
|
||||||
Please see the → [Internalization README](../README.md).
|
|
||||||
@@ -3,14 +3,5 @@
|
|||||||
"alert.confirm-restart": "هل تريد بالتأكيد إعادة تشغيل NodeBB؟",
|
"alert.confirm-restart": "هل تريد بالتأكيد إعادة تشغيل NodeBB؟",
|
||||||
|
|
||||||
"acp-title": "لوحة تحكم إدارة NodeBB | %1",
|
"acp-title": "لوحة تحكم إدارة NodeBB | %1",
|
||||||
"settings-header-contents": "محتويات",
|
"settings-header-contents": "محتويات"
|
||||||
"changes-saved": "Changes Saved",
|
|
||||||
"changes-saved-message": "Your changes to the NodeBB configuration have been saved.",
|
|
||||||
"changes-not-saved": "لم يتم حفظ التغييرات",
|
|
||||||
"changes-not-saved-message": "حدثت مشكلة أثناء حفظ التغييرات في NodeBB. (%1)",
|
|
||||||
"save-changes": "Save changes",
|
|
||||||
"min": "Min:",
|
|
||||||
"max": "Max:",
|
|
||||||
"view": "View",
|
|
||||||
"edit": "Edit"
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"cache": "Cache",
|
|
||||||
"post-cache": "التخزين المؤقت للمشاركات",
|
"post-cache": "التخزين المؤقت للمشاركات",
|
||||||
"group-cache": "التخزين المؤقت للمجموعات",
|
"posts-in-cache": "المشاركات المخزنة مؤقتاً",
|
||||||
"local-cache": "تخزين مؤقت محلي",
|
"average-post-size": "متوسط حجم المشاركة",
|
||||||
"object-cache": "تخزين مؤقت للأشياء",
|
"length-to-max": "الطول / أقصى حد",
|
||||||
"percent-full": "1% كاملة",
|
"percent-full": "1% كاملة",
|
||||||
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
|
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
|
||||||
"items-in-cache": "العناصر في التخزين المؤقت"
|
"items-in-cache": "العناصر في التخزين المؤقت",
|
||||||
|
"control-panel": "لوحة التحكم",
|
||||||
|
"update-settings": "تحديث إعدادات التخزين المؤقت"
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,52 @@
|
|||||||
{
|
{
|
||||||
"x-b": "٪1 بايت",
|
"x-b": "%1 b",
|
||||||
"x-mb": "٪1 ميقا بايت",
|
"x-mb": "%1 mb",
|
||||||
"x-gb": "٪1 قيقا بايت",
|
"x-gb": "%1 gb",
|
||||||
"uptime-seconds": "مدة التشغيل بالثواني",
|
"uptime-seconds": "Uptime in Seconds",
|
||||||
"uptime-days": "مدة التشغيل بالأيام",
|
"uptime-days": "Uptime in Days",
|
||||||
|
|
||||||
"mongo": "MongoDB",
|
"mongo": "Mongo",
|
||||||
"mongo.version": "اصدار MongoDB",
|
"mongo.version": "MongoDB Version",
|
||||||
"mongo.storage-engine": "محرك التخزين",
|
"mongo.storage-engine": "Storage Engine",
|
||||||
"mongo.collections": "التجميعات",
|
"mongo.collections": "Collections",
|
||||||
"mongo.objects": "العناصر",
|
"mongo.objects": "Objects",
|
||||||
"mongo.avg-object-size": "معدل حجم العناصر",
|
"mongo.avg-object-size": "Avg. Object Size",
|
||||||
"mongo.data-size": "حجم البيانات",
|
"mongo.data-size": "Data Size",
|
||||||
"mongo.storage-size": "حجم التخرين",
|
"mongo.storage-size": "Storage Size",
|
||||||
"mongo.index-size": "حجم الفهرس",
|
"mongo.index-size": "Index Size",
|
||||||
"mongo.file-size": "حجم الملف",
|
"mongo.file-size": "File Size",
|
||||||
"mongo.resident-memory": "الذاكرة الساكنة",
|
"mongo.resident-memory": "Resident Memory",
|
||||||
"mongo.virtual-memory": "الذاكرة الإفتراضية",
|
"mongo.virtual-memory": "الذاكرة الإفتراضية",
|
||||||
"mongo.mapped-memory": "الذاكرة المعينة",
|
"mongo.mapped-memory": "Mapped Memory",
|
||||||
"mongo.bytes-in": "البايتات الواردة",
|
"mongo.bytes-in": "Bytes In",
|
||||||
"mongo.bytes-out": "البايتات الصادرة",
|
"mongo.bytes-out": "Bytes Out",
|
||||||
"mongo.num-requests": "عدد الطلبات",
|
"mongo.num-requests": "Number of Requests",
|
||||||
"mongo.raw-info": "معلومات MongoDB الأولية",
|
"mongo.raw-info": "MongoDB Raw Info",
|
||||||
"mongo.unauthorized": "لم يستطع NodeBB من الاستعلام عن احصاءات قواعد البيانات ل MongoDB. الرجاء التأكد من أن المستخدم في NodeBB يحتوي على دور "clusterMonitor" ال "admin" لقواعد البيانات.",
|
"mongo.unauthorized": "NodeBB was unable to query the MongoDB database for relevant statistics. Please ensure that the user in use by NodeBB contains the "clusterMonitor" role for the "admin" database.",
|
||||||
|
|
||||||
"redis": "Redis",
|
"redis": "Redis",
|
||||||
"redis.version": "إصدار Redis",
|
"redis.version": "Redis Version",
|
||||||
"redis.keys": "المفاتيح",
|
"redis.keys": "Keys",
|
||||||
"redis.expires": "ينتهي ",
|
"redis.expires": "Expires",
|
||||||
"redis.avg-ttl": "متوسط وقت الانعاش",
|
"redis.avg-ttl": "Average TTL",
|
||||||
"redis.connected-clients": "العميل المتصل",
|
"redis.connected-clients": "Connected Clients",
|
||||||
"redis.connected-slaves": "البدلاء المتصلين",
|
"redis.connected-slaves": "Connected Slaves",
|
||||||
"redis.blocked-clients": "العملاء المحظورون",
|
"redis.blocked-clients": "Blocked Clients",
|
||||||
"redis.used-memory": "الذاكرة المستخدمة",
|
"redis.used-memory": "الذاكرة المستخدمة",
|
||||||
"redis.memory-frag-ratio": "نسبة تجزئة الذاكرة",
|
"redis.memory-frag-ratio": "Memory Fragmentation Ratio",
|
||||||
"redis.total-connections-recieved": "إجمالي الاتصالات المستلمة",
|
"redis.total-connections-recieved": "إجمالي الاتصالات المستلمة",
|
||||||
"redis.total-commands-processed": "إجمالي الأوامر التي تمت معالجتها",
|
"redis.total-commands-processed": "إجمالي الأوامر التي تمت معالجتها",
|
||||||
"redis.iops": "العمليات اللحظية في الثانية",
|
"redis.iops": "Instantaneous Ops. Per Second",
|
||||||
"redis.iinput": "الإدخال الفوري في الثانية",
|
"redis.iinput": "Instantaneous Input Per Second",
|
||||||
"redis.ioutput": "المخرجات الفورية في الثانية",
|
"redis.ioutput": "Instantaneous Output Per Second",
|
||||||
"redis.total-input": "مجموع المدخلات",
|
"redis.total-input": "Total Input",
|
||||||
"redis.total-output": "مجموع المخرجات",
|
"redis.total-output": "Total Ouput",
|
||||||
|
|
||||||
"redis.keyspace-hits": "المفاتيح المضغوطة",
|
"redis.keyspace-hits": "Keyspace Hits",
|
||||||
"redis.keyspace-misses": "المفاتيح المخفقة",
|
"redis.keyspace-misses": "Keyspace Misses",
|
||||||
"redis.raw-info": "معلومات Redis الأولية",
|
"redis.raw-info": "Redis Raw Info",
|
||||||
|
|
||||||
"postgres": "Postgres",
|
"postgres": "Postgres",
|
||||||
"postgres.version": "إصدار PostgreSQL",
|
"postgres.version": "PostgreSQL Version",
|
||||||
"postgres.raw-info": "معلومات Postgres الأولية"
|
"postgres.raw-info": "Postgres Raw Info"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"errors": "Errors",
|
|
||||||
"figure-x": "شكل %1",
|
"figure-x": "شكل %1",
|
||||||
"error-events-per-day": "<code>%1</code> حدث كل يوم ",
|
"error-events-per-day": "<code>%1</code> حدث كل يوم ",
|
||||||
"error.404": "404 لم يتم العثور",
|
"error.404": "404 لم يتم العثور",
|
||||||
|
|||||||
@@ -2,12 +2,10 @@
|
|||||||
"events": "أحداث",
|
"events": "أحداث",
|
||||||
"no-events": "لا توجد أحداث",
|
"no-events": "لا توجد أحداث",
|
||||||
"control-panel": "لوحة تحكم الأحداث",
|
"control-panel": "لوحة تحكم الأحداث",
|
||||||
"delete-events": "حذف الاحداث",
|
"filters": "Filters",
|
||||||
"confirm-delete-all-events": "هل أنت متاكد أنك تريد حذف كل سجل اﻻحداث؟",
|
"filters-apply": "Apply Filters",
|
||||||
"filters": "تصفية",
|
"filter-type": "Event Type",
|
||||||
"filters-apply": "تطبيق التصفية",
|
"filter-start": "Start Date",
|
||||||
"filter-type": "نوع الحدث",
|
"filter-end": "End Date",
|
||||||
"filter-start": "تاريخ البدء",
|
"filter-perPage": "Per Page"
|
||||||
"filter-end": "تاريخ الانتهاء",
|
|
||||||
"filter-perPage": "لكل صفحة"
|
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,16 @@
|
|||||||
{
|
{
|
||||||
"customise": "Customise",
|
"custom-css": "Custom CSS/LESS",
|
||||||
"custom-css": "Custom CSS/SASS",
|
"custom-css.description": "Enter your own CSS/LESS declarations here, which will be applied after all other styles.",
|
||||||
"custom-css.description": "Enter your own CSS/SASS declarations here, which will be applied after all other styles.",
|
"custom-css.enable": "Enable Custom CSS/LESS",
|
||||||
"custom-css.enable": "Enable Custom CSS/SASS",
|
|
||||||
|
|
||||||
"custom-js": "Javascript مخصصة",
|
"custom-js": "Javascript مخصصة",
|
||||||
"custom-js.description": "أدخل Javascript الخاص بك هنا. سيتم تنفيذها بعد تحميل الصفحة بالكامل.",
|
"custom-js.description": "أدخل Javascript الخاص بك هنا. سيتم تنفيذها بعد تحميل الصفحة بالكامل.",
|
||||||
"custom-js.enable": "تفعيل Javascript المخصصة",
|
"custom-js.enable": "تفعيل Javascript المخصصة",
|
||||||
|
|
||||||
"custom-header": "ترويسة مخصصة",
|
"custom-header": "ترويسة مخصصة",
|
||||||
"custom-header.description": "أدخل وسوم HTML المخصصة هنا (مثل: Meta Tags, وغيرها)، والتي سيتم تضمينها لجزئية <code><head></code> من ترميز المنتدى. يُسمح بعلامات البرمجة النصية، ولكن لا يُنصح بها ، نظرًا لأن علامة التبويب <a href=\"#custom-js\" data-toggle=\"tab\"> Custom Javascript </a> متاحة.",
|
"custom-header.description": "Enter custom HTML here (ex. Meta Tags, etc.), which will be appended to the <code><head></code> section of your forum's markup. Script tags are allowed, but are discouraged, as the <a href=\"#custom-js\" data-toggle=\"tab\">Custom Javascript</a> tab is available.",
|
||||||
"custom-header.enable": "تفعيل الترويسة المخصصة",
|
"custom-header.enable": "تفعيل الترويسة المخصصة",
|
||||||
|
|
||||||
"custom-css.livereload": "تفعيل إعادة التحميل المباشرة",
|
"custom-css.livereload": "تفعيل إعادة التحميل المباشرة",
|
||||||
"custom-css.livereload.description": "فعل هذا الخيار لإجبار جميع الجلسات في الأجهزة التي قمت بتسجيل الدخول فيها بحسابك على التحديث عند النقر على زر الحفظ",
|
"custom-css.livereload.description": "فعل هذا الخيار لإجبار جميع الجلسات في الأجهزة التي قمت بتسجيل الدخول فيها بحسابك على التحديث عند النقر على زر الحفظ"
|
||||||
"bsvariables": "_variables.scss",
|
|
||||||
"bsvariables.description": "Override bootstrap variables here. You can also use a tool like <a href=\"https://bootstrap.build/app\">bootstrap.build</a> and paste the output here.<br/>Changes require a rebuild & restart.",
|
|
||||||
"bsvariables.enable": "Enable _variables.scss"
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
{
|
{
|
||||||
"skins": "Skins",
|
|
||||||
"bootswatch-skins": "Bootswatch Skins",
|
|
||||||
"custom-skins": "Custom Skins",
|
|
||||||
"add-skin": "Add Skin",
|
|
||||||
"save-custom-skins": "Save Custom Skins",
|
|
||||||
"save-custom-skins-success": "Custom skins saved successfully",
|
|
||||||
"custom-skin-name": "Custom Skin Name",
|
|
||||||
"custom-skin-variables": "Custom Skin Variables",
|
|
||||||
"loading": "جاري تحميل السمات...",
|
"loading": "جاري تحميل السمات...",
|
||||||
"homepage": "الصفحة الرئيسية",
|
"homepage": "الصفحة الرئيسية",
|
||||||
"select-skin": "إختيار السمة",
|
"select-skin": "إختيار السمة",
|
||||||
"revert-skin": "Revert Skin",
|
|
||||||
"current-skin": "السمة الحالية",
|
"current-skin": "السمة الحالية",
|
||||||
"skin-updated": "تم تحديث السمة",
|
"skin-updated": "تم تحديث السمة",
|
||||||
"applied-success": "تم تطبيق السمة %1 بنجاح",
|
"applied-success": "تم تطبيق السمة %1 بنجاح",
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
{
|
{
|
||||||
"themes": "Themes",
|
|
||||||
"checking-for-installed": "جاري التحقق من القوالب المثبتة...",
|
"checking-for-installed": "جاري التحقق من القوالب المثبتة...",
|
||||||
"homepage": "الصفحة الرئيسية",
|
"homepage": "الصفحة الرئيسية",
|
||||||
"select-theme": "إختيار القالب",
|
"select-theme": "إختيار القالب",
|
||||||
"revert-theme": "Revert Theme",
|
|
||||||
"current-theme": "القالب المستخدم حالياً",
|
"current-theme": "القالب المستخدم حالياً",
|
||||||
"no-themes": "لم يتم العثور على قوالب مثبتة",
|
"no-themes": "لم يتم العثور على قوالب مثبتة",
|
||||||
"revert-confirm": "هل أنت متأكد من أنك ترغب في استعادة قااب NodeBB الافتراضي؟",
|
"revert-confirm": "هل أنت متأكد من أنك ترغب في استعادة قااب NodeBB الافتراضي؟",
|
||||||
"theme-changed": "تم تغيير القالب",
|
"theme-changed": "تم تغيير القالب",
|
||||||
"revert-success": "لقد قمت بنجاح بإستعادة القالب الأساسي لـNodeBB",
|
"revert-success": "لقد قمت بنجاح بإستعادة القالب الأساسي لـNodeBB",
|
||||||
"restart-to-activate": "يرجى إعادة بناء وإعادة تشغيل NodeBB لتنشيط هذا الثيم."
|
"restart-to-activate": "Please rebuild and restart your NodeBB to fully activate this theme."
|
||||||
}
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
{
|
|
||||||
"forum-traffic": "حركة المنتدى",
|
|
||||||
"page-views": "مشاهدات الصفحات",
|
|
||||||
"unique-visitors": "زائرين فريدين",
|
|
||||||
"logins": "عمليات تسجيل الدخول",
|
|
||||||
"new-users": "مستخدم جديد",
|
|
||||||
"posts": "مشاركات",
|
|
||||||
"topics": "مواضيع",
|
|
||||||
"page-views-seven": "آخر 7 ايام",
|
|
||||||
"page-views-thirty": "آخر 30 يوماً",
|
|
||||||
"page-views-last-day": "آخر 24 ساعة",
|
|
||||||
"page-views-custom": "مدة زمنية مخصصة",
|
|
||||||
"page-views-custom-start": "بداية المدة",
|
|
||||||
"page-views-custom-end": "نهاية المده",
|
|
||||||
"page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو <code>YYYY-MM-DD</code>",
|
|
||||||
"page-views-custom-error": "الرجاء إدخال نطاق تاريخ صالح بالتنسيق <code>YYYY-MM-DD</code>",
|
|
||||||
|
|
||||||
"stats.yesterday": "الأمس",
|
|
||||||
"stats.today": "اليوم",
|
|
||||||
"stats.last-week": "الاسبوع الماضي",
|
|
||||||
"stats.this-week": "هذا الاسبوع",
|
|
||||||
"stats.last-month": "الشهر الماضي",
|
|
||||||
"stats.this-month": "هذا الشهر",
|
|
||||||
"stats.all": "كل الوقت",
|
|
||||||
|
|
||||||
"updates": "تحديثات",
|
|
||||||
"running-version": "المنتدى يعمل حاليا على <strong>NodeBB الإصدار<span id=\"version\">%1</span></strong>.",
|
|
||||||
"keep-updated": "تأكد دائما من أن NodeBB يعمل على احدث إصدار للحصول على أحدث التصحيحات الأمنية وإصلاحات الأخطاء.",
|
|
||||||
"up-to-date": "You are <strong>up-to-date</strong> <i class=\"fa fa-check\"></i>",
|
|
||||||
"upgrade-available": "A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.",
|
|
||||||
"prerelease-upgrade-available": "This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.",
|
|
||||||
"prerelease-warning": "This is a <strong>pre-release</strong> version of NodeBB. Unintended bugs may occur. <i class=\"fa fa-exclamation-triangle\"></i>",
|
|
||||||
"fallback-emailer-not-found": "مرسل البريد الإلكتروني الاحتياطي غير موجود!",
|
|
||||||
"running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator",
|
|
||||||
"latest-lookup-failed": "Failed to look up latest available version of NodeBB",
|
|
||||||
|
|
||||||
"notices": "إشعارات",
|
|
||||||
"restart-not-required": "إعادة التشغيل غير مطلوب",
|
|
||||||
"restart-required": "إعادة التشغيل مطلوبة",
|
|
||||||
"search-plugin-installed": "إضافة البحث منصبة",
|
|
||||||
"search-plugin-not-installed": "إضافة البحث غير منصبة",
|
|
||||||
"search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث",
|
|
||||||
|
|
||||||
"control-panel": "التحكم بالنظام",
|
|
||||||
"rebuild-and-restart": "أعد البناء & وأعد التشغيل",
|
|
||||||
"restart": "أعد التشغيل",
|
|
||||||
"restart-warning": "ستؤدي إعادة بناء أو إعادة تشغيل NodeBB إلى إنقطاع الاتصال بالمنتدى لبضع ثوان.",
|
|
||||||
"restart-disabled": "تم تعطيل إعادة بناء NodeBB وإعادة تشغيله حيث لا يبدو أنك تقوم بتشغيله عبر البرنامج الخفي المناسب.",
|
|
||||||
"maintenance-mode": "وضع الصيانة",
|
|
||||||
"maintenance-mode-title": "انقر هنا لإعداد وضع الصيانة لـNodeBB",
|
|
||||||
"realtime-chart-updates": "التحديث الفوري للرسم البياني",
|
|
||||||
|
|
||||||
"active-users": "المستخدمين النشطين",
|
|
||||||
"active-users.users": "الأعضاء",
|
|
||||||
"active-users.guests": "الزوار",
|
|
||||||
"active-users.total": "المجموع",
|
|
||||||
"active-users.connections": "روابط الاتصال",
|
|
||||||
|
|
||||||
"guest-registered-users": "الزائر مقابل المستخدمين المسجلين",
|
|
||||||
"guest": "الزائر",
|
|
||||||
"registered": "مسجل",
|
|
||||||
|
|
||||||
"user-presence": "تواجد المستخدمين",
|
|
||||||
"on-categories": "في قائمة الأقسام",
|
|
||||||
"reading-posts": "قراءة المشاركات",
|
|
||||||
"browsing-topics": "تصفح المواضيع",
|
|
||||||
"recent": "الأخيرة",
|
|
||||||
"unread": "غير مقروء",
|
|
||||||
|
|
||||||
"high-presence-topics": "مواضيع ذات حضور قوي",
|
|
||||||
"popular-searches": "عمليات البحث الشائعة",
|
|
||||||
|
|
||||||
"graphs.page-views": "مشاهدات الصفحة",
|
|
||||||
"graphs.page-views-registered": "زيارات الصفحات المسجلة",
|
|
||||||
"graphs.page-views-guest": "زيارات الصفحات للزوار",
|
|
||||||
"graphs.page-views-bot": "زيارات الصفحات الآلية",
|
|
||||||
"graphs.unique-visitors": "زوار فريدين",
|
|
||||||
"graphs.registered-users": "مستخدمين مسجلين",
|
|
||||||
"graphs.guest-users": "المستخدمين الزوار",
|
|
||||||
"last-restarted-by": "آخر إعادة تشغيل بواسطة",
|
|
||||||
"no-users-browsing": "لا يوجد مستخدمين يتصفحون",
|
|
||||||
|
|
||||||
"back-to-dashboard": "العودة للوحة التحكم",
|
|
||||||
"details.no-users": "لم ينضم أي مستخدم خلال الإطار الزمني المحدد",
|
|
||||||
"details.no-topics": "لم يتم نشر أي مواضيع خلال الإطار الزمني المحدد",
|
|
||||||
"details.no-searches": "لم يتم إجراء أي بحث خلال الإطار الزمني المحدد",
|
|
||||||
"details.no-logins": "لم يوثق أي تسجيل دخول خلال الإطار الزمني المحدد",
|
|
||||||
"details.logins-static": "يقوم NodeBB بحفظ بيانات الجلسة لمدة ٪1 يوم/أيام فقط، ولذا فإن هذا الجدول أدناه سيعرض فقط أحدث الجلسات النشطة",
|
|
||||||
"details.logins-login-time": "وقت تسجيل الدخول",
|
|
||||||
"start": "بدء",
|
|
||||||
"end": "إنهاء",
|
|
||||||
"filter": "تصفية"
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
{
|
{
|
||||||
"you-are-on": "أنت في <strong> %1:%2 </strong>",
|
"you-are-on": "Info - You are on <strong>%1:%2</strong>",
|
||||||
"ip": "رقم الآي بي <strong> %1 </strong>",
|
"nodes-responded": "%1 nodes responded within %2ms!",
|
||||||
"nodes-responded": "عدد %1 نقطة/نقاط استجابوا خلال %2 جزء من الثانية.",
|
"host": "host",
|
||||||
"host": "المضيف",
|
|
||||||
"primary": "primary / run jobs",
|
|
||||||
"pid": "pid",
|
"pid": "pid",
|
||||||
"nodejs": "nodejs",
|
"nodejs": "nodejs",
|
||||||
"online": "online",
|
"online": "online",
|
||||||
"git": "git",
|
"git": "git",
|
||||||
"process-memory": "process memory",
|
"memory": "memory",
|
||||||
"system-memory": "system memory",
|
|
||||||
"used-memory-process": "Used memory by process",
|
|
||||||
"used-memory-os": "Used system memory",
|
|
||||||
"total-memory-os": "Total system memory",
|
|
||||||
"load": "system load",
|
"load": "system load",
|
||||||
"cpu-usage": "cpu usage",
|
"cpu-usage": "cpu usage",
|
||||||
"uptime": "uptime",
|
"uptime": "uptime",
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
{
|
{
|
||||||
"logger": "Logger",
|
"logger-settings": "Logger Settings",
|
||||||
"logger-settings": "إعدادات المسجِّل",
|
"description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.",
|
||||||
"description": "من خلال تمكين مربعات الاختيار ، ستتلقى سجلات إلى جهازك الطرفي. إذا حددت مسارًا ، فسيتم بعد ذلك حفظ السجلات في ملف بدلاً من ذلك. يعد تسجيل HTTP مفيدًا لجمع الإحصائيات حول من ومتى وماذا يصل الأشخاص في المنتدى. بالإضافة إلى تسجيل طلبات HTTP ، يمكننا أيضًا تسجيل أحداث socket.io. يمكن أن يكون تسجيل Socket.io ، جنبًا إلى جنب مع شاشة redis-cli ، مفيدًا جدًا في تعلم العناصر الداخلية لـ NodeBB.",
|
"explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.",
|
||||||
"explanation": "ما عليك سوى تحديدأو/ إلغاء تحديد إعدادات التسجيل لتمكين أو تعطيل التسجيل أثناء التنقل. لا حاجة لإعادة التشغيل.",
|
"enable-http": "Enable HTTP logging",
|
||||||
"enable-http": "تمكين سجلات HTTP",
|
"enable-socket": "Enable socket.io event logging",
|
||||||
"enable-socket": "تفعيل تسجيل أحداث socket.io",
|
"file-path": "Path to log file",
|
||||||
"file-path": "مسار ملف السجل",
|
"file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal",
|
||||||
"file-path-placeholder": "/path/to/log/file.log ::: اتركه فارغا لاظهار السجلات لنافذة الطرفية",
|
|
||||||
|
|
||||||
"control-panel": "لوحة تحكم المسجل",
|
"control-panel": "Logger Control Panel",
|
||||||
"update-settings": "تحديث إعدادات المسجل"
|
"update-settings": "Update Logger Settings"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"plugins": "Plugins",
|
|
||||||
"trending": "Trending",
|
|
||||||
"installed": "منصبة",
|
"installed": "منصبة",
|
||||||
"active": "مفعلة",
|
"active": "مفعلة",
|
||||||
"inactive": "معطلة",
|
"inactive": "معطلة",
|
||||||
@@ -40,7 +38,7 @@
|
|||||||
"alert.upgraded": "الإضافة مرقاة",
|
"alert.upgraded": "الإضافة مرقاة",
|
||||||
"alert.installed": "الإضافة منصبة",
|
"alert.installed": "الإضافة منصبة",
|
||||||
"alert.uninstalled": "تم إلغاء تنصيب الإضافة",
|
"alert.uninstalled": "تم إلغاء تنصيب الإضافة",
|
||||||
"alert.activate-success": "Please rebuild and restart your NodeBB to fully activate this plugin",
|
"alert.activate-success": "يرجى إعادة تشغيل NodeBB لتنشيط الإضافة بشكل بالكامل",
|
||||||
"alert.deactivate-success": "تم تعطيل الإضافة بنجاح",
|
"alert.deactivate-success": "تم تعطيل الإضافة بنجاح",
|
||||||
"alert.upgrade-success": "Please rebuild and restart your NodeBB to fully upgrade this plugin.",
|
"alert.upgrade-success": "Please rebuild and restart your NodeBB to fully upgrade this plugin.",
|
||||||
"alert.install-success": "تم تثبيت الإضافة بنجاح، يرجى تفعيلها.",
|
"alert.install-success": "تم تثبيت الإضافة بنجاح، يرجى تفعيلها.",
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"rewards": "المكافآت",
|
"rewards": "Rewards",
|
||||||
"add-reward": "Add reward",
|
"condition-if-users": "If User's",
|
||||||
"condition-if-users": "إذا كان للمستخدم",
|
"condition-is": "Is:",
|
||||||
"condition-is": "يكون: ",
|
"condition-then": "Then:",
|
||||||
"condition-then": "عندئذ:",
|
"max-claims": "Amount of times reward is claimable",
|
||||||
"max-claims": "عدد المرات التي يمكن فيها المطالبة بالمكافأة",
|
"zero-infinite": "Enter 0 for infinite",
|
||||||
"zero-infinite": "أدخل 0 للقيمة اللانهائية",
|
"delete": "Delete",
|
||||||
"select-reward": "Select reward",
|
"enable": "Enable",
|
||||||
"delete": "احذف",
|
"disable": "Disable",
|
||||||
"enable": "مكّن",
|
"control-panel": "Rewards Control",
|
||||||
"disable": "عطّل",
|
"new-reward": "New Reward",
|
||||||
|
|
||||||
"alert.delete-success": "المكافآت المحذوفة بنجاح",
|
"alert.delete-success": "Successfully deleted reward",
|
||||||
"alert.no-inputs-found": "مكافأة غير قانونية - لم يتم العثور على مدخلات!",
|
"alert.no-inputs-found": "Illegal reward - no inputs found!",
|
||||||
"alert.save-success": "المكافآت المحفوظة بنجاح"
|
"alert.save-success": "Successfully saved rewards"
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
{
|
{
|
||||||
"widgets": "Widgets",
|
|
||||||
"available": "Available Widgets",
|
"available": "Available Widgets",
|
||||||
"explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.",
|
"explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.",
|
||||||
"none-installed": "No widgets found! Activate the widget essentials plugin in the <a href=\"%1\">plugins</a> control panel.",
|
"none-installed": "No widgets found! Activate the essential widgets plugin in the <a href=\"%1\">plugins</a> control panel.",
|
||||||
"clone-from": "Clone widgets from",
|
"clone-from": "Clone widgets from",
|
||||||
"containers.available": "Available Containers",
|
"containers.available": "Available Containers",
|
||||||
"containers.explanation": "Drag and drop on top of any active widget",
|
"containers.explanation": "Drag and drop on top of any active widget",
|
||||||
"containers.none": "None",
|
"containers.none": "None",
|
||||||
"container.well": "Well",
|
"container.well": "Well",
|
||||||
"container.jumbotron": "Jumbotron",
|
"container.jumbotron": "Jumbotron",
|
||||||
"container.card": "Card",
|
"container.panel": "Panel",
|
||||||
"container.card-header": "Card Header",
|
"container.panel-header": "Panel Header",
|
||||||
"container.card-body": "Card Body",
|
"container.panel-body": "Panel Body",
|
||||||
"container.alert": "Alert",
|
"container.alert": "Alert",
|
||||||
|
|
||||||
"alert.confirm-delete": "Are you sure you wish to delete this widget?",
|
"alert.confirm-delete": "Are you sure you wish to delete this widget?",
|
||||||
@@ -27,7 +26,5 @@
|
|||||||
"container.placeholder": "Drag and drop a container or enter HTML here.",
|
"container.placeholder": "Drag and drop a container or enter HTML here.",
|
||||||
"show-to-groups": "Show to groups",
|
"show-to-groups": "Show to groups",
|
||||||
"hide-from-groups": "Hide from groups",
|
"hide-from-groups": "Hide from groups",
|
||||||
"start-date": "Start date",
|
|
||||||
"end-date": "End date",
|
|
||||||
"hide-on-mobile": "Hide on mobile"
|
"hide-on-mobile": "Hide on mobile"
|
||||||
}
|
}
|
||||||
79
public/language/ar/admin/general/dashboard.json
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"forum-traffic": "Forum Traffic",
|
||||||
|
"page-views": "مشاهدات الصفحات",
|
||||||
|
"unique-visitors": "زائرين فريدين",
|
||||||
|
"new-users": "New Users",
|
||||||
|
"posts": "مشاركات",
|
||||||
|
"topics": "مواضيع",
|
||||||
|
"page-views-seven": "آخر 7 ايام",
|
||||||
|
"page-views-thirty": "آخر 30 يوماً",
|
||||||
|
"page-views-last-day": "آخر 24 ساعة",
|
||||||
|
"page-views-custom": "مدة زمنية مخصصة",
|
||||||
|
"page-views-custom-start": "بداية المدة",
|
||||||
|
"page-views-custom-end": "نهاية المده",
|
||||||
|
"page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو <code>YYYY-MM-DD</code>",
|
||||||
|
"page-views-custom-error": "الرجاء إدخال نطاق تاريخ صالح بالتنسيق <code>YYYY-MM-DD</code>",
|
||||||
|
|
||||||
|
"stats.yesterday": "Yesterday",
|
||||||
|
"stats.today": "Today",
|
||||||
|
"stats.last-week": "Last Week",
|
||||||
|
"stats.this-week": "This Week",
|
||||||
|
"stats.last-month": "Last Month",
|
||||||
|
"stats.this-month": "This Month",
|
||||||
|
"stats.all": "كل الوقت",
|
||||||
|
|
||||||
|
"updates": "تحديثات",
|
||||||
|
"running-version": "المنتدى يعمل حاليا على <strong>NodeBB الإصدار<span id=\"version\">%1</span></strong>.",
|
||||||
|
"keep-updated": "تأكد دائما من أن NodeBB يعمل على احدث إصدار للحصول على أحدث التصحيحات الأمنية وإصلاحات الأخطاء.",
|
||||||
|
"up-to-date": "<p>المنتدى <strong>يعمل على أحدث إصدار</strong> <i class=\"fa fa-check\"></i></p>",
|
||||||
|
"upgrade-available": "<p>A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.</p>",
|
||||||
|
"prerelease-upgrade-available": "<p>This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.</p>",
|
||||||
|
"prerelease-warning": "<p>هذه نسخة <strong>ماقبل الإصدار</strong> من NodeBB. قد تحدث أخطاء غير مقصودة. <i class=\"fa fa-exclamation-triangle\"></i></p>",
|
||||||
|
"running-in-development": "المنتدى قيد التشغيل في وضع \"المطورين\". وقد تكون هناك ثغرات أمنية مفتوحة؛ من فضلك تواصل مع مسؤول نظامك.",
|
||||||
|
"latest-lookup-failed": "<p>Failed to look up latest available version of NodeBB</p>",
|
||||||
|
|
||||||
|
"notices": "إشعارات",
|
||||||
|
"restart-not-required": "إعادة التشغيل غير مطلوب",
|
||||||
|
"restart-required": "إعادة التشغيل مطلوبة",
|
||||||
|
"search-plugin-installed": "إضافة البحث منصبة",
|
||||||
|
"search-plugin-not-installed": "إضافة البحث غير منصبة",
|
||||||
|
"search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث",
|
||||||
|
|
||||||
|
"control-panel": "التحكم بالنظام",
|
||||||
|
"rebuild-and-restart": "Rebuild & Restart",
|
||||||
|
"restart": "Restart",
|
||||||
|
"restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.",
|
||||||
|
"restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.",
|
||||||
|
"maintenance-mode": "وضع الصيانة",
|
||||||
|
"maintenance-mode-title": "انقر هنا لإعداد وضع الصيانة لـNodeBB",
|
||||||
|
"realtime-chart-updates": "التحديث الفوري للرسم البياني",
|
||||||
|
|
||||||
|
"active-users": "المستخدمين النشطين",
|
||||||
|
"active-users.users": "الأعضاء",
|
||||||
|
"active-users.guests": "الزوار",
|
||||||
|
"active-users.total": "المجموع",
|
||||||
|
"active-users.connections": "Connections",
|
||||||
|
|
||||||
|
"anonymous-registered-users": "المجهولين مقابل المستخدمين المسجلين",
|
||||||
|
"anonymous": "مجهول",
|
||||||
|
"registered": "مسجل",
|
||||||
|
|
||||||
|
"user-presence": "تواجد المستخدمين",
|
||||||
|
"on-categories": "في قائمة الأقسام",
|
||||||
|
"reading-posts": "قراءة المشاركات",
|
||||||
|
"browsing-topics": "تصفح المواضيع",
|
||||||
|
"recent": "الأخيرة",
|
||||||
|
"unread": "غير مقروء",
|
||||||
|
|
||||||
|
"high-presence-topics": "مواضيع ذات حضور قوي",
|
||||||
|
|
||||||
|
"graphs.page-views": "مشاهدات الصفحة",
|
||||||
|
"graphs.page-views-registered": "Page Views Registered",
|
||||||
|
"graphs.page-views-guest": "Page Views Guest",
|
||||||
|
"graphs.page-views-bot": "Page Views Bot",
|
||||||
|
"graphs.unique-visitors": "زوار فريدين",
|
||||||
|
"graphs.registered-users": "مستخدمين مسجلين",
|
||||||
|
"graphs.anonymous-users": "مستخدمين مجهولين",
|
||||||
|
"last-restarted-by": "Last restarted by",
|
||||||
|
"no-users-browsing": "No users browsing"
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"navigation": "Navigation",
|
|
||||||
"icon": "Icon:",
|
"icon": "Icon:",
|
||||||
"change-icon": "change",
|
"change-icon": "change",
|
||||||
"route": "Route:",
|
"route": "Route:",
|
||||||
@@ -12,8 +11,6 @@
|
|||||||
"properties": "Properties:",
|
"properties": "Properties:",
|
||||||
"groups": "Groups:",
|
"groups": "Groups:",
|
||||||
"open-new-window": "Open in a new window",
|
"open-new-window": "Open in a new window",
|
||||||
"dropdown": "Dropdown",
|
|
||||||
"dropdown-placeholder": "Place your dropdown menu items below, ie: <br/><li><a class="dropdown-item" href="https://myforum.com">Link 1</a></li>",
|
|
||||||
|
|
||||||
"btn.delete": "Delete",
|
"btn.delete": "Delete",
|
||||||
"btn.disable": "Disable",
|
"btn.disable": "Disable",
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"post-sharing": "Post Sharing",
|
"post-sharing": "Post Sharing",
|
||||||
"info-plugins-additional": "Plugins can add additional networks for sharing posts."
|
"info-plugins-additional": "Plugins can add additional networks for sharing posts.",
|
||||||
|
"save-success": "Successfully saved Post Sharing Networks!"
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
{
|
{
|
||||||
"manage-admins-and-mods": "Manage Admins & Mods",
|
|
||||||
"administrators": "Administrators",
|
"administrators": "Administrators",
|
||||||
"global-moderators": "Global Moderators",
|
"global-moderators": "Global Moderators",
|
||||||
"moderators": "Moderators",
|
|
||||||
"no-global-moderators": "No Global Moderators",
|
"no-global-moderators": "No Global Moderators",
|
||||||
"no-sub-categories": "No subcategories",
|
"moderators-of-category": "%1 Moderators",
|
||||||
"view-children": "View children (%1)",
|
|
||||||
"no-moderators": "No Moderators",
|
"no-moderators": "No Moderators",
|
||||||
"add-administrator": "Add Administrator",
|
"add-administrator": "Add Administrator",
|
||||||
"add-global-moderator": "Add Global Moderator",
|
"add-global-moderator": "Add Global Moderator",
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manage-categories": "Manage Categories",
|
|
||||||
"add-category": "Add category",
|
|
||||||
"jump-to": "Jump to...",
|
|
||||||
"settings": "اعدادات القسم",
|
"settings": "اعدادات القسم",
|
||||||
"edit-category": "Edit Category",
|
|
||||||
"privileges": "الصلاحيات",
|
"privileges": "الصلاحيات",
|
||||||
"back-to-categories": "Back to categories",
|
|
||||||
"name": "Category Name",
|
"name": "Category Name",
|
||||||
"description": "Category Description",
|
"description": "Category Description",
|
||||||
"bg-color": "Background Colour",
|
"bg-color": "Background Colour",
|
||||||
@@ -14,19 +10,12 @@
|
|||||||
"custom-class": "Custom Class",
|
"custom-class": "Custom Class",
|
||||||
"num-recent-replies": "# of Recent Replies",
|
"num-recent-replies": "# of Recent Replies",
|
||||||
"ext-link": "External Link",
|
"ext-link": "External Link",
|
||||||
"subcategories-per-page": "Subcategories per page",
|
|
||||||
"is-section": "Treat this category as a section",
|
"is-section": "Treat this category as a section",
|
||||||
"post-queue": "Post queue",
|
|
||||||
"tag-whitelist": "Tag Whitelist",
|
|
||||||
"upload-image": "Upload Image",
|
"upload-image": "Upload Image",
|
||||||
"upload": "Upload",
|
|
||||||
"select-icon": "Select Icon",
|
|
||||||
"delete-image": "Remove",
|
"delete-image": "Remove",
|
||||||
"category-image": "Category Image",
|
"category-image": "Category Image",
|
||||||
"image-and-icon": "Image & Icon",
|
|
||||||
"parent-category": "Parent Category",
|
"parent-category": "Parent Category",
|
||||||
"optional-parent-category": "(Optional) Parent Category",
|
"optional-parent-category": "(Optional) Parent Category",
|
||||||
"top-level": "Top Level",
|
|
||||||
"parent-category-none": "(None)",
|
"parent-category-none": "(None)",
|
||||||
"copy-parent": "Copy Parent",
|
"copy-parent": "Copy Parent",
|
||||||
"copy-settings": "Copy Settings From",
|
"copy-settings": "Copy Settings From",
|
||||||
@@ -37,11 +26,6 @@
|
|||||||
"enable": "Enable",
|
"enable": "Enable",
|
||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"analytics": "Analytics",
|
|
||||||
|
|
||||||
"view-category": "View category",
|
|
||||||
"set-order": "Set order",
|
|
||||||
"set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.",
|
|
||||||
|
|
||||||
"select-category": "Select Category",
|
"select-category": "Select Category",
|
||||||
"set-parent-category": "Set Parent Category",
|
"set-parent-category": "Set Parent Category",
|
||||||
@@ -58,8 +42,6 @@
|
|||||||
"privileges.no-users": "No user-specific privileges in this category.",
|
"privileges.no-users": "No user-specific privileges in this category.",
|
||||||
"privileges.section-group": "Group",
|
"privileges.section-group": "Group",
|
||||||
"privileges.group-private": "This group is private",
|
"privileges.group-private": "This group is private",
|
||||||
"privileges.inheritance-exception": "This group does not inherit privileges from registered-users group",
|
|
||||||
"privileges.banned-user-inheritance": "Banned users inherit privileges from banned-users group",
|
|
||||||
"privileges.search-group": "Add Group",
|
"privileges.search-group": "Add Group",
|
||||||
"privileges.copy-to-children": "Copy to Children",
|
"privileges.copy-to-children": "Copy to Children",
|
||||||
"privileges.copy-from-category": "Copy from Category",
|
"privileges.copy-from-category": "Copy from Category",
|
||||||
@@ -81,6 +63,7 @@
|
|||||||
"alert.create-success": "Category successfully created!",
|
"alert.create-success": "Category successfully created!",
|
||||||
"alert.none-active": "You have no active categories.",
|
"alert.none-active": "You have no active categories.",
|
||||||
"alert.create": "Create a Category",
|
"alert.create": "Create a Category",
|
||||||
|
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
|
||||||
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
|
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
|
||||||
"alert.purge-success": "Category purged!",
|
"alert.purge-success": "Category purged!",
|
||||||
"alert.copy-success": "Settings Copied!",
|
"alert.copy-success": "Settings Copied!",
|
||||||
@@ -92,9 +75,7 @@
|
|||||||
"alert.user-search": "Search for a user here...",
|
"alert.user-search": "Search for a user here...",
|
||||||
"alert.find-group": "Find a Group",
|
"alert.find-group": "Find a Group",
|
||||||
"alert.group-search": "Search for a group here...",
|
"alert.group-search": "Search for a group here...",
|
||||||
"alert.not-enough-whitelisted-tags": "Whitelisted tags are less than minimum tags, you need to create more whitelisted tags!",
|
|
||||||
"collapse-all": "Collapse All",
|
"collapse-all": "Collapse All",
|
||||||
"expand-all": "Expand All",
|
"expand-all": "Expand All",
|
||||||
"disable-on-create": "Disable on create",
|
"disable-on-create": "Disable on create"
|
||||||
"no-matches": "No matches"
|
|
||||||
}
|
}
|
||||||
@@ -9,11 +9,10 @@
|
|||||||
"default": "System default",
|
"default": "System default",
|
||||||
"default-help": "<em>System default</em> means the user has not explicitly overridden the global forum setting for digests, which is currently: "<strong>%1</strong>"",
|
"default-help": "<em>System default</em> means the user has not explicitly overridden the global forum setting for digests, which is currently: "<strong>%1</strong>"",
|
||||||
"resend": "Resend Digest",
|
"resend": "Resend Digest",
|
||||||
"resend-all-confirm": "Are you sure you wish to manually execute this digest run?",
|
"resend-all-confirm": "Are you sure you wish to mnually execute this digest run?",
|
||||||
"resent-single": "Manual digest resend completed",
|
"resent-single": "Manual digest resend completed",
|
||||||
"resent-day": "Daily digest resent",
|
"resent-day": "Daily digest resent",
|
||||||
"resent-week": "Weekly digest resent",
|
"resent-week": "Weekly digest resent",
|
||||||
"resent-biweek": "Bi-Weekly digest resent",
|
|
||||||
"resent-month": "Monthly digest resent",
|
"resent-month": "Monthly digest resent",
|
||||||
"null": "<em>Never</em>",
|
"null": "<em>Never</em>",
|
||||||
"manual-run": "Manual digest run:",
|
"manual-run": "Manual digest run:",
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
{
|
{
|
||||||
"manage-groups": "Manage Groups",
|
|
||||||
"add-group": "Add group",
|
|
||||||
"edit-group": "Edit Group",
|
|
||||||
"back-to-groups": "Back to groups",
|
|
||||||
"view-group": "View group",
|
|
||||||
"icon-and-title": "Icon & Title",
|
|
||||||
"name": "اسم المجموعة",
|
"name": "اسم المجموعة",
|
||||||
"badge": "Badge",
|
"badge": "Badge",
|
||||||
"properties": "Properties",
|
"properties": "Properties",
|
||||||
@@ -14,9 +8,6 @@
|
|||||||
"hidden": "Hidden",
|
"hidden": "Hidden",
|
||||||
"private": "Private",
|
"private": "Private",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"delete": "Delete",
|
|
||||||
"privileges": "Privileges",
|
|
||||||
"members-csv": "Members (CSV)",
|
|
||||||
"search-placeholder": "Search",
|
"search-placeholder": "Search",
|
||||||
"create": "Create Group",
|
"create": "Create Group",
|
||||||
"description-placeholder": "A short description about your group",
|
"description-placeholder": "A short description about your group",
|
||||||
@@ -45,5 +36,6 @@
|
|||||||
"revert": "Revert",
|
"revert": "Revert",
|
||||||
|
|
||||||
"edit.no-users-found": "No Users Found",
|
"edit.no-users-found": "No Users Found",
|
||||||
"edit.confirm-remove-user": "Are you sure you want to remove this user?"
|
"edit.confirm-remove-user": "Are you sure you want to remove this user?",
|
||||||
|
"edit.save-success": "Changes saved!"
|
||||||
}
|
}
|
||||||
11
public/language/ar/admin/manage/post-queue.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"post-queue": "Post Queue",
|
||||||
|
"description": "There are no posts in the post queue. <br> To enable this feature, go to <a href=\"%1\">Settings → Post → Post Queue</a> and enable <strong>Post Queue</strong>.",
|
||||||
|
"user": "User",
|
||||||
|
"category": "Category",
|
||||||
|
"title": "Title",
|
||||||
|
"content": "Content",
|
||||||
|
"posted": "Posted",
|
||||||
|
"reply-to": "Reply to \"%1\"",
|
||||||
|
"content-editable": "You can click on individual content to edit before posting."
|
||||||
|
}
|
||||||
@@ -1,19 +1,13 @@
|
|||||||
{
|
{
|
||||||
"manage-privileges": "Manage Privileges",
|
|
||||||
"discard-changes": "Discard changes",
|
|
||||||
"global": "Global",
|
"global": "Global",
|
||||||
"admin": "Admin",
|
"global.no-users": "No user-specific global privileges.",
|
||||||
"group-privileges": "Group Privileges",
|
"group-privileges": "Group Privileges",
|
||||||
"user-privileges": "User Privileges",
|
"user-privileges": "User Privileges",
|
||||||
"edit-privileges": "Edit Privileges",
|
|
||||||
"select-clear-all": "Select/Clear All",
|
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"upload-images": "Upload Images",
|
"upload-images": "Upload Images",
|
||||||
"upload-files": "Upload Files",
|
"upload-files": "Upload Files",
|
||||||
"signature": "Signature",
|
"signature": "Signature",
|
||||||
"ban": "Ban",
|
"ban": "Ban",
|
||||||
"mute": "Mute",
|
|
||||||
"invite": "Invite",
|
|
||||||
"search-content": "Search Content",
|
"search-content": "Search Content",
|
||||||
"search-users": "Search Users",
|
"search-users": "Search Users",
|
||||||
"search-tags": "Search Tags",
|
"search-tags": "Search Tags",
|
||||||
@@ -28,7 +22,6 @@
|
|||||||
"access-topics": "Access Topics",
|
"access-topics": "Access Topics",
|
||||||
"create-topics": "Create Topics",
|
"create-topics": "Create Topics",
|
||||||
"reply-to-topics": "Reply to Topics",
|
"reply-to-topics": "Reply to Topics",
|
||||||
"schedule-topics": "Schedule Topics",
|
|
||||||
"tag-topics": "Tag Topics",
|
"tag-topics": "Tag Topics",
|
||||||
"edit-posts": "Edit Posts",
|
"edit-posts": "Edit Posts",
|
||||||
"view-edit-history": "View Edit History",
|
"view-edit-history": "View Edit History",
|
||||||
@@ -38,28 +31,5 @@
|
|||||||
"downvote-posts": "Downvote Posts",
|
"downvote-posts": "Downvote Posts",
|
||||||
"delete-topics": "Delete Topics",
|
"delete-topics": "Delete Topics",
|
||||||
"purge": "Purge",
|
"purge": "Purge",
|
||||||
"moderate": "Moderate",
|
"moderate": "Moderate"
|
||||||
"admin-dashboard": "Dashboard",
|
|
||||||
"admin-categories": "Categories",
|
|
||||||
"admin-privileges": "Privileges",
|
|
||||||
"admin-users": "Users",
|
|
||||||
"admin-admins-mods": "Admins & Mods",
|
|
||||||
"admin-groups": "Groups",
|
|
||||||
"admin-tags": "Tags",
|
|
||||||
"admin-settings": "Settings",
|
|
||||||
|
|
||||||
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
|
|
||||||
"alert.confirm-admins-mods": "<strong>Are you sure you wish to grant the "Admins & Mods" privilege to this user/group?</strong> Users with this privilege are able to promote and demote other users into privileged positions, <em>including super administrator</em>",
|
|
||||||
"alert.confirm-save": "Please confirm your intention to save these privileges",
|
|
||||||
"alert.confirm-discard": "Are you sure you wish to discard your privilege changes?",
|
|
||||||
"alert.discarded": "Privilege changes discarded",
|
|
||||||
"alert.confirm-copyToAll": "Are you sure you wish to apply this set of <strong>%1</strong> to <strong>all categories</strong>?",
|
|
||||||
"alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of <strong>%1</strong> to <strong>all categories</strong>?",
|
|
||||||
"alert.confirm-copyToChildren": "Are you sure you wish to apply this set of <strong>%1</strong> to <strong>all descendant (child) categories</strong>?",
|
|
||||||
"alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of <strong>%1</strong> to <strong>all descendant (child) categories</strong>?",
|
|
||||||
"alert.no-undo": "<em>This action cannot be undone.</em>",
|
|
||||||
"alert.admin-warning": "Administrators implicitly get all privileges",
|
|
||||||
"alert.copyPrivilegesFrom-title": "Select a category to copy from",
|
|
||||||
"alert.copyPrivilegesFrom-warning": "This will copy <strong>%1</strong> from the selected category.",
|
|
||||||
"alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of <strong>%1</strong> from the selected category."
|
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"queue": "الطابور",
|
"queue": "Queue",
|
||||||
"description": "There are no users in the registration queue. <br> To enable this feature, go to <a href=\"%1\">Settings → User → User Registration</a> and set <strong>Registration Type</strong> to \"Admin Approval\".",
|
"description": "There are no users in the registration queue. <br> To enable this feature, go to <a href=\"%1\">Settings → User → User Registration</a> and set <strong>Registration Type</strong> to \"Admin Approval\".",
|
||||||
|
|
||||||
"list.name": "اﻹسم",
|
"list.name": "Name",
|
||||||
"list.email": "البريد الإلكتروني",
|
"list.email": "Email",
|
||||||
"list.ip": "IP",
|
"list.ip": "IP",
|
||||||
"list.time": "التوقيت",
|
"list.time": "Time",
|
||||||
"list.username-spam": "Frequency: %1 Appears: %2 Confidence: %3",
|
"list.username-spam": "Frequency: %1 Appears: %2 Confidence: %3",
|
||||||
"list.email-spam": "Frequency: %1 Appears: %2",
|
"list.email-spam": "Frequency: %1 Appears: %2",
|
||||||
"list.ip-spam": "Frequency: %1 Appears: %2",
|
"list.ip-spam": "Frequency: %1 Appears: %2",
|
||||||
|
|
||||||
"invitations": "الدعوات",
|
"invitations": "Invitations",
|
||||||
"invitations.description": "Below is a complete list of invitations sent. Use ctrl-f to search through the list by email or username. <br><br>The username will be displayed to the right of the emails for users who have redeemed their invitations.",
|
"invitations.description": "Below is a complete list of invitations sent. Use ctrl-f to search through the list by email or username. <br><br>The username will be displayed to the right of the emails for users who have redeemed their invitations.",
|
||||||
"invitations.inviter-username": "أسم المستخدم الداعي",
|
"invitations.inviter-username": "Inviter Username",
|
||||||
"invitations.invitee-email": "البريد اﻻلكتروني للمدعو",
|
"invitations.invitee-email": "Invitee Email",
|
||||||
"invitations.invitee-username": "اسم المستخم للمدعو (اذا كان مسجل)",
|
"invitations.invitee-username": "Invitee Username (if registered)",
|
||||||
|
|
||||||
"invitations.confirm-delete": "هل أنت متأكد من أنك تريد حذف هذه الدعوة؟"
|
"invitations.confirm-delete": "Are you sure you wish to delete this invitation?"
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
{
|
{
|
||||||
"manage-tags": "Manage Tags",
|
|
||||||
"none": "Your forum does not have any topics with tags yet.",
|
"none": "Your forum does not have any topics with tags yet.",
|
||||||
"bg-color": "Background Colour",
|
"bg-color": "Background Colour",
|
||||||
"text-color": "Text Colour",
|
"text-color": "Text Colour",
|
||||||
"description": "Select tags by clicking or dragging, use <code>CTRL</code> to select multiple tags.",
|
"create-modify": "Create & Modify Tags",
|
||||||
|
"description": "Select tags via clicking and/or dragging, use shift to select multiple.",
|
||||||
"create": "Create Tag",
|
"create": "Create Tag",
|
||||||
"add-tag": "Add tag",
|
|
||||||
"modify": "Modify Tags",
|
"modify": "Modify Tags",
|
||||||
"rename": "Rename Tags",
|
"rename": "Rename Tags",
|
||||||
"delete": "Delete Selected Tags",
|
"delete": "Delete Selected Tags",
|
||||||
"search": "Search for tags...",
|
"search": "Search for tags...",
|
||||||
"settings": "Tags Settings",
|
"settings": "Click <a href=\"%1\">here</a> to visit the tag settings page.",
|
||||||
"name": "Tag Name",
|
"name": "Tag Name",
|
||||||
|
|
||||||
"alerts.editing": "Editing tag(s)",
|
"alerts.editing-multiple": "Editing multiple tags",
|
||||||
|
"alerts.editing-x": "Editing \"%1\" tag",
|
||||||
"alerts.confirm-delete": "Do you want to delete the selected tags?",
|
"alerts.confirm-delete": "Do you want to delete the selected tags?",
|
||||||
"alerts.update-success": "Tag Updated!",
|
"alerts.update-success": "Tag Updated!"
|
||||||
"reset-colors": "Reset colors"
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
{
|
{
|
||||||
"manage-uploads": "Manage Uploads",
|
|
||||||
"upload-file": "Upload File",
|
"upload-file": "Upload File",
|
||||||
"filename": "Filename",
|
"filename": "Filename",
|
||||||
"usage": "Post Usage",
|
"usage": "Post Usage",
|
||||||
"orphaned": "Orphaned",
|
"orphaned": "Orphaned",
|
||||||
"size/filecount": "Size / Filecount",
|
"size/filecount": "Size / Filecount",
|
||||||
"confirm-delete": "Do you really want to delete this file?",
|
"confirm-delete": "Do you really want to delete this file?",
|
||||||
"filecount": "%1 files",
|
"filecount": "%1 files"
|
||||||
"new-folder": "New Folder",
|
|
||||||
"name-new-folder": "Enter a name for new the folder"
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"manage-users": "Manage Users",
|
|
||||||
"users": "المستخدمين",
|
"users": "المستخدمين",
|
||||||
"edit": "Actions",
|
"edit": "تحرير",
|
||||||
"make-admin": "Make Admin",
|
"make-admin": "Make Admin",
|
||||||
"remove-admin": "Remove Admin",
|
"remove-admin": "Remove Admin",
|
||||||
"validate-email": "Validate Email",
|
"validate-email": "Validate Email",
|
||||||
@@ -13,19 +12,23 @@
|
|||||||
"unban": "Unban User(s)",
|
"unban": "Unban User(s)",
|
||||||
"reset-lockout": "Reset Lockout",
|
"reset-lockout": "Reset Lockout",
|
||||||
"reset-flags": "Reset Flags",
|
"reset-flags": "Reset Flags",
|
||||||
"delete": "Delete <strong>User(s)</strong>",
|
"delete": "Delete User(s)",
|
||||||
"delete-content": "Delete User(s) <strong>Content</strong>",
|
"purge": "Delete User(s) and Content",
|
||||||
"purge": "Delete <strong>User(s)</strong> and <strong>Content</strong>",
|
|
||||||
"download-csv": "Download CSV",
|
"download-csv": "Download CSV",
|
||||||
"manage-groups": "Manage Groups",
|
"manage-groups": "Manage Groups",
|
||||||
"add-group": "Add Group",
|
"add-group": "Add Group",
|
||||||
"create": "Create User",
|
"invite": "Invite",
|
||||||
"invite": "Invite by Email",
|
|
||||||
"new": "New User",
|
"new": "New User",
|
||||||
"filter-by": "Filter by",
|
|
||||||
|
"pills.latest": "Latest Users",
|
||||||
"pills.unvalidated": "Not Validated",
|
"pills.unvalidated": "Not Validated",
|
||||||
"pills.validated": "Validated",
|
"pills.no-posts": "No Posts",
|
||||||
|
"pills.top-posters": "Top Posters",
|
||||||
|
"pills.top-rep": "Most Reputation",
|
||||||
|
"pills.inactive": "Inactive",
|
||||||
|
"pills.flagged": "Most Flagged",
|
||||||
"pills.banned": "Banned",
|
"pills.banned": "Banned",
|
||||||
|
"pills.search": "User Search",
|
||||||
|
|
||||||
"50-per-page": "50 per page",
|
"50-per-page": "50 per page",
|
||||||
"100-per-page": "100 per page",
|
"100-per-page": "100 per page",
|
||||||
@@ -49,12 +52,6 @@
|
|||||||
"users.uid": "uid",
|
"users.uid": "uid",
|
||||||
"users.username": "username",
|
"users.username": "username",
|
||||||
"users.email": "email",
|
"users.email": "email",
|
||||||
"users.no-email": "(no email)",
|
|
||||||
"users.validated": "Validated",
|
|
||||||
"users.not-validated": "Not Validated",
|
|
||||||
"users.validation-pending": "Validation Pending",
|
|
||||||
"users.validation-expired": "Validation Expired",
|
|
||||||
"users.ip": "IP",
|
|
||||||
"users.postcount": "postcount",
|
"users.postcount": "postcount",
|
||||||
"users.reputation": "reputation",
|
"users.reputation": "reputation",
|
||||||
"users.flags": "flags",
|
"users.flags": "flags",
|
||||||
@@ -68,7 +65,7 @@
|
|||||||
"create.password": "Password",
|
"create.password": "Password",
|
||||||
"create.password-confirm": "Confirm Password",
|
"create.password-confirm": "Confirm Password",
|
||||||
|
|
||||||
"temp-ban.length": "Length",
|
"temp-ban.length": "Ban Length",
|
||||||
"temp-ban.reason": "Reason <span class=\"text-muted\">(Optional)</span>",
|
"temp-ban.reason": "Reason <span class=\"text-muted\">(Optional)</span>",
|
||||||
"temp-ban.hours": "Hours",
|
"temp-ban.hours": "Hours",
|
||||||
"temp-ban.days": "Days",
|
"temp-ban.days": "Days",
|
||||||
@@ -96,12 +93,9 @@
|
|||||||
"alerts.validate-email-success": "Emails validated",
|
"alerts.validate-email-success": "Emails validated",
|
||||||
"alerts.validate-force-password-reset-success": "User(s) passwords have been reset and their existing sessions have been revoked.",
|
"alerts.validate-force-password-reset-success": "User(s) passwords have been reset and their existing sessions have been revoked.",
|
||||||
"alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?",
|
"alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?",
|
||||||
"alerts.password-reset-email-sent": "Password reset email sent.",
|
"alerts.confirm-delete": "<b>Warning!</b><br/>Do you really want to delete user(s)?<br/> This action is not reversable! Only the user account will be deleted, their posts and topics will remain.",
|
||||||
"alerts.confirm-delete": "<strong>Warning!</strong><p>Do you really want to delete <strong>user(s)</strong>?</p><p>This action is not reversible! Only the user account will be deleted, their posts and topics will remain.</p>",
|
|
||||||
"alerts.delete-success": "User(s) Deleted!",
|
"alerts.delete-success": "User(s) Deleted!",
|
||||||
"alerts.confirm-delete-content": "<strong>Warning!</strong><p>Do you really want to delete these user(s) <strong>content</strong>?</p><p>This action is not reversible! The users' accounts will remain, but their posts and topics will be deleted.</p>",
|
"alerts.confirm-purge": "<b>Warning!</b><br/>Do you really want to delete user(s) and their content?<br/> This action is not reversable! All user data and content will be erased!",
|
||||||
"alerts.delete-content-success": "User(s) Content Deleted!",
|
|
||||||
"alerts.confirm-purge": "<strong>Warning!</strong><p>Do you really want to delete <strong>user(s) and their content</strong>?</p><p>This action is not reversible! All user data and content will be erased!</p>",
|
|
||||||
"alerts.create": "Create User",
|
"alerts.create": "Create User",
|
||||||
"alerts.button-create": "Create",
|
"alerts.button-create": "Create",
|
||||||
"alerts.button-cancel": "Cancel",
|
"alerts.button-cancel": "Cancel",
|
||||||
@@ -111,7 +105,5 @@
|
|||||||
|
|
||||||
"alerts.prompt-email": "Emails: ",
|
"alerts.prompt-email": "Emails: ",
|
||||||
"alerts.email-sent-to": "An invitation email has been sent to %1",
|
"alerts.email-sent-to": "An invitation email has been sent to %1",
|
||||||
"alerts.x-users-found": "%1 user(s) found, (%2 seconds)",
|
"alerts.x-users-found": "%1 user(s) found! Search took %2 ms."
|
||||||
"export-users-started": "Exporting users as csv, this might take a while. You will receive a notification when it is complete.",
|
|
||||||
"export-users-completed": "Users exported as csv, click here to download."
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"section-dashboard": "Dashboards",
|
|
||||||
"dashboard/overview": "Overview",
|
|
||||||
"dashboard/logins": "Logins",
|
|
||||||
"dashboard/users": "Users",
|
|
||||||
"dashboard/topics": "Topics",
|
|
||||||
"dashboard/searches": "Searches",
|
|
||||||
"section-general": "عام",
|
"section-general": "عام",
|
||||||
|
"general/dashboard": "اللوحة الرئيسية",
|
||||||
|
"general/homepage": "الصفحة الرئيسية",
|
||||||
|
"general/navigation": "التصفح",
|
||||||
|
"general/languages": "اللغات",
|
||||||
|
"general/sounds": "الأصوات",
|
||||||
|
"general/social": "شبكات التواصل",
|
||||||
|
|
||||||
"section-manage": "إدارة",
|
"section-manage": "إدارة",
|
||||||
"manage/categories": "الأقسام",
|
"manage/categories": "الأقسام",
|
||||||
@@ -22,23 +22,17 @@
|
|||||||
|
|
||||||
"section-settings": "إعدادات",
|
"section-settings": "إعدادات",
|
||||||
"settings/general": "عامة",
|
"settings/general": "عامة",
|
||||||
"settings/homepage": "Home Page",
|
"settings/reputation": "السمعة",
|
||||||
"settings/navigation": "Navigation",
|
|
||||||
"settings/reputation": "Reputation & Flags",
|
|
||||||
"settings/email": "البريد الإلكتروني",
|
"settings/email": "البريد الإلكتروني",
|
||||||
"settings/user": "Users",
|
"settings/user": "الأعضاء",
|
||||||
"settings/group": "Groups",
|
"settings/group": "المجموعات",
|
||||||
"settings/guest": "الزوار",
|
"settings/guest": "الزوار",
|
||||||
"settings/uploads": "الرفع",
|
"settings/uploads": "الرفع",
|
||||||
"settings/languages": "Languages",
|
"settings/post": "المشاركة",
|
||||||
"settings/post": "Posts",
|
"settings/chat": "الدردشة",
|
||||||
"settings/chat": "Chats",
|
|
||||||
"settings/pagination": "ترقيم الصفحات",
|
"settings/pagination": "ترقيم الصفحات",
|
||||||
"settings/tags": "الكلمات المفتاحية",
|
"settings/tags": "الكلمات المفتاحية",
|
||||||
"settings/notifications": "التنبيهات",
|
"settings/notifications": "التنبيهات",
|
||||||
"settings/api": "API Access",
|
|
||||||
"settings/sounds": "Sounds",
|
|
||||||
"settings/social": "Social",
|
|
||||||
"settings/cookies": "Cookies",
|
"settings/cookies": "Cookies",
|
||||||
"settings/web-crawler": "Web Crawler",
|
"settings/web-crawler": "Web Crawler",
|
||||||
"settings/sockets": "Sockets",
|
"settings/sockets": "Sockets",
|
||||||
@@ -72,13 +66,11 @@
|
|||||||
"development/info": "Info",
|
"development/info": "Info",
|
||||||
|
|
||||||
"rebuild-and-restart-forum": "Rebuild & Restart Forum",
|
"rebuild-and-restart-forum": "Rebuild & Restart Forum",
|
||||||
"rebuild-and-restart": "Rebuild & Restart",
|
|
||||||
"restart-forum": "Restart Forum",
|
"restart-forum": "Restart Forum",
|
||||||
"restart": "Restart",
|
|
||||||
"logout": "Log out",
|
"logout": "Log out",
|
||||||
"view-forum": "View Forum",
|
"view-forum": "View Forum",
|
||||||
|
|
||||||
"search.placeholder": "Search settings",
|
"search.placeholder": "Search for settings",
|
||||||
"search.no-results": "No results...",
|
"search.no-results": "No results...",
|
||||||
"search.search-forum": "Search the forum for <strong></strong>",
|
"search.search-forum": "Search the forum for <strong></strong>",
|
||||||
"search.keep-typing": "Type more to see results...",
|
"search.keep-typing": "Type more to see results...",
|
||||||
|
|||||||
@@ -3,11 +3,8 @@
|
|||||||
"maintenance-mode.help": "When the forum is in maintenance mode, all requests will be redirected to a static holding page. Administrators are exempt from this redirection, and are able to access the site normally.",
|
"maintenance-mode.help": "When the forum is in maintenance mode, all requests will be redirected to a static holding page. Administrators are exempt from this redirection, and are able to access the site normally.",
|
||||||
"maintenance-mode.status": "Maintenance Mode Status Code",
|
"maintenance-mode.status": "Maintenance Mode Status Code",
|
||||||
"maintenance-mode.message": "Maintenance Message",
|
"maintenance-mode.message": "Maintenance Message",
|
||||||
"maintenance-mode.groups-exempt-from-maintenance-mode": "Select groups that should be exempt from maintenance mode",
|
|
||||||
"headers": "Headers",
|
"headers": "Headers",
|
||||||
"headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame",
|
"headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame",
|
||||||
"headers.csp-frame-ancestors": "Set Content-Security-Policy frame-ancestors header to Place NodeBB in an iFrame",
|
|
||||||
"headers.csp-frame-ancestors-help": "'none', 'self'(default) or list of URIs to allow.",
|
|
||||||
"headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB",
|
"headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB",
|
||||||
"headers.acao": "Access-Control-Allow-Origin",
|
"headers.acao": "Access-Control-Allow-Origin",
|
||||||
"headers.acao-regex": "Access-Control-Allow-Origin Regular Expression",
|
"headers.acao-regex": "Access-Control-Allow-Origin Regular Expression",
|
||||||
@@ -16,35 +13,16 @@
|
|||||||
"headers.acac": "Access-Control-Allow-Credentials",
|
"headers.acac": "Access-Control-Allow-Credentials",
|
||||||
"headers.acam": "Access-Control-Allow-Methods",
|
"headers.acam": "Access-Control-Allow-Methods",
|
||||||
"headers.acah": "Access-Control-Allow-Headers",
|
"headers.acah": "Access-Control-Allow-Headers",
|
||||||
"headers.coep": "Cross-Origin-Embedder-Policy",
|
|
||||||
"headers.coep-help": "When enabled (default), will set the header to <code>require-corp</code>",
|
|
||||||
"headers.coop": "Cross-Origin-Opener-Policy",
|
|
||||||
"headers.corp": "Cross-Origin-Resource-Policy",
|
|
||||||
"headers.permissions-policy": "Permissions-Policy",
|
|
||||||
"headers.permissions-policy-help": "Allows setting permissions policy header, for example \"geolocation=*, camera=()\", see <a href=\"https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md\">this</a> for more info.",
|
|
||||||
"hsts": "Strict Transport Security",
|
"hsts": "Strict Transport Security",
|
||||||
"hsts.enabled": "Enabled HSTS (recommended)",
|
"hsts.enabled": "Enabled HSTS (recommended)",
|
||||||
"hsts.maxAge": "HSTS Max Age",
|
|
||||||
"hsts.subdomains": "Include subdomains in HSTS header",
|
"hsts.subdomains": "Include subdomains in HSTS header",
|
||||||
"hsts.preload": "Allow preloading of HSTS header",
|
"hsts.preload": "Allow preloading of HSTS header",
|
||||||
"hsts.help": "If enabled, an HSTS header will be set for this site. You can elect to include subdomains and preloading flags in your header. If in doubt, you can leave these unchecked. <a href=\"%1\">More information <i class=\"fa fa-external-link\"></i></a>",
|
"hsts.help": "If enabled, an HSTS header will be set for this site. You can elect to include subdomains and preloading flags in your header. If in doubt, you can leave these unchecked. <a href=\"%1\">More information <i class=\"fa fa-external-link\"></i></a>",
|
||||||
"traffic-management": "Traffic Management",
|
"traffic-management": "Traffic Management",
|
||||||
"traffic.help": "NodeBB uses a module that automatically denies requests in high-traffic situations. You can tune these settings here, although the defaults are a good starting point.",
|
"traffic.help": "NodeBB deploys equipped with a module that automatically denies requests in high-traffic situations. You can tune these settings here, although the defaults are a good starting point.",
|
||||||
"traffic.enable": "Enable Traffic Management",
|
"traffic.enable": "Enable Traffic Management",
|
||||||
"traffic.event-lag": "Event Loop Lag Threshold (in milliseconds)",
|
"traffic.event-lag": "Event Loop Lag Threshold (in milliseconds)",
|
||||||
"traffic.event-lag-help": "Lowering this value decreases wait times for page loads, but will also show the \"excessive load\" message to more users. (Restart required)",
|
"traffic.event-lag-help": "Lowering this value decreases wait times for page loads, but will also show the \"excessive load\" message to more users. (Restart required)",
|
||||||
"traffic.lag-check-interval": "Check Interval (in milliseconds)",
|
"traffic.lag-check-interval": "Check Interval (in milliseconds)",
|
||||||
"traffic.lag-check-interval-help": "Lowering this value causes NodeBB to become more sensitive to spikes in load, but may also cause the check to become too sensitive. (Restart required)",
|
"traffic.lag-check-interval-help": "Lowering this value causes NodeBB to become more sensitive to spikes in load, but may also cause the check to become too sensitive. (Restart required)"
|
||||||
|
|
||||||
"sockets.settings": "WebSocket Settings",
|
|
||||||
"sockets.max-attempts": "Max Reconnection Attempts",
|
|
||||||
"sockets.default-placeholder": "Default: %1",
|
|
||||||
"sockets.delay": "Reconnection Delay",
|
|
||||||
|
|
||||||
"analytics.settings": "Analytics Settings",
|
|
||||||
"analytics.max-cache": "Analytics Cache Max Value",
|
|
||||||
"analytics.max-cache-help": "On high-traffic installs, the cache could be exhausted continuously if there are more concurrent active users than the Max Cache value. (Restart required)",
|
|
||||||
"compression.settings": "Compression Settings",
|
|
||||||
"compression.enable": "Enable Compression",
|
|
||||||
"compression.help": "This setting enables gzip compression. For a high-traffic website in production, the best way to put compression in place is to implement it at a reverse proxy level. You can enable it here for testing purposes."
|
|
||||||
}
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"tokens": "Tokens",
|
|
||||||
"settings": "Settings",
|
|
||||||
"lead-text": "From this page you can configure access to the Write API in NodeBB.",
|
|
||||||
"intro": "By default, the Write API authenticates users based on their session cookie, but NodeBB also supports Bearer authentication via tokens generated via this page.",
|
|
||||||
"warning": "<strong>Be advised</strong> — treat tokens like passwords. If they are leaked, your account should be considered compromised.",
|
|
||||||
"docs": "Click here to access the full API specification",
|
|
||||||
|
|
||||||
"require-https": "Require API usage via HTTPS only",
|
|
||||||
"require-https-caveat": "<strong>Note</strong>: Some installations involving load balancers may proxy their requests to NodeBB using HTTP, in which case this option should remain disabled.",
|
|
||||||
|
|
||||||
"uid": "User ID",
|
|
||||||
"token": "Token",
|
|
||||||
"uid-help-text": "Specify a User ID to associate with this token. If the user ID is <code>0</code>, it will be considered a <em>master</em> token, which can assume the identity of other users based on the <code>_uid</code> parameter",
|
|
||||||
"description": "Description",
|
|
||||||
"last-seen": "Last seen",
|
|
||||||
"created": "Created",
|
|
||||||
"create-token": "Create Token",
|
|
||||||
"update-token": "Update Token",
|
|
||||||
"master-token": "Master token",
|
|
||||||
"last-seen-never": "This key has never been used.",
|
|
||||||
"no-description": "No description specified.",
|
|
||||||
"actions": "Actions",
|
|
||||||
"edit": "Edit",
|
|
||||||
"roll": "Roll",
|
|
||||||
|
|
||||||
"delete-confirm": "Are you sure you wish to delete this token? It will not be recoverable.",
|
|
||||||
"roll-confirm": "Are you sure you wish to regenerate this token? The old token will be immediately revoked and will not be recoverable."
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
"max-length": "Maximum length of chat messages",
|
"max-length": "Maximum length of chat messages",
|
||||||
"max-room-size": "Maximum number of users in chat rooms",
|
"max-room-size": "Maximum number of users in chat rooms",
|
||||||
"delay": "Time between chat messages in milliseconds",
|
"delay": "Time between chat messages in milliseconds",
|
||||||
"notification-delay": "Notification delay for chat messages. (0 for no delay)",
|
|
||||||
"restrictions.seconds-edit-after": "Number of seconds a chat message will remain editable. (0 disabled)",
|
"restrictions.seconds-edit-after": "Number of seconds a chat message will remain editable. (0 disabled)",
|
||||||
"restrictions.seconds-delete-after": "Number of seconds a chat message will remain deletable. (0 disabled)"
|
"restrictions.seconds-delete-after": "Number of seconds a chat message will remain deletable. (0 disabled)"
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,5 @@
|
|||||||
"consent.blank-localised-default": "Leave blank to use NodeBB localised defaults",
|
"consent.blank-localised-default": "Leave blank to use NodeBB localised defaults",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"cookie-domain": "Session cookie domain",
|
"cookie-domain": "Session cookie domain",
|
||||||
"max-user-sessions": "Max active sessions per user",
|
|
||||||
"blank-default": "Leave blank for default"
|
"blank-default": "Leave blank for default"
|
||||||
}
|
}
|
||||||
@@ -4,19 +4,18 @@
|
|||||||
"address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.",
|
"address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.",
|
||||||
"from": "From Name",
|
"from": "From Name",
|
||||||
"from-help": "The from name to display in the email.",
|
"from-help": "The from name to display in the email.",
|
||||||
|
"sendmail-rate-limit": "Send <em>X</em> emails...",
|
||||||
"confirmation-settings": "Confirmation",
|
"sendmail-rate-delta": "... every <em>X</em> milliseconds",
|
||||||
"confirmation.expiry": "Hours to keep email confirmation link valid",
|
"sendmail-rate-help": "Instructs the NodeBB mailer to limit the number of messages sent at once in order to not overwhelm email receiving services. These options do not apply if SMTP Transport is enabled (below).",
|
||||||
|
|
||||||
"smtp-transport": "SMTP Transport",
|
"smtp-transport": "SMTP Transport",
|
||||||
"smtp-transport.enabled": "Enable SMTP Transport",
|
"smtp-transport.enabled": "Use an external email server to send emails",
|
||||||
"smtp-transport-help": "You can select from a list of well-known services or enter a custom one.",
|
"smtp-transport-help": "You can select from a list of well-known services or enter a custom one.",
|
||||||
"smtp-transport.service": "Select a service",
|
"smtp-transport.service": "Select a service",
|
||||||
"smtp-transport.service-custom": "Custom Service",
|
"smtp-transport.service-custom": "Custom Service",
|
||||||
"smtp-transport.service-help": "Select a service name above in order to use the known information about it. Alternatively, select "Custom Service" and enter the details below.",
|
"smtp-transport.service-help": "Select a service name above in order to use the known information about it. Alternatively, select 'Custom Service' and enter the details below.",
|
||||||
"smtp-transport.gmail-warning1": "If you are using GMail as your email provider, you will have to generate an "App Password" in order for NodeBB to authenticate successfully. You can generate one at the <a href=\"https://myaccount.google.com/apppasswords\">App Passwords <i class=\"fa fa-external-link\"></i></a> page.",
|
"smtp-transport.gmail-warning1": "There have been reports of the Gmail service not working on accounts with heightened security. In those scenarios, you will have to <a href=\"https://www.google.com/settings/security/lesssecureapps\">configure your GMail account to allow less secure apps</a>.",
|
||||||
"smtp-transport.gmail-warning2": "For more information about this workaround, <a href=\"https://nodemailer.com/usage/using-gmail/\">please consult this NodeMailer article <i class=\"fa fa-external-link\"></i></a> on the issue. An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. <a href=\"../extend/plugins\">Browse available plugins here</a>.",
|
"smtp-transport.gmail-warning2": "For more information about this workaround, <a href=\"https://nodemailer.com/usage/using-gmail/\">please consult this NodeMailer article on the issue.</a> An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. <a href=\"../extend/plugins\">Browse available plugins here</a>.",
|
||||||
"smtp-transport.auto-enable-toast": "It looks like you're configuring an SMTP transport. We enabled the \"SMTP Transport\" option for you.",
|
|
||||||
"smtp-transport.host": "SMTP Host",
|
"smtp-transport.host": "SMTP Host",
|
||||||
"smtp-transport.port": "SMTP Port",
|
"smtp-transport.port": "SMTP Port",
|
||||||
"smtp-transport.security": "Connection security",
|
"smtp-transport.security": "Connection security",
|
||||||
@@ -26,8 +25,6 @@
|
|||||||
"smtp-transport.username": "Username",
|
"smtp-transport.username": "Username",
|
||||||
"smtp-transport.username-help": "<b>For the Gmail service,</b> enter the full email address here, especially if you are using a Google Apps managed domain.",
|
"smtp-transport.username-help": "<b>For the Gmail service,</b> enter the full email address here, especially if you are using a Google Apps managed domain.",
|
||||||
"smtp-transport.password": "Password",
|
"smtp-transport.password": "Password",
|
||||||
"smtp-transport.pool": "Enable pooled connections",
|
|
||||||
"smtp-transport.pool-help": "Pooling connections prevents NodeBB from creating a new connection for every email. This option only applies if SMTP Transport is enabled.",
|
|
||||||
|
|
||||||
"template": "Edit Email Template",
|
"template": "Edit Email Template",
|
||||||
"template.select": "Select Email Template",
|
"template.select": "Select Email Template",
|
||||||
@@ -39,14 +36,5 @@
|
|||||||
"subscriptions": "Email Digests",
|
"subscriptions": "Email Digests",
|
||||||
"subscriptions.disable": "Disable email digests",
|
"subscriptions.disable": "Disable email digests",
|
||||||
"subscriptions.hour": "Digest Hour",
|
"subscriptions.hour": "Digest Hour",
|
||||||
"subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. <code>0</code> for midnight, <code>17</code> for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.<br /> The approximate server time is: <span id=\"serverTime\"></span><br /> The next daily digest is scheduled to be sent <span id=\"nextDigestTime\"></span>",
|
"subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. <code>0</code> for midnight, <code>17</code> for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.<br /> The approximate server time is: <span id=\"serverTime\"></span><br /> The next daily digest is scheduled to be sent <span id=\"nextDigestTime\"></span>"
|
||||||
"notifications.remove-images": "Remove images from email notifications",
|
|
||||||
"require-email-address": "Require new users to specify an email address",
|
|
||||||
"require-email-address-warning": "By default, users can opt-out of entering an email address by leaving the field blank. Enabling this option means new users will have to enter <strong>and confirm</strong> an email address in order to proceed with registration and subsequent access to the forum. <strong>It does not ensure user will enter a real email address, nor even an address they own.</strong>",
|
|
||||||
"send-validation-email": "Send validation emails when an email is added or changed",
|
|
||||||
"include-unverified-emails": "Send emails to recipients who have not explicitly confirmed their emails",
|
|
||||||
"include-unverified-warning": "By default, users with emails associated with their account have already been verified, but there are situations where this is not the case (e.g. SSO logins, grandfathered users, etc). <strong>Enable this setting at your own risk</strong> – sending emails to unverified addresses may be a violation of regional anti-spam laws.",
|
|
||||||
"prompt": "Prompt users to enter or confirm their emails",
|
|
||||||
"prompt-help": "If a user does not have an email set, or their email is not confirmed, a warning will be shown on screen.",
|
|
||||||
"sendEmailToBanned": "Send emails to users even if they have been banned"
|
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,35 @@
|
|||||||
{
|
{
|
||||||
"general-settings": "General Settings",
|
"site-settings": "Site Settings",
|
||||||
"on-this-page": "On this page:",
|
"title": "Site Title",
|
||||||
"site-settings": "اعدادات الموقع",
|
"title.url": "URL",
|
||||||
"title": "عنوان الموقع",
|
|
||||||
"title.short": "عنوان قصير",
|
|
||||||
"title.short-placeholder": "ان لم تقم بكتابة عنوان مختصر, سيتم استخدام عنوان الموقع الكلي",
|
|
||||||
"title.url": "Title Link URL",
|
|
||||||
"title.url-placeholder": "The URL of the site title",
|
"title.url-placeholder": "The URL of the site title",
|
||||||
"title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index. Note: This is not the external URL used in emails, etc. That is set by the <code>url</code> property in config.json",
|
"title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.",
|
||||||
"title.name": "اسم المنتدي",
|
"title.name": "Your Community Name",
|
||||||
"title.show-in-header": "Show Site Title in Header",
|
"title.show-in-header": "Show Site Title in Header",
|
||||||
"browser-title": "عنوان المتصفح",
|
"browser-title": "Browser Title",
|
||||||
"browser-title-help": "If no browser title is specified, the site title will be used",
|
"browser-title-help": "If no browser title is specified, the site title will be used",
|
||||||
"title-layout": "Title Layout",
|
"title-layout": "Title Layout",
|
||||||
"title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}",
|
"title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}",
|
||||||
"description.placeholder": "A short description about your community",
|
"description.placeholder": "A short description about your community",
|
||||||
"description": "وصف الموقع",
|
"description": "Site Description",
|
||||||
"keywords": "الكلمات الدليله للموقع",
|
"keywords": "Site Keywords",
|
||||||
"keywords-placeholder": "Keywords describing your community, comma-separated",
|
"keywords-placeholder": "Keywords describing your community, comma-separated",
|
||||||
"logo-and-icons": "Site Logo & Icons",
|
"logo": "Site Logo",
|
||||||
"logo.image": "صورة",
|
"logo.image": "Image",
|
||||||
"logo.image-placeholder": "Path to a logo to display on forum header",
|
"logo.image-placeholder": "Path to a logo to display on forum header",
|
||||||
"logo.upload": "رفع",
|
"logo.upload": "Upload",
|
||||||
"logo.url": "Logo Link URL",
|
"logo.url": "URL",
|
||||||
"logo.url-placeholder": "The URL of the site logo",
|
"logo.url-placeholder": "The URL of the site logo",
|
||||||
"logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index. <br> Note: This is not the external URL used in emails, etc. That is set by the <code>url</code> property in config.json",
|
"logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.",
|
||||||
"logo.alt-text": "نص بديل",
|
"logo.alt-text": "Alt Text",
|
||||||
"log.alt-text-placeholder": "Alternative text for accessibility",
|
"log.alt-text-placeholder": "Alternative text for accessibility",
|
||||||
"favicon": "صورة المفضله",
|
"favicon": "Favicon",
|
||||||
"favicon.upload": "رفع",
|
"favicon.upload": "Upload",
|
||||||
"pwa": "Progressive Web App",
|
"touch-icon": "Homescreen/Touch Icon",
|
||||||
"touch-icon": "Touch Icon",
|
"touch-icon.upload": "Upload",
|
||||||
"touch-icon.upload": "رفع",
|
"touch-icon.help": "Recommended size and format: 192x192, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.",
|
||||||
"touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.",
|
|
||||||
"maskable-icon": "Maskable (Homescreen) Icon",
|
|
||||||
"maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.",
|
|
||||||
"outgoing-links": "Outgoing Links",
|
"outgoing-links": "Outgoing Links",
|
||||||
"outgoing-links.warning-page": "Use Outgoing Links Warning Page",
|
"outgoing-links.warning-page": "Use Outgoing Links Warning Page",
|
||||||
"search": "Search",
|
"search-default-sort-by": "Search default sort by",
|
||||||
"search-default-in": "Search In",
|
"outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page"
|
||||||
"search-default-in-quick": "Quick Search In",
|
|
||||||
"search-default-sort-by": "Sort by",
|
|
||||||
"outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page",
|
|
||||||
"site-colors": "Site Color Metadata",
|
|
||||||
"theme-color": "لون الثيم",
|
|
||||||
"background-color": "لون الخلفية",
|
|
||||||
"background-color-help": "Color used for splash screen background when website is installed as a PWA",
|
|
||||||
"undo-timeout": "Undo Timeout",
|
|
||||||
"undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.",
|
|
||||||
"topic-tools": "Topic Tools"
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
"private-groups": "Private Groups",
|
"private-groups": "Private Groups",
|
||||||
"private-groups.help": "If enabled, joining of groups requires the approval of the group owner <em>(Default: enabled)</em>",
|
"private-groups.help": "If enabled, joining of groups requires the approval of the group owner <em>(Default: enabled)</em>",
|
||||||
"private-groups.warning": "<strong>Beware!</strong> If this option is disabled and you have private groups, they automatically become public.",
|
"private-groups.warning": "<strong>Beware!</strong> If this option is disabled and you have private groups, they automatically become public.",
|
||||||
"allow-multiple-badges": "Allow Multiple Badges",
|
|
||||||
"allow-multiple-badges-help": "This flag can be used to allow users to select multiple group badges, requires theme support.",
|
"allow-multiple-badges-help": "This flag can be used to allow users to select multiple group badges, requires theme support.",
|
||||||
"max-name-length": "Maximum Group Name Length",
|
"max-name-length": "Maximum Group Name Length",
|
||||||
"max-title-length": "Maximum Group Title Length",
|
"max-title-length": "Maximum Group Title Length",
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
{
|
{
|
||||||
"settings": "Settings",
|
"handles": "Guest Handles",
|
||||||
"guest-settings": "Guest settings",
|
|
||||||
"handles.enabled": "Allow guest handles",
|
"handles.enabled": "Allow guest handles",
|
||||||
"handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\"",
|
"handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\""
|
||||||
"topic-views.enabled": "Allow guests to increase topic view counts",
|
|
||||||
"reply-notifications.enabled": "Allow guests to generate reply notifications"
|
|
||||||
}
|
}
|
||||||
@@ -3,5 +3,5 @@
|
|||||||
"welcome-notification": "Welcome Notification",
|
"welcome-notification": "Welcome Notification",
|
||||||
"welcome-notification-link": "Welcome Notification Link",
|
"welcome-notification-link": "Welcome Notification Link",
|
||||||
"welcome-notification-uid": "Welcome Notification User (UID)",
|
"welcome-notification-uid": "Welcome Notification User (UID)",
|
||||||
"post-queue-notification-uid": "Post Queue User (UID)"
|
"notification-alert-timeout": "Notification Alert Timeout"
|
||||||
}
|
}
|
||||||