mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-17 05:50:25 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0df3ea8661 | ||
|
|
b6c84222c2 | ||
|
|
6bcc0d0ddc | ||
|
|
e219cf0226 | ||
|
|
b5da3f136b | ||
|
|
8a02c66ed5 | ||
|
|
66946be9f0 |
@@ -10,7 +10,7 @@ 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
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
.*
|
|
||||||
logs
|
|
||||||
test
|
|
||||||
node_modules
|
|
||||||
commitlint.config.js
|
|
||||||
nodebb.bat
|
|
||||||
renovate.json
|
|
||||||
*.yml
|
|
||||||
*.md
|
|
||||||
Dockerfile
|
|
||||||
@@ -18,4 +18,3 @@ logs/
|
|||||||
.eslintrc
|
.eslintrc
|
||||||
test/files
|
test/files
|
||||||
*.min.js
|
*.min.js
|
||||||
install/docker/
|
|
||||||
114
.eslintrc
114
.eslintrc
@@ -1,3 +1,115 @@
|
|||||||
{
|
{
|
||||||
"extends": "nodebb"
|
"extends": "airbnb-base",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "script"
|
||||||
|
},
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
// === Configure rules for our style ===
|
||||||
|
// imports must be resolvable
|
||||||
|
"import/no-unresolved": "error",
|
||||||
|
// use single quotes,
|
||||||
|
// unless a different style allows avoiding escapes
|
||||||
|
"quotes": ["error", "single", {
|
||||||
|
"avoidEscape": true,
|
||||||
|
"allowTemplateLiterals": true
|
||||||
|
}],
|
||||||
|
// allow else-if return
|
||||||
|
"no-else-return": [ "error", { "allowElseIf": true } ],
|
||||||
|
// expressions split over multiple lines
|
||||||
|
// should break after the operator
|
||||||
|
"operator-linebreak": [ "error", "after" ],
|
||||||
|
// require arrow parens only when needed
|
||||||
|
// and whenever the body is a block
|
||||||
|
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
|
||||||
|
// what variables are errors in callbacks
|
||||||
|
"handle-callback-err": [ "error","^(e$|(e|(.*(_e|E)))rr)" ],
|
||||||
|
// allow dangling commas in functions
|
||||||
|
// require them everywhere else
|
||||||
|
"comma-dangle": ["error", {
|
||||||
|
"arrays": "always-multiline",
|
||||||
|
"objects": "always-multiline",
|
||||||
|
"imports": "always-multiline",
|
||||||
|
"exports": "always-multiline",
|
||||||
|
"functions": "only-multiline"
|
||||||
|
}],
|
||||||
|
// we actually encourage `return await`
|
||||||
|
"no-return-await": "off",
|
||||||
|
// allow `while (true)`
|
||||||
|
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||||
|
// allow ignoring an error with `catch`
|
||||||
|
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||||
|
// allow `3 + 5 - 1`, but not `3 * 5 - 1`
|
||||||
|
"no-mixed-operators": ["error", { "allowSamePrecedence": true }],
|
||||||
|
// require `'use strict';`
|
||||||
|
"strict": ["error", "global"],
|
||||||
|
// we actually use tabs for indentation
|
||||||
|
"indent": ["error", "tab", { "SwitchCase": 1 }],
|
||||||
|
"no-tabs": "off",
|
||||||
|
// we want `== null` to also handle undefined
|
||||||
|
"no-eq-null": "off",
|
||||||
|
// allow `for (..; i++)`
|
||||||
|
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
|
||||||
|
// allow using functions defined later
|
||||||
|
"no-use-before-define": ["error", "nofunc"],
|
||||||
|
// require consistent newlines before and after braces
|
||||||
|
// if contents are multiline
|
||||||
|
"object-curly-newline": ["error", { "consistent": true, "multiline": true }],
|
||||||
|
// require consistent linebreaks inline function parenthesis (arguments or params)
|
||||||
|
"function-paren-newline": ["error", "consistent"],
|
||||||
|
// only require const if all parts of destructuring can be const
|
||||||
|
"prefer-const": ["error", { "destructuring": "all" }],
|
||||||
|
// don't require destructuring for arrays or assignment
|
||||||
|
"prefer-destructuring": ["error", {
|
||||||
|
"VariableDeclarator": { "array": false, "object": true },
|
||||||
|
"AssignmentExpression": { "array": false, "object": false }
|
||||||
|
}],
|
||||||
|
// identical to airbnb rule, except for allowing for..of, because we want to use it
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "ForInStatement",
|
||||||
|
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "LabeledStatement",
|
||||||
|
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "WithStatement",
|
||||||
|
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// allow lines of up to 120 characters
|
||||||
|
"max-len": ["error", { "code": 120, "tabWidth": 2, "ignoreUrls": true, "ignoreStrings": true, "ignoreTemplateLiterals": true, "ignoreRegExpLiterals": true }],
|
||||||
|
|
||||||
|
// === Disable rules ===
|
||||||
|
// more liberal naming
|
||||||
|
"camelcase": "off",
|
||||||
|
"no-underscore-dangle": "off",
|
||||||
|
// don't require anonymous function names
|
||||||
|
"func-names": "off",
|
||||||
|
// allow console
|
||||||
|
"no-console": "off",
|
||||||
|
// allow new for side effects
|
||||||
|
// allow new with non-capitalized
|
||||||
|
"no-new": "off",
|
||||||
|
"new-cap": "off",
|
||||||
|
// allow shadowing variables (usually callbacks)
|
||||||
|
"no-shadow": "off",
|
||||||
|
// allow multiple empty lines in a row
|
||||||
|
"no-multiple-empty-lines": "off",
|
||||||
|
// allow not using object shorthand
|
||||||
|
"object-shorthand": "off",
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
"consistent-return": "off",
|
||||||
|
"no-restricted-globals": "off",
|
||||||
|
"no-prototype-builtins": "off",
|
||||||
|
"import/no-extraneous-dependencies": "off",
|
||||||
|
"import/no-dynamic-require": "off",
|
||||||
|
"global-require": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
|
"default-case": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
.github/ISSUE_TEMPLATE.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<!--
|
||||||
|
== 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:**
|
||||||
|
- **NodeJS version:**
|
||||||
|
<!-- (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...
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
- **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
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
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
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!**"
|
|
||||||
4
.github/SECURITY.md
vendored
4
.github/SECURITY.md
vendored
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
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.
|
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.
|
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.
|
||||||
|
|
||||||
# Bug Bounty Program
|
# 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).
|
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 following page: https://blog.nodebb.org/bounty
|
||||||
72
.github/workflows/docker.yml
vendored
72
.github/workflows/docker.yml
vendored
@@ -1,72 +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@v4
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Get current date in NST
|
|
||||||
run: echo "CURRENT_DATE_NST=$(date +'%Y%m%d-%H%M%S' -d '-3 hours -30 minutes')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
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 }}
|
|
||||||
type=raw,value=${{ env.CURRENT_DATE_NST }}
|
|
||||||
flavor: |
|
|
||||||
latest=true
|
|
||||||
|
|
||||||
- name: Cache node_modules
|
|
||||||
id: cache-node-modules
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: var-cache-node-modules
|
|
||||||
key: var-cache-node-modules-${{ hashFiles('Dockerfile', 'install/package.json') }}
|
|
||||||
|
|
||||||
- name: Build and push Docker images
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=min
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
28
.github/workflows/test.yaml
vendored
28
.github/workflows/test.yaml
vendored
@@ -14,32 +14,26 @@ defaults:
|
|||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
permissions:
|
|
||||||
checks: write # for coverallsapp/github-action to create new checks
|
|
||||||
contents: read # for actions/checkout to fetch code
|
|
||||||
name: Lint and test
|
name: Lint and test
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
node: [18, 20]
|
node: [12, 14]
|
||||||
database: [mongo-dev, mongo, redis, postgres]
|
database: [mongo-dev, mongo, redis, postgres]
|
||||||
include:
|
include:
|
||||||
# only run coverage once
|
# only run coverage once
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
node: 18
|
node: 14
|
||||||
coverage: true
|
coverage: true
|
||||||
# test under development once
|
# test under development once
|
||||||
- database: mongo-dev
|
- database: mongo-dev
|
||||||
test_env: development
|
test_env: development
|
||||||
# only run eslint once
|
# only run eslint once
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
node: 18
|
node: 14
|
||||||
database: mongo-dev
|
database: mongo-dev
|
||||||
lint: true
|
lint: true
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -48,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: 'postgres:17-alpine'
|
image: 'postgres:10-alpine'
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
@@ -63,7 +57,7 @@ jobs:
|
|||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: 'redis:7.4.2'
|
image: 'redis:2.8.9'
|
||||||
# Set health checks to wait until redis has started
|
# Set health checks to wait until redis has started
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "redis-cli ping"
|
--health-cmd "redis-cli ping"
|
||||||
@@ -75,18 +69,18 @@ jobs:
|
|||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
image: 'mongo:8.0'
|
image: 'mongo:3.2'
|
||||||
ports:
|
ports:
|
||||||
# Maps port 27017 on service container to the host
|
# Maps port 27017 on service container to the host
|
||||||
- 27017:27017
|
- 27017:27017
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- run: cp install/package.json package.json
|
- run: cp install/package.json package.json
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
|
|
||||||
@@ -192,7 +186,7 @@ jobs:
|
|||||||
run: npm run coverage
|
run: npm run coverage
|
||||||
|
|
||||||
- name: Test coverage
|
- name: Test coverage
|
||||||
uses: coverallsapp/github-action@cfd0633edbd2411b532b808ba7a8b5e04f76d2c8 # v2.3.4
|
uses: coverallsapp/github-action@1.1.3
|
||||||
if: matrix.coverage
|
if: matrix.coverage
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -200,13 +194,11 @@ jobs:
|
|||||||
parallel: true
|
parallel: true
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
permissions:
|
|
||||||
checks: write # for coverallsapp/github-action to create new checks
|
|
||||||
needs: test
|
needs: test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Coveralls Finished
|
- name: Coveralls Finished
|
||||||
uses: coverallsapp/github-action@cfd0633edbd2411b532b808ba7a8b5e04f76d2c8 # v2.3.4
|
uses: coverallsapp/github-action@1.1.3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
parallel-finished: true
|
parallel-finished: true
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -68,8 +68,4 @@ test/files/normalise-resized.jpg
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
/package.json
|
/package.json
|
||||||
*.mongodb
|
*.mongodb
|
||||||
link-plugins.sh
|
link-plugins.sh
|
||||||
test.sh
|
|
||||||
|
|
||||||
.docker/**
|
|
||||||
!**/.gitkeep
|
|
||||||
17
.jsbeautifyrc
Normal file
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
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
|
||||||
|
}
|
||||||
7549
.tx/config
7549
.tx/config
File diff suppressed because it is too large
Load Diff
6589
CHANGELOG.md
6589
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
87
Dockerfile
87
Dockerfile
@@ -1,75 +1,22 @@
|
|||||||
FROM node:lts as build
|
FROM node:lts
|
||||||
|
|
||||||
|
RUN mkdir -p /usr/src/app
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
ARG NODE_ENV
|
||||||
|
ENV NODE_ENV $NODE_ENV
|
||||||
|
|
||||||
|
COPY install/package.json /usr/src/app/package.json
|
||||||
|
|
||||||
|
RUN npm install --only=prod && \
|
||||||
|
npm cache clean --force
|
||||||
|
|
||||||
|
COPY . /usr/src/app
|
||||||
|
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
DAEMON=false \
|
daemon=false \
|
||||||
SILENT=false \
|
silent=false
|
||||||
USER=nodebb \
|
|
||||||
UID=1001 \
|
|
||||||
GID=1001
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app/
|
|
||||||
|
|
||||||
COPY . /usr/src/app/
|
|
||||||
|
|
||||||
# Install corepack to allow usage of other package managers
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
# Removing unnecessary files for us
|
|
||||||
RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \;
|
|
||||||
|
|
||||||
# Prepage package.json
|
|
||||||
RUN cp /usr/src/app/install/package.json /usr/src/app/
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& DEBIAN_FRONTEND=noninteractive \
|
|
||||||
apt-get -y --no-install-recommends install \
|
|
||||||
tini
|
|
||||||
|
|
||||||
RUN groupadd --gid ${GID} ${USER} \
|
|
||||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
|
||||||
&& chown -R ${USER}:${USER} /usr/src/app/
|
|
||||||
|
|
||||||
USER ${USER}
|
|
||||||
|
|
||||||
RUN npm install --omit=dev
|
|
||||||
# TODO: generate lockfiles for each package manager
|
|
||||||
## pnpm import \
|
|
||||||
|
|
||||||
FROM node:lts-slim AS final
|
|
||||||
|
|
||||||
ENV NODE_ENV=production \
|
|
||||||
DAEMON=false \
|
|
||||||
SILENT=false \
|
|
||||||
USER=nodebb \
|
|
||||||
UID=1001 \
|
|
||||||
GID=1001
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app/
|
|
||||||
|
|
||||||
RUN corepack enable \
|
|
||||||
&& groupadd --gid ${GID} ${USER} \
|
|
||||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
|
||||||
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
|
|
||||||
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/
|
|
||||||
|
|
||||||
COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
|
|
||||||
COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
|
|
||||||
|
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh \
|
|
||||||
&& chmod +x /usr/local/bin/tini
|
|
||||||
|
|
||||||
# TODO: Have docker-compose use environment variables to create files like setup.json and config.json.
|
|
||||||
# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate
|
|
||||||
|
|
||||||
USER ${USER}
|
|
||||||
|
|
||||||
EXPOSE 4567
|
EXPOSE 4567
|
||||||
|
|
||||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"]
|
CMD node ./nodebb build ; node ./nodebb start
|
||||||
|
|
||||||
# Utilising tini as our init system within the Docker container for graceful start-up and termination.
|
|
||||||
# Tini serves as an uncomplicated init system, adept at managing the reaping of zombie processes and forwarding signals.
|
|
||||||
# This approach is crucial to circumvent issues with unmanaged subprocesses and signal handling in containerised environments.
|
|
||||||
# By integrating tini, we enhance the reliability and stability of our Docker containers.
|
|
||||||
# Ensures smooth start-up and shutdown processes, and reliable, safe handling of signal processing.
|
|
||||||
ENTRYPOINT ["tini", "--", "entrypoint.sh"]
|
|
||||||
|
|||||||
95
Gruntfile.js
95
Gruntfile.js
@@ -20,7 +20,6 @@ const prestart = require('./src/prestart');
|
|||||||
prestart.loadConfig(configFile);
|
prestart.loadConfig(configFile);
|
||||||
|
|
||||||
const db = require('./src/database');
|
const db = require('./src/database');
|
||||||
const plugins = require('./src/plugins');
|
|
||||||
|
|
||||||
module.exports = function (grunt) {
|
module.exports = function (grunt) {
|
||||||
const args = [];
|
const args = [];
|
||||||
@@ -41,39 +40,53 @@ module.exports = function (grunt) {
|
|||||||
|
|
||||||
grunt.registerTask('init', async function () {
|
grunt.registerTask('init', async function () {
|
||||||
const done = this.async();
|
const done = this.async();
|
||||||
let pluginList = [];
|
let plugins = [];
|
||||||
if (!process.argv.includes('--core')) {
|
if (!process.argv.includes('--core')) {
|
||||||
await db.init();
|
await db.init();
|
||||||
pluginList = await plugins.getActive();
|
plugins = await db.getSortedSetRange('plugins:active', 0, -1);
|
||||||
addBaseThemes(pluginList);
|
addBaseThemes(plugins);
|
||||||
if (!pluginList.includes('nodebb-plugin-composer-default')) {
|
if (!plugins.includes('nodebb-plugin-composer-default')) {
|
||||||
pluginList.push('nodebb-plugin-composer-default');
|
plugins.push('nodebb-plugin-composer-default');
|
||||||
}
|
}
|
||||||
if (!pluginList.includes('nodebb-theme-harmony')) {
|
if (!plugins.includes('nodebb-theme-persona')) {
|
||||||
pluginList.push('nodebb-theme-harmony');
|
plugins.push('nodebb-theme-persona');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleUpdated_Client = pluginList.map(p => `node_modules/${p}/*.scss`)
|
const styleUpdated_Client = plugins.map(p => `node_modules/${p}/*.less`)
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/*.css`))
|
.concat(plugins.map(p => `node_modules/${p}/*.css`))
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/+(public|static|scss)/**/*.scss`))
|
.concat(plugins.map(p => `node_modules/${p}/+(public|static|less)/**/*.less`))
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/+(public|static)/**/*.css`));
|
.concat(plugins.map(p => `node_modules/${p}/+(public|static)/**/*.css`));
|
||||||
|
|
||||||
const clientUpdated = pluginList.map(p => `node_modules/${p}/+(public|static)/**/*.js`);
|
const styleUpdated_Admin = plugins.map(p => `node_modules/${p}/*.less`)
|
||||||
const serverUpdated = pluginList.map(p => `node_modules/${p}/*.js`)
|
.concat(plugins.map(p => `node_modules/${p}/*.css`))
|
||||||
.concat(pluginList.map(p => `node_modules/${p}/+(lib|src)/**/*.js`));
|
.concat(plugins.map(p => `node_modules/${p}/+(public|static|less)/**/*.less`))
|
||||||
|
.concat(plugins.map(p => `node_modules/${p}/+(public|static)/**/*.css`));
|
||||||
|
|
||||||
|
const clientUpdated = plugins.map(p => `node_modules/${p}/+(public|static)/**/*.js`);
|
||||||
|
const serverUpdated = plugins.map(p => `node_modules/${p}/*.js`)
|
||||||
|
.concat(plugins.map(p => `node_modules/${p}/+(lib|src)/**/*.js`));
|
||||||
|
|
||||||
|
const templatesUpdated = plugins.map(p => `node_modules/${p}/+(public|static|templates)/**/*.tpl`);
|
||||||
|
const langUpdated = plugins.map(p => `node_modules/${p}/+(public|static|languages)/**/*.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: {
|
styleUpdated_Client: {
|
||||||
files: [
|
files: [
|
||||||
'public/scss/**/*.scss',
|
'public/less/**/*.less',
|
||||||
...styleUpdated_Client,
|
...styleUpdated_Client,
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
styleUpdated_Admin: {
|
||||||
|
files: [
|
||||||
|
'public/less/**/*.less',
|
||||||
|
...styleUpdated_Admin,
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
clientUpdated: {
|
clientUpdated: {
|
||||||
@@ -84,7 +97,7 @@ module.exports = function (grunt) {
|
|||||||
'node_modules/benchpressjs/build/benchpress.js',
|
'node_modules/benchpressjs/build/benchpress.js',
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serverUpdated: {
|
serverUpdated: {
|
||||||
@@ -92,14 +105,14 @@ module.exports = function (grunt) {
|
|||||||
'app.js',
|
'app.js',
|
||||||
'install/*.js',
|
'install/*.js',
|
||||||
'src/**/*.js',
|
'src/**/*.js',
|
||||||
'public/src/modules/translator.common.js',
|
'public/src/modules/translator.js',
|
||||||
'public/src/modules/helpers.common.js',
|
'public/src/modules/helpers.js',
|
||||||
'public/src/utils.common.js',
|
'public/src/utils.js',
|
||||||
serverUpdated,
|
serverUpdated,
|
||||||
'!src/upgrades/**',
|
'!src/upgrades/**',
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
templatesUpdated: {
|
templatesUpdated: {
|
||||||
@@ -108,7 +121,7 @@ module.exports = function (grunt) {
|
|||||||
...templatesUpdated,
|
...templatesUpdated,
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
langUpdated: {
|
langUpdated: {
|
||||||
@@ -118,13 +131,13 @@ module.exports = function (grunt) {
|
|||||||
...langUpdated,
|
...langUpdated,
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
interval: interval,
|
interval: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const build = require('./src/meta/build');
|
const build = require('./src/meta/build');
|
||||||
if (!grunt.option('skip')) {
|
if (!grunt.option('skip')) {
|
||||||
await build.build(true, { watch: true });
|
await build.build(true);
|
||||||
}
|
}
|
||||||
run();
|
run();
|
||||||
done();
|
done();
|
||||||
@@ -153,14 +166,16 @@ module.exports = function (grunt) {
|
|||||||
grunt.event.removeAllListeners('watch');
|
grunt.event.removeAllListeners('watch');
|
||||||
grunt.event.on('watch', (action, filepath, target) => {
|
grunt.event.on('watch', (action, filepath, target) => {
|
||||||
let compiling;
|
let compiling;
|
||||||
if (target === 'styleUpdated') {
|
if (target === 'styleUpdated_Client') {
|
||||||
compiling = ['clientCSS', 'acpCSS'];
|
compiling = 'clientCSS';
|
||||||
|
} else if (target === 'styleUpdated_Admin') {
|
||||||
|
compiling = 'acpCSS';
|
||||||
} else if (target === 'clientUpdated') {
|
} else if (target === 'clientUpdated') {
|
||||||
compiling = ['js'];
|
compiling = 'js';
|
||||||
} else if (target === 'templatesUpdated') {
|
} else if (target === 'templatesUpdated') {
|
||||||
compiling = ['tpl'];
|
compiling = 'tpl';
|
||||||
} else if (target === 'langUpdated') {
|
} else if (target === 'langUpdated') {
|
||||||
compiling = ['lang'];
|
compiling = 'lang';
|
||||||
} else if (target === 'serverUpdated') {
|
} else if (target === 'serverUpdated') {
|
||||||
// empty require cache
|
// empty require cache
|
||||||
const paths = ['./src/meta/build.js', './src/meta/index.js'];
|
const paths = ['./src/meta/build.js', './src/meta/index.js'];
|
||||||
@@ -168,7 +183,7 @@ module.exports = function (grunt) {
|
|||||||
return run();
|
return run();
|
||||||
}
|
}
|
||||||
|
|
||||||
require('./src/meta/build').build(compiling, { webpack: false }, (err) => {
|
require('./src/meta/build').build([compiling], (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
winston.error(err.stack);
|
winston.error(err.stack);
|
||||||
}
|
}
|
||||||
@@ -179,10 +194,10 @@ module.exports = function (grunt) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function addBaseThemes(pluginList) {
|
function addBaseThemes(plugins) {
|
||||||
let themeId = pluginList.find(p => p.includes('nodebb-theme-'));
|
let themeId = plugins.find(p => p.includes('nodebb-theme-'));
|
||||||
if (!themeId) {
|
if (!themeId) {
|
||||||
return pluginList;
|
return plugins;
|
||||||
}
|
}
|
||||||
let baseTheme;
|
let baseTheme;
|
||||||
do {
|
do {
|
||||||
@@ -193,9 +208,9 @@ function addBaseThemes(pluginList) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (baseTheme) {
|
if (baseTheme) {
|
||||||
pluginList.push(baseTheme);
|
plugins.push(baseTheme);
|
||||||
themeId = baseTheme;
|
themeId = baseTheme;
|
||||||
}
|
}
|
||||||
} while (baseTheme);
|
} while (baseTheme);
|
||||||
return pluginList;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -1,15 +1,22 @@
|
|||||||
# 
|
# 
|
||||||
|
|
||||||
[](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 supports either Redis, MongoDB, or a PostgreSQL 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](https://try.nodebb.org)
|
||||||
|
* [Developer Community](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,30 +31,32 @@ 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 18 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
|
* A version of Node.js at least 12 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
|
||||||
* MongoDB, version 3.6 or greater **or** Redis, version 2.8.9 or greater
|
* MongoDB, version 2.6 or greater **or** Redis, version 2.8.9 or greater
|
||||||
* If you are using [clustering](https://docs.nodebb.org/configuring/scaling/) you need Redis installed and configured.
|
* 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
|
||||||
|
|
||||||
[Please refer to platform-specific installation documentation](https://docs.nodebb.org/installing/os).
|
[Please refer to platform-specific installation documentation](https://docs.nodebb.org/installing/os)
|
||||||
If installing via the cloud (or using Docker), [please see cloud-based installation documentation](https://docs.nodebb.org/installing/cloud/).
|
|
||||||
|
|
||||||
## Securing NodeBB
|
## Securing NodeBB
|
||||||
|
|
||||||
@@ -60,7 +69,6 @@ It is important to ensure that your NodeBB and database servers are secured. Bea
|
|||||||
2. Use `iptables` to secure your server from unintended open ports. In Ubuntu, `ufw` provides a friendlier interface to working with `iptables`.
|
2. Use `iptables` to secure your server from unintended open ports. In Ubuntu, `ufw` provides a friendlier interface to working with `iptables`.
|
||||||
* e.g. If your NodeBB is proxied, no ports should be open except 80 (and possibly 22, for SSH access)
|
* e.g. If your NodeBB is proxied, no ports should be open except 80 (and possibly 22, for SSH access)
|
||||||
|
|
||||||
|
|
||||||
## Upgrading NodeBB
|
## Upgrading NodeBB
|
||||||
|
|
||||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.nodebb.org/configuring/upgrade/)
|
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.nodebb.org/configuring/upgrade/)
|
||||||
@@ -70,15 +78,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")
|
|
||||||
|
|||||||
4
app.js
4
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
|
||||||
@@ -71,7 +71,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,77 +0,0 @@
|
|||||||
FROM node:lts AS git
|
|
||||||
|
|
||||||
ENV USER=nodebb \
|
|
||||||
UID=1001 \
|
|
||||||
GID=1001
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app/
|
|
||||||
|
|
||||||
RUN groupadd --gid ${GID} ${USER} \
|
|
||||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
|
||||||
&& chown -R ${USER}:${USER} /usr/src/app/
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get -y --no-install-recommends install tini
|
|
||||||
|
|
||||||
USER ${USER}
|
|
||||||
|
|
||||||
# Change to the git branch you want to test
|
|
||||||
RUN git clone --recurse-submodules -j8 --depth 1 https://github.com/NodeBB/NodeBB.git .
|
|
||||||
|
|
||||||
RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \;
|
|
||||||
|
|
||||||
FROM node:lts AS node_modules_touch
|
|
||||||
|
|
||||||
ENV NODE_ENV=development \
|
|
||||||
USER=nodebb \
|
|
||||||
UID=1001 \
|
|
||||||
GID=1001
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app/
|
|
||||||
|
|
||||||
RUN corepack enable \
|
|
||||||
&& groupadd --gid ${GID} ${USER} \
|
|
||||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
|
||||||
&& chown -R ${USER}:${USER} /usr/src/app/
|
|
||||||
|
|
||||||
COPY --from=git --chown=${USER}:${USER} /usr/src/app/install/package.json /usr/src/app/
|
|
||||||
|
|
||||||
USER ${USER}
|
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
FROM node:lts-slim AS final
|
|
||||||
|
|
||||||
ENV NODE_ENV=development \
|
|
||||||
DAEMON=false \
|
|
||||||
SILENT=false \
|
|
||||||
USER=nodebb \
|
|
||||||
UID=1001 \
|
|
||||||
GID=1001
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app/
|
|
||||||
|
|
||||||
RUN corepack enable \
|
|
||||||
&& groupadd --gid ${GID} ${USER} \
|
|
||||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
|
||||||
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
|
|
||||||
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/
|
|
||||||
|
|
||||||
COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
|
|
||||||
COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
|
|
||||||
COPY --from=node_modules_touch --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
|
|
||||||
COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
|
|
||||||
|
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh \
|
|
||||||
&& chmod +x /usr/local/bin/tini
|
|
||||||
|
|
||||||
# TODO: Have docker-compose use environment variables to create files like setup.json and config.json.
|
|
||||||
# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate
|
|
||||||
|
|
||||||
USER ${USER}
|
|
||||||
|
|
||||||
EXPOSE 4567
|
|
||||||
|
|
||||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"]
|
|
||||||
|
|
||||||
ENTRYPOINT ["tini", "--", "entrypoint.sh"]
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
nodebb:
|
|
||||||
build: .
|
|
||||||
# image: ghcr.io/nodebb/nodebb:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
|
||||||
volumes:
|
|
||||||
- nodebb-build:/usr/src/app/build
|
|
||||||
- nodebb-uploads:/usr/src/app/public/uploads
|
|
||||||
- nodebb-config:/opt/config
|
|
||||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:17.2-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: nodebb
|
|
||||||
POSTGRES_PASSWORD: nodebb
|
|
||||||
POSTGRES_DB: nodebb
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7.4.2-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
|
|
||||||
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
profiles:
|
|
||||||
- redis
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/database/postgresql/data
|
|
||||||
|
|
||||||
redis-data:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/database/redis
|
|
||||||
|
|
||||||
nodebb-build:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/build
|
|
||||||
|
|
||||||
nodebb-uploads:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/public/uploads
|
|
||||||
|
|
||||||
nodebb-config:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/config
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
nodebb:
|
|
||||||
build: .
|
|
||||||
# image: ghcr.io/nodebb/nodebb:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
|
||||||
volumes:
|
|
||||||
- nodebb-build:/usr/src/app/build
|
|
||||||
- nodebb-uploads:/usr/src/app/public/uploads
|
|
||||||
- nodebb-config:/opt/config
|
|
||||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7.4.2-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
|
|
||||||
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
redis-data:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/database/redis
|
|
||||||
|
|
||||||
nodebb-build:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/build
|
|
||||||
|
|
||||||
nodebb-uploads:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/public/uploads
|
|
||||||
|
|
||||||
nodebb-config:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/config
|
|
||||||
@@ -1,89 +1,24 @@
|
|||||||
|
version: '3.5'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nodebb:
|
node:
|
||||||
build: .
|
build: .
|
||||||
# image: ghcr.io/nodebb/nodebb:latest
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
depends_on:
|
||||||
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
- db
|
||||||
volumes:
|
expose:
|
||||||
- nodebb-build:/usr/src/app/build
|
- 4567 # use a reverse proxy like Traefik
|
||||||
- nodebb-uploads:/usr/src/app/public/uploads
|
|
||||||
- nodebb-config:/opt/config
|
|
||||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
|
||||||
|
|
||||||
mongo:
|
db:
|
||||||
image: 'mongo:7-jammy'
|
image: mongo:bionic
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
expose:
|
||||||
- '27017:27017'
|
- 27017
|
||||||
environment:
|
environment:
|
||||||
MONGO_INITDB_ROOT_USERNAME: nodebb
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
MONGO_INITDB_ROOT_PASSWORD: nodebb
|
MONGO_INITDB_ROOT_PASSWORD: root
|
||||||
MONGO_INITDB_DATABASE: nodebb
|
|
||||||
volumes:
|
volumes:
|
||||||
- mongo-data:/data/db
|
- mongo:/data/db
|
||||||
- ./install/docker/mongodb-user-init.js:/docker-entrypoint-initdb.d/user-init.js
|
|
||||||
redis:
|
|
||||||
image: redis:7.4.2-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
|
|
||||||
# command: ['redis-server', '--save', '60', '1', '--loglevel', 'warning'] # uncomment if you want to use snapshotting instead of AOF
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
profiles:
|
|
||||||
- redis
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:17.2-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: nodebb
|
|
||||||
POSTGRES_PASSWORD: nodebb
|
|
||||||
POSTGRES_DB: nodebb
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
profiles:
|
|
||||||
- postgres
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mongo-data:
|
mongo:
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/database/mongo/data
|
|
||||||
|
|
||||||
redis-data:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/database/redis
|
|
||||||
|
|
||||||
postgres-data:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/database/postgresql/data
|
|
||||||
|
|
||||||
nodebb-build:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/build
|
|
||||||
|
|
||||||
nodebb-uploads:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/public/uploads
|
|
||||||
|
|
||||||
nodebb-config:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
o: bind
|
|
||||||
type: none
|
|
||||||
device: ./.docker/config
|
|
||||||
|
|||||||
@@ -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,20 +14,16 @@
|
|||||||
"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": 2000,
|
"chatMessageDelay": 200,
|
||||||
"newbieChatMessageDelay": 120000,
|
|
||||||
"notificationSendDelay": 60,
|
"notificationSendDelay": 60,
|
||||||
"newbieReputationThreshold": 3,
|
"newbiePostDelayThreshold": 3,
|
||||||
"postQueue": 0,
|
"postQueue": 0,
|
||||||
"postQueueReputationThreshold": 0,
|
"postQueueReputationThreshold": 0,
|
||||||
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"],
|
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"],
|
||||||
"groupsExemptFromNewUserRestrictions": ["administrators", "Global Moderators"],
|
|
||||||
"groupsExemptFromMaintenanceMode": ["administrators", "Global Moderators"],
|
|
||||||
"minimumPostLength": 8,
|
"minimumPostLength": 8,
|
||||||
"maximumPostLength": 32767,
|
"maximumPostLength": 32767,
|
||||||
"systemTags": "",
|
"systemTags": "",
|
||||||
@@ -42,17 +37,15 @@
|
|||||||
"registrationApprovalType": "normal",
|
"registrationApprovalType": "normal",
|
||||||
"allowAccountDelete": 1,
|
"allowAccountDelete": 1,
|
||||||
"privateUploads": 0,
|
"privateUploads": 0,
|
||||||
"allowedFileExtensions": "png,jpg,bmp,txt,webp,webm,mp4,gif",
|
"allowedFileExtensions": "png,jpg,bmp,txt",
|
||||||
"uploadRateLimitThreshold": 10,
|
"uploadRateLimitThreshold": 10,
|
||||||
"uploadRateLimitCooldown": 60,
|
"uploadRateLimitCooldown": 60,
|
||||||
"allowUserHomePage": 1,
|
"allowUserHomePage": 1,
|
||||||
"allowMultipleBadges": 1,
|
"allowMultipleBadges": 0,
|
||||||
"maximumFileSize": 2048,
|
"maximumFileSize": 2048,
|
||||||
"stripEXIFData": 1,
|
"stripEXIFData": 1,
|
||||||
"orphanExpiryDays": 0,
|
|
||||||
"resizeImageWidthThreshold": 2000,
|
"resizeImageWidthThreshold": 2000,
|
||||||
"resizeImageWidth": 760,
|
"resizeImageWidth": 760,
|
||||||
"resizeImageKeepOriginal": 1,
|
|
||||||
"rejectImageWidth": 5000,
|
"rejectImageWidth": 5000,
|
||||||
"rejectImageHeight": 5000,
|
"rejectImageHeight": 5000,
|
||||||
"resizeImageQuality": 80,
|
"resizeImageQuality": 80,
|
||||||
@@ -67,16 +60,15 @@
|
|||||||
"maximumAboutMeLength": 1000,
|
"maximumAboutMeLength": 1000,
|
||||||
"maximumUsersInChatRoom": 0,
|
"maximumUsersInChatRoom": 0,
|
||||||
"maximumChatMessageLength": 1000,
|
"maximumChatMessageLength": 1000,
|
||||||
"maximumChatRoomNameLength": 50,
|
|
||||||
"maximumProfileImageSize": 256,
|
"maximumProfileImageSize": 256,
|
||||||
"maximumCoverImageSize": 2048,
|
"maximumCoverImageSize": 2048,
|
||||||
"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,
|
||||||
@@ -84,15 +76,9 @@
|
|||||||
"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,
|
"downvotesPerDay": 10,
|
||||||
"downvotesPerUserPerDay": 3,
|
"downvotesPerUserPerDay": 3,
|
||||||
"min:rep:chat": 0,
|
|
||||||
"min:rep:downvote": 0,
|
"min:rep:downvote": 0,
|
||||||
"min:rep:upvote": 0,
|
|
||||||
"min:rep:post-links": 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,
|
||||||
@@ -100,21 +86,13 @@
|
|||||||
"min:rep:aboutme": 0,
|
"min:rep:aboutme": 0,
|
||||||
"min:rep:signature": 0,
|
"min:rep:signature": 0,
|
||||||
"flags:limitPerTarget": 0,
|
"flags:limitPerTarget": 0,
|
||||||
"flags:postFlagsPerDay": 10,
|
|
||||||
"flags:userFlagsPerDay": 10,
|
|
||||||
"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-topic-with-tag": "notification",
|
|
||||||
"notificationType_new-topic-in-category": "notification",
|
|
||||||
"notificationType_new-reply": "notification",
|
"notificationType_new-reply": "notification",
|
||||||
"notificationType_post-edit": "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_new-group-chat": "notification",
|
||||||
"notificationType_new-public-chat": "none",
|
|
||||||
"notificationType_group-invite": "notification",
|
"notificationType_group-invite": "notification",
|
||||||
"notificationType_group-leave": "notification",
|
"notificationType_group-leave": "notification",
|
||||||
"notificationType_group-request-membership": "notification",
|
"notificationType_group-request-membership": "notification",
|
||||||
@@ -130,9 +108,6 @@
|
|||||||
"postsPerPage": 20,
|
"postsPerPage": 20,
|
||||||
"categoriesPerPage": 50,
|
"categoriesPerPage": 50,
|
||||||
"userSearchResultsPerPage": 50,
|
"userSearchResultsPerPage": 50,
|
||||||
"searchDefaultSortBy": "relevance",
|
|
||||||
"searchDefaultIn": "titlesposts",
|
|
||||||
"searchDefaultInQuick": "titles",
|
|
||||||
"maximumGroupNameLength": 255,
|
"maximumGroupNameLength": 255,
|
||||||
"maximumGroupTitleLength": 40,
|
"maximumGroupTitleLength": 40,
|
||||||
"preventTopicDeleteAfterReplies": 0,
|
"preventTopicDeleteAfterReplies": 0,
|
||||||
@@ -141,8 +116,7 @@
|
|||||||
"sitemapTopics": 500,
|
"sitemapTopics": 500,
|
||||||
"maintenanceMode": 0,
|
"maintenanceMode": 0,
|
||||||
"maintenanceModeStatus": 503,
|
"maintenanceModeStatus": 503,
|
||||||
"upvoteVisibility": "all",
|
"votesArePublic": 0,
|
||||||
"downvoteVisibility": "privileged",
|
|
||||||
"maximumInvites": 0,
|
"maximumInvites": 0,
|
||||||
"username:disableEdit": 0,
|
"username:disableEdit": 0,
|
||||||
"email:disableEdit": 0,
|
"email:disableEdit": 0,
|
||||||
@@ -159,20 +133,11 @@
|
|||||||
"maximumRelatedTopics": 0,
|
"maximumRelatedTopics": 0,
|
||||||
"disableEmailSubscriptions": 0,
|
"disableEmailSubscriptions": 0,
|
||||||
"emailConfirmInterval": 10,
|
"emailConfirmInterval": 10,
|
||||||
"emailConfirmExpiry": 24,
|
|
||||||
"removeEmailNotificationImages": 0,
|
"removeEmailNotificationImages": 0,
|
||||||
"sendValidationEmail": 1,
|
|
||||||
"includeUnverifiedEmails": 0,
|
|
||||||
"emailPrompt": 1,
|
|
||||||
"sendEmailToBanned": 0,
|
|
||||||
"requireEmailAddress": 0,
|
|
||||||
"inviteExpiration": 7,
|
"inviteExpiration": 7,
|
||||||
"dailyDigestFreq": "off",
|
"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,
|
||||||
@@ -183,7 +148,7 @@
|
|||||||
"onlineCutoff": 30,
|
"onlineCutoff": 30,
|
||||||
"timeagoCutoff": 30,
|
"timeagoCutoff": 30,
|
||||||
"necroThreshold": 7,
|
"necroThreshold": 7,
|
||||||
"categoryWatchState": "tracking",
|
"categoryWatchState": "watching",
|
||||||
"submitPluginUsage": 1,
|
"submitPluginUsage": 1,
|
||||||
"showAverageApprovalTime": 1,
|
"showAverageApprovalTime": 1,
|
||||||
"autoApproveTime": 0,
|
"autoApproveTime": 0,
|
||||||
@@ -193,13 +158,5 @@
|
|||||||
"composer:showHelpTab": 1,
|
"composer:showHelpTab": 1,
|
||||||
"composer:allowPluginHelp": 1,
|
"composer:allowPluginHelp": 1,
|
||||||
"maxReconnectionAttempts": 5,
|
"maxReconnectionAttempts": 5,
|
||||||
"reconnectionDelay": 1500,
|
"reconnectionDelay": 1500
|
||||||
"disableCustomUserSkins": 0,
|
}
|
||||||
"activitypubEnabled": 1,
|
|
||||||
"activitypubAllowLoopback": 0,
|
|
||||||
"activitypubProbe": 1,
|
|
||||||
"activitypubProbeTimeout": 2000,
|
|
||||||
"activitypubContentPruneDays": 30,
|
|
||||||
"activitypubUserPruneDays": 7,
|
|
||||||
"activitypubFilter": 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,23 +38,15 @@
|
|||||||
"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]]"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"route": "/world",
|
|
||||||
"title": "[[global:header.world]]",
|
|
||||||
"enabled": true,
|
|
||||||
"iconClass": "fa-globe",
|
|
||||||
"textClass": "d-lg-none",
|
|
||||||
"text": "[[global:header.world]]"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"route": "/users",
|
"route": "/users",
|
||||||
"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]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -62,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]]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -70,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)
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
const prompt = require('prompt');
|
const prompt = require('prompt');
|
||||||
const winston = require('winston');
|
const winston = require('winston');
|
||||||
|
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
const promptGet = util.promisify((schema, callback) => prompt.get(schema, callback));
|
||||||
|
|
||||||
const questions = {
|
const 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,
|
||||||
@@ -24,17 +28,17 @@ async function getDatabaseConfig(config) {
|
|||||||
if (config['redis:host'] && config['redis:port']) {
|
if (config['redis:host'] && config['redis:port']) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
return await prompt.get(questions.redis);
|
return await promptGet(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;
|
return config;
|
||||||
}
|
}
|
||||||
return await prompt.get(questions.mongo);
|
return await promptGet(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;
|
return config;
|
||||||
}
|
}
|
||||||
return await prompt.get(questions.postgres);
|
return await promptGet(questions.postgres);
|
||||||
}
|
}
|
||||||
throw new Error(`unknown database : ${config.database}`);
|
throw new Error(`unknown database : ${config.database}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,198 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Function to set default values for environment variables
|
|
||||||
set_defaults() {
|
|
||||||
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
|
|
||||||
export CONFIG="$CONFIG_DIR/config.json"
|
|
||||||
export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}"
|
|
||||||
export NODEBB_BUILD_VERB="${NODEBB_BUILD_VERB:-build}"
|
|
||||||
export START_BUILD="${START_BUILD:-${FORCE_BUILD_BEFORE_START:-false}}"
|
|
||||||
export SETUP="${SETUP:-}"
|
|
||||||
export PACKAGE_MANAGER="${PACKAGE_MANAGER:-npm}"
|
|
||||||
export OVERRIDE_UPDATE_LOCK="${OVERRIDE_UPDATE_LOCK:-false}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to check if a directory exists and is writable
|
|
||||||
check_directory() {
|
|
||||||
local dir="$1"
|
|
||||||
if [ ! -d "$dir" ]; then
|
|
||||||
echo "Error: Directory $dir does not exist. Creating..."
|
|
||||||
mkdir -p "$dir" || {
|
|
||||||
echo "Error: Failed to create directory $dir"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
if [ ! -w "$dir" ]; then
|
|
||||||
echo "Warning: No write permission for directory $dir, attempting to fix..."
|
|
||||||
chown -R $USER:$USER "$dir" || true # attempt to change ownership, do not exit on failure
|
|
||||||
chmod -R 760 "$dir" || true # attempt to change permissions, do not exit on failure
|
|
||||||
if [ ! -w "$dir" ]; then
|
|
||||||
echo "Error: No write permission for directory $dir. Exiting..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to copy or link package.json and lock files based on package manager
|
|
||||||
copy_or_link_files() {
|
|
||||||
local src_dir="$1"
|
|
||||||
local dest_dir="$2"
|
|
||||||
local package_manager="$3"
|
|
||||||
local lock_file
|
|
||||||
|
|
||||||
case "$package_manager" in
|
|
||||||
yarn) lock_file="yarn.lock" ;;
|
|
||||||
npm) lock_file="package-lock.json" ;;
|
|
||||||
pnpm) lock_file="pnpm-lock.yaml" ;;
|
|
||||||
*)
|
|
||||||
echo "Unknown package manager: $package_manager"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Check if source and destination files are the same
|
|
||||||
if [ "$(realpath "$src_dir/package.json")" != "$(realpath "$dest_dir/package.json")" ] || [ "$OVERRIDE_UPDATE_LOCK" = true ]; then
|
|
||||||
cp "$src_dir/package.json" "$dest_dir/package.json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$(realpath "$src_dir/$lock_file")" != "$(realpath "$dest_dir/$lock_file")" ] || [ "$OVERRIDE_UPDATE_LOCK" = true ]; then
|
|
||||||
cp "$src_dir/$lock_file" "$dest_dir/$lock_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove unnecessary lock files in src_dir
|
|
||||||
rm -f "$src_dir/"{yarn.lock,package-lock.json,pnpm-lock.yaml}
|
|
||||||
|
|
||||||
# Symbolically link the copied files in src_dir to dest_dir
|
|
||||||
ln -fs "$dest_dir/package.json" "$src_dir/package.json"
|
|
||||||
ln -fs "$dest_dir/$lock_file" "$src_dir/$lock_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to install dependencies using pnpm
|
|
||||||
install_dependencies() {
|
|
||||||
case "$PACKAGE_MANAGER" in
|
|
||||||
yarn) yarn install || {
|
|
||||||
echo "Failed to install dependencies with yarn"
|
|
||||||
exit 1
|
|
||||||
} ;;
|
|
||||||
npm) npm install || {
|
|
||||||
echo "Failed to install dependencies with npm"
|
|
||||||
exit 1
|
|
||||||
} ;;
|
|
||||||
pnpm) pnpm install || {
|
|
||||||
echo "Failed to install dependencies with pnpm"
|
|
||||||
exit 1
|
|
||||||
} ;;
|
|
||||||
*)
|
|
||||||
echo "Unknown package manager: $PACKAGE_MANAGER"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to start setup session
|
|
||||||
start_setup_session() {
|
|
||||||
local config="$1"
|
|
||||||
echo "Starting setup session"
|
|
||||||
exec /usr/src/app/nodebb setup --config="$config"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Handle building and upgrading NodeBB
|
|
||||||
build_forum() {
|
|
||||||
local config="$1"
|
|
||||||
local start_build="$2"
|
|
||||||
local package_hash=$(md5sum install/package.json | head -c 32)
|
|
||||||
if [ "$package_hash" = "$(cat $CONFIG_DIR/install_hash.md5 || true)" ]; then
|
|
||||||
echo "package.json was updated. Upgrading..."
|
|
||||||
/usr/src/app/nodebb upgrade --config="$config" || {
|
|
||||||
echo "Failed to build NodeBB. Exiting..."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
elif [ "$start_build" = true ]; then
|
|
||||||
echo "Build before start is enabled. Building..."
|
|
||||||
/usr/src/app/nodebb "${NODEBB_BUILD_VERB}" --config="$config" || {
|
|
||||||
echo "Failed to build NodeBB. Exiting..."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "No changes in package.json. Skipping build..."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo -n $package_hash > $CONFIG_DIR/install_hash.md5
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Function to start forum
|
|
||||||
start_forum() {
|
|
||||||
local config="$1"
|
|
||||||
local start_build="$2"
|
|
||||||
|
|
||||||
build_forum "$config" "$start_build"
|
|
||||||
|
|
||||||
case "$PACKAGE_MANAGER" in
|
|
||||||
yarn)
|
|
||||||
yarn start --config="$config" --no-silent --no-daemon || {
|
|
||||||
echo "Failed to start forum with yarn"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
;;
|
|
||||||
npm)
|
|
||||||
npm start -- --config="$config" --no-silent --no-daemon || {
|
|
||||||
echo "Failed to start forum with npm"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
;;
|
|
||||||
pnpm)
|
|
||||||
pnpm start -- --config="$config" --no-silent --no-daemon || {
|
|
||||||
echo "Failed to start forum with pnpm"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown package manager: $PACKAGE_MANAGER"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to start installation session
|
|
||||||
start_installation_session() {
|
|
||||||
local nodebb_init_verb="$1"
|
|
||||||
local config="$2"
|
|
||||||
|
|
||||||
echo "Config file not found at $config"
|
|
||||||
echo "Starting installation session"
|
|
||||||
exec /usr/src/app/nodebb "$nodebb_init_verb" --config="$config"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function for debugging and logging
|
|
||||||
debug_log() {
|
|
||||||
local message="$1"
|
|
||||||
echo "DEBUG: $message"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main function
|
|
||||||
main() {
|
|
||||||
set_defaults
|
|
||||||
check_directory "$CONFIG_DIR"
|
|
||||||
copy_or_link_files /usr/src/app "$CONFIG_DIR" "$PACKAGE_MANAGER"
|
|
||||||
install_dependencies
|
|
||||||
|
|
||||||
debug_log "PACKAGE_MANAGER: $PACKAGE_MANAGER"
|
|
||||||
debug_log "CONFIG location: $CONFIG"
|
|
||||||
debug_log "START_BUILD: $START_BUILD"
|
|
||||||
|
|
||||||
if [ -n "$SETUP" ]; then
|
|
||||||
start_setup_session "$CONFIG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "$CONFIG" ]; then
|
|
||||||
start_forum "$CONFIG" "$START_BUILD"
|
|
||||||
else
|
|
||||||
start_installation_session "$NODEBB_INIT_VERB" "$CONFIG"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Execute main function
|
|
||||||
main "$@"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
db.createUser( { user: 'nodebb', pwd: 'nodebb', roles: [ { role: 'readWrite', db: 'nodebb' }, { role: 'clusterMonitor', db: 'admin' } ] } )
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"defaults": {
|
|
||||||
"mongo": {
|
|
||||||
"host": "mongo",
|
|
||||||
"port": 27017,
|
|
||||||
"database": "nodebb",
|
|
||||||
"username": "nodebb",
|
|
||||||
"password": "nodebb"
|
|
||||||
},
|
|
||||||
"redis": {
|
|
||||||
"host": "redis",
|
|
||||||
"port": 6379,
|
|
||||||
"database": 0
|
|
||||||
},
|
|
||||||
"postgres": {
|
|
||||||
"host": "postgres",
|
|
||||||
"port": 5432,
|
|
||||||
"database": "nodebb",
|
|
||||||
"username": "nodebb",
|
|
||||||
"password": "nodebb"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
"name": "nodebb",
|
"name": "nodebb",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"description": "NodeBB Forum",
|
"description": "NodeBB Forum",
|
||||||
"version": "4.0.0",
|
"version": "1.17.2",
|
||||||
"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/"
|
||||||
@@ -28,167 +28,149 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adactive/bootstrap-tagsinput": "0.8.2",
|
"@adactive/bootstrap-tagsinput": "^0.8.2",
|
||||||
"@fontsource/inter": "5.1.1",
|
"ace-builds": "^1.4.12",
|
||||||
"@fontsource/poppins": "5.1.1",
|
"archiver": "^5.2.0",
|
||||||
"@fortawesome/fontawesome-free": "6.7.2",
|
"async": "^3.2.0",
|
||||||
"@isaacs/ttlcache": "1.4.1",
|
"autoprefixer": "10.3.1",
|
||||||
"@nodebb/spider-detector": "2.0.3",
|
|
||||||
"@popperjs/core": "2.11.8",
|
|
||||||
"@resvg/resvg-js": "2.6.2",
|
|
||||||
"@textcomplete/contenteditable": "0.1.13",
|
|
||||||
"@textcomplete/core": "0.1.13",
|
|
||||||
"@textcomplete/textarea": "0.1.13",
|
|
||||||
"ace-builds": "1.37.5",
|
|
||||||
"archiver": "7.0.1",
|
|
||||||
"async": "3.2.6",
|
|
||||||
"autoprefixer": "10.4.20",
|
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"benchpressjs": "2.5.1",
|
"benchpressjs": "2.4.3",
|
||||||
"body-parser": "1.20.3",
|
"body-parser": "^1.19.0",
|
||||||
"bootbox": "6.0.0",
|
"bootbox": "5.5.2",
|
||||||
"bootstrap": "5.3.3",
|
"bootstrap": "^3.4.1",
|
||||||
"bootswatch": "5.3.3",
|
"chart.js": "^2.9.4",
|
||||||
"chalk": "4.1.2",
|
"cli-graph": "^3.2.2",
|
||||||
"chart.js": "4.4.7",
|
"clipboard": "^2.0.6",
|
||||||
"cli-graph": "3.2.2",
|
"colors": "^1.4.0",
|
||||||
"clipboard": "2.0.11",
|
"commander": "^7.1.0",
|
||||||
"colors": "1.4.0",
|
"compare-versions": "3.6.0",
|
||||||
"commander": "12.1.0",
|
"compression": "^1.7.4",
|
||||||
"compare-versions": "6.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"compression": "1.7.5",
|
"connect-mongo": "4.4.1",
|
||||||
"connect-flash": "0.1.1",
|
"connect-multiparty": "^2.2.0",
|
||||||
"connect-mongo": "5.1.0",
|
"connect-pg-simple": "^6.2.1",
|
||||||
"connect-multiparty": "2.2.0",
|
"connect-redis": "6.0.0",
|
||||||
"connect-pg-simple": "10.0.0",
|
"cookie-parser": "^1.4.5",
|
||||||
"connect-redis": "8.0.1",
|
"cron": "^1.8.2",
|
||||||
"cookie-parser": "1.4.7",
|
"cropperjs": "^1.5.11",
|
||||||
"cron": "3.5.0",
|
"csurf": "^1.11.0",
|
||||||
"cropperjs": "1.6.2",
|
"daemon": "^1.1.0",
|
||||||
"csrf-sync": "4.0.3",
|
"diff": "^5.0.0",
|
||||||
"daemon": "1.1.0",
|
"express": "^4.17.1",
|
||||||
"diff": "7.0.0",
|
"express-session": "^1.17.1",
|
||||||
"esbuild": "0.24.2",
|
"express-useragent": "^1.0.15",
|
||||||
"express": "4.21.2",
|
"graceful-fs": "^4.2.6",
|
||||||
"express-session": "1.18.1",
|
"helmet": "^4.4.1",
|
||||||
"express-useragent": "1.0.15",
|
"html-to-text": "7.1.1",
|
||||||
"fetch-cookie": "3.1.0",
|
"ipaddr.js": "^2.0.0",
|
||||||
"file-loader": "6.2.0",
|
"jquery": "3.6.0",
|
||||||
"fs-extra": "11.3.0",
|
"jquery-deserialize": "2.0.0-rc1",
|
||||||
"graceful-fs": "4.2.11",
|
|
||||||
"helmet": "7.2.0",
|
|
||||||
"html-to-text": "9.0.5",
|
|
||||||
"imagesloaded": "5.0.0",
|
|
||||||
"ipaddr.js": "2.2.0",
|
|
||||||
"jquery": "3.7.1",
|
|
||||||
"jquery-deserialize": "2.0.0",
|
|
||||||
"jquery-form": "4.3.0",
|
"jquery-form": "4.3.0",
|
||||||
"jquery-serializeobject": "1.0.0",
|
"jquery-serializeobject": "1.0.0",
|
||||||
"jquery-ui": "1.14.1",
|
"jquery-ui": "1.12.1",
|
||||||
"jsesc": "3.1.0",
|
"jsesc": "3.0.2",
|
||||||
"json2csv": "5.0.7",
|
"json2csv": "5.0.6",
|
||||||
"jsonwebtoken": "9.0.2",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"lodash": "4.17.21",
|
"less": "^3.11.1",
|
||||||
"logrotate-stream": "0.2.9",
|
"lodash": "^4.17.21",
|
||||||
"lru-cache": "10.4.3",
|
"logrotate-stream": "^0.2.7",
|
||||||
"mime": "3.0.0",
|
"lru-cache": "6.0.0",
|
||||||
"mkdirp": "3.0.1",
|
"material-design-lite": "^1.3.0",
|
||||||
"mongodb": "6.12.0",
|
"mime": "^2.5.2",
|
||||||
"morgan": "1.10.0",
|
"mkdirp": "^1.0.4",
|
||||||
"mousetrap": "1.6.5",
|
"mongodb": "3.6.10",
|
||||||
"multiparty": "4.2.3",
|
"morgan": "^1.10.0",
|
||||||
"nconf": "0.12.1",
|
"mousetrap": "^1.6.5",
|
||||||
"nodebb-plugin-2factor": "7.5.8",
|
"multiparty": "4.2.2",
|
||||||
"nodebb-plugin-composer-default": "10.2.44",
|
"@nodebb/bootswatch": "3.4.2",
|
||||||
"nodebb-plugin-dbsearch": "6.2.5",
|
"nconf": "^0.11.2",
|
||||||
"nodebb-plugin-emoji": "6.0.1",
|
"nodebb-plugin-composer-default": "6.5.34",
|
||||||
"nodebb-plugin-emoji-android": "4.1.1",
|
"nodebb-plugin-dbsearch": "5.0.2",
|
||||||
"nodebb-plugin-markdown": "13.0.0",
|
"nodebb-plugin-emoji": "^3.5.0",
|
||||||
"nodebb-plugin-mentions": "4.6.10",
|
"nodebb-plugin-emoji-android": "2.0.5",
|
||||||
"nodebb-plugin-spam-be-gone": "2.3.0",
|
"nodebb-plugin-markdown": "8.14.2",
|
||||||
"nodebb-plugin-web-push": "0.7.2",
|
"nodebb-plugin-mentions": "2.13.11",
|
||||||
"nodebb-rewards-essentials": "1.0.0",
|
"nodebb-plugin-spam-be-gone": "0.7.9",
|
||||||
"nodebb-theme-harmony": "2.0.0",
|
"nodebb-rewards-essentials": "0.1.5",
|
||||||
"nodebb-theme-lavender": "7.1.17",
|
"nodebb-theme-lavender": "5.2.1",
|
||||||
"nodebb-theme-peace": "2.2.33",
|
"nodebb-theme-persona": "11.0.26",
|
||||||
"nodebb-theme-persona": "14.0.1",
|
"nodebb-theme-slick": "1.4.7",
|
||||||
"nodebb-widget-essentials": "7.0.32",
|
"nodebb-theme-vanilla": "12.0.8",
|
||||||
"nodemailer": "6.9.16",
|
"nodebb-widget-essentials": "5.0.4",
|
||||||
|
"nodemailer": "^6.5.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"passport": "0.7.0",
|
"passport": "^0.4.1",
|
||||||
"passport-http-bearer": "1.0.1",
|
"passport-http-bearer": "^1.0.1",
|
||||||
"passport-local": "1.0.0",
|
"passport-local": "1.0.0",
|
||||||
"pg": "8.13.1",
|
"pg": "^8.5.1",
|
||||||
"pg-cursor": "2.12.1",
|
"pg-cursor": "^2.5.2",
|
||||||
"postcss": "8.5.1",
|
"postcss": "8.3.5",
|
||||||
"postcss-clean": "1.2.0",
|
"postcss-clean": "1.2.0",
|
||||||
"progress-webpack-plugin": "1.0.16",
|
"prompt": "^1.1.0",
|
||||||
"prompt": "1.3.0",
|
"ioredis": "4.27.6",
|
||||||
"ioredis": "5.4.2",
|
"request": "2.88.2",
|
||||||
"rimraf": "5.0.10",
|
"request-promise-native": "^1.0.9",
|
||||||
"rss": "1.2.2",
|
"requirejs": "2.3.6",
|
||||||
"rtlcss": "4.3.0",
|
"rimraf": "3.0.2",
|
||||||
"sanitize-html": "2.14.0",
|
"rss": "^1.2.2",
|
||||||
"sass": "1.83.4",
|
"sanitize-html": "^2.3.2",
|
||||||
"satori": "0.12.1",
|
"semver": "^7.3.4",
|
||||||
"semver": "7.6.3",
|
"serve-favicon": "^2.5.0",
|
||||||
"serve-favicon": "2.5.0",
|
"sharp": "0.28.3",
|
||||||
"sharp": "0.32.6",
|
"sitemap": "^7.0.0",
|
||||||
"sitemap": "8.0.0",
|
"slideout": "1.0.1",
|
||||||
"socket.io": "4.8.1",
|
"socket.io": "4.1.3",
|
||||||
"socket.io-client": "4.8.1",
|
"socket.io-adapter-cluster": "^1.0.1",
|
||||||
"@socket.io/redis-adapter": "8.3.0",
|
"socket.io-client": "4.1.3",
|
||||||
"sortablejs": "1.15.6",
|
"@socket.io/redis-adapter": "7.0.0",
|
||||||
"spdx-license-list": "6.9.0",
|
"sortablejs": "1.14.0",
|
||||||
"terser-webpack-plugin": "5.3.11",
|
"spdx-license-list": "^6.4.0",
|
||||||
"textcomplete": "0.18.2",
|
"spider-detector": "2.0.0",
|
||||||
"textcomplete.contenteditable": "0.1.1",
|
"textcomplete": "^0.18.0",
|
||||||
"timeago": "1.6.7",
|
"textcomplete.contenteditable": "^0.1.1",
|
||||||
|
"timeago": "^1.6.7",
|
||||||
"tinycon": "0.6.8",
|
"tinycon": "0.6.8",
|
||||||
"toobusy-js": "0.5.1",
|
"toobusy-js": "^0.5.1",
|
||||||
"tough-cookie": "5.1.0",
|
"uglify-es": "^3.3.9",
|
||||||
"validator": "13.12.0",
|
"validator": "13.6.0",
|
||||||
"webpack": "5.97.1",
|
"visibilityjs": "2.0.2",
|
||||||
"webpack-merge": "6.0.1",
|
"winston": "3.3.3",
|
||||||
"winston": "3.17.0",
|
"xml": "^1.0.1",
|
||||||
"workerpool": "9.2.0",
|
"xregexp": "^5.0.1",
|
||||||
"xml": "1.0.1",
|
"yargs": "16.2.0",
|
||||||
"xregexp": "5.1.1",
|
"zxcvbn": "^4.4.2"
|
||||||
"yargs": "17.7.2",
|
|
||||||
"zxcvbn": "4.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@apidevtools/swagger-parser": "10.1.0",
|
"@apidevtools/swagger-parser": "10.0.2",
|
||||||
"@commitlint/cli": "19.6.1",
|
"@commitlint/cli": "12.1.4",
|
||||||
"@commitlint/config-angular": "19.7.0",
|
"@commitlint/config-angular": "12.1.4",
|
||||||
"coveralls": "3.1.1",
|
"coveralls": "3.1.1",
|
||||||
"eslint": "8.57.1",
|
"eslint": "7.30.0",
|
||||||
"eslint-config-nodebb": "0.2.1",
|
"eslint-config-airbnb-base": "14.2.1",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.23.4",
|
||||||
"grunt": "1.6.1",
|
"grunt": "1.4.1",
|
||||||
"grunt-contrib-watch": "1.1.0",
|
"grunt-contrib-watch": "1.1.0",
|
||||||
"husky": "8.0.3",
|
"husky": "6.0.0",
|
||||||
"jsdom": "26.0.0",
|
"jsdom": "16.6.0",
|
||||||
"lint-staged": "15.4.0",
|
"lint-staged": "11.0.1",
|
||||||
"mocha": "11.0.1",
|
"mocha": "8.4.0",
|
||||||
"mocha-lcov-reporter": "1.3.0",
|
"mocha-lcov-reporter": "1.3.0",
|
||||||
"mockdate": "3.0.5",
|
"mockdate": "3.0.5",
|
||||||
"nyc": "17.1.0",
|
"nyc": "15.1.0",
|
||||||
"smtp-server": "3.13.6"
|
"smtp-server": "3.9.0"
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"sass-embedded": "1.83.4"
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"*/jquery": "3.7.1"
|
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/NodeBB/NodeBB/issues"
|
"url": "https://github.com/NodeBB/NodeBB/issues"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"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",
|
||||||
@@ -200,4 +182,4 @@
|
|||||||
"url": "https://github.com/barisusakli"
|
"url": "https://github.com/barisusakli"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
197
install/web.js
197
install/web.js
@@ -2,22 +2,22 @@
|
|||||||
|
|
||||||
const winston = require('winston');
|
const winston = require('winston');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const session = require('express-session');
|
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
const less = require('less');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
const webpack = require('webpack');
|
const lessRenderAsync = util.promisify(
|
||||||
|
(style, opts, cb) => less.render(String(style), opts, cb)
|
||||||
|
);
|
||||||
|
const uglify = require('uglify-es');
|
||||||
const nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
|
|
||||||
const Benchpress = require('benchpressjs');
|
const Benchpress = require('benchpressjs');
|
||||||
const { mkdirp } = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
const { paths } = require('../src/constants');
|
const { paths } = require('../src/constants');
|
||||||
const utils = require('../src/utils');
|
|
||||||
|
|
||||||
const sass = utils.getSass();
|
|
||||||
const { generateToken, csrfSynchronisedProtection } = require('../src/middleware/csrf');
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
let server;
|
let server;
|
||||||
@@ -50,13 +50,20 @@ winston.configure({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const web = module.exports;
|
const web = module.exports;
|
||||||
|
|
||||||
|
const scripts = [
|
||||||
|
'node_modules/jquery/dist/jquery.js',
|
||||||
|
'node_modules/xregexp/xregexp-all.js',
|
||||||
|
'public/src/modules/slugify.js',
|
||||||
|
'public/src/utils.js',
|
||||||
|
'public/src/installer/install.js',
|
||||||
|
'node_modules/zxcvbn/dist/zxcvbn.js',
|
||||||
|
];
|
||||||
|
|
||||||
let installing = false;
|
let installing = false;
|
||||||
let success = false;
|
let success = false;
|
||||||
let error = false;
|
let error = false;
|
||||||
let launchUrl;
|
let launchUrl;
|
||||||
let timeStart = 0;
|
|
||||||
const totalTime = 1000 * 60 * 3;
|
|
||||||
|
|
||||||
|
|
||||||
const viewsDir = path.join(paths.baseDir, 'build/public/templates');
|
const viewsDir = path.join(paths.baseDir, 'build/public/templates');
|
||||||
|
|
||||||
@@ -65,8 +72,6 @@ web.install = async function (port) {
|
|||||||
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', (filepath, options, callback) => {
|
app.engine('tpl', (filepath, options, callback) => {
|
||||||
filepath = filepath.replace(/\.tpl$/, '.js');
|
filepath = filepath.replace(/\.tpl$/, '.js');
|
||||||
|
|
||||||
@@ -77,18 +82,11 @@ web.install = async function (port) {
|
|||||||
app.use(bodyParser.urlencoded({
|
app.use(bodyParser.urlencoded({
|
||||||
extended: true,
|
extended: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(session({
|
|
||||||
secret: utils.generateUUID(),
|
|
||||||
resave: false,
|
|
||||||
saveUninitialized: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
compileTemplate(),
|
compileTemplate(),
|
||||||
compileSass(),
|
compileLess(),
|
||||||
runWebpack(),
|
compileJS(),
|
||||||
copyCSS(),
|
copyCSS(),
|
||||||
loadDefaults(),
|
loadDefaults(),
|
||||||
]);
|
]);
|
||||||
@@ -99,13 +97,6 @@ web.install = async function (port) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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, () => {
|
||||||
@@ -114,40 +105,19 @@ function launchExpress(port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupRoutes() {
|
function setupRoutes() {
|
||||||
app.get('/', csrfSynchronisedProtection, welcome);
|
app.get('/', welcome);
|
||||||
app.post('/', csrfSynchronisedProtection, 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'];
|
const dbs = ['redis', 'mongo', 'postgres'];
|
||||||
const databases = dbs.map((databaseName) => {
|
const databases = dbs.map((databaseName) => {
|
||||||
const questions = require(`../src/database/${databaseName}`).questions.filter(question => question && !question.hideOnWebInstall);
|
const questions = require(`../src/database/${databaseName}`).questions.filter(question => question && !question.hideOnWebInstall);
|
||||||
|
|
||||||
@@ -158,6 +128,7 @@ function welcome(req, res) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const defaults = require('./data/defaults.json');
|
const defaults = require('./data/defaults.json');
|
||||||
|
|
||||||
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,
|
||||||
@@ -170,8 +141,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,
|
|
||||||
csrf_token: generateToken(req),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,52 +148,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;
|
||||||
|
const setupEnvVars = nconf.get();
|
||||||
|
for (const [key, value] of Object.entries(req.body)) {
|
||||||
|
if (!process.env.hasOwnProperty(key)) {
|
||||||
|
setupEnvVars[key.replace(':', '__')] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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];
|
||||||
CONFIG: nconf.get('config'),
|
|
||||||
NODEBB_CONFIG: nconf.get('config'),
|
|
||||||
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 (const [parentKey, value] of Object.entries(setupEnvVars)) {
|
||||||
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||||
|
Object.keys(value).forEach(key => pushToRoot(parentKey, key));
|
||||||
|
delete setupEnvVars[parentKey];
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
setupEnvVars[parentKey] = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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'], {
|
const child = require('child_process').fork('app', ['--setup'], {
|
||||||
env: setupEnvVars,
|
env: setupEnvVars,
|
||||||
});
|
});
|
||||||
child.on('error', (err) => {
|
|
||||||
error = true;
|
|
||||||
success = false;
|
|
||||||
winston.error(err.stack);
|
|
||||||
});
|
|
||||||
child.on('close', (data) => {
|
child.on('close', (data) => {
|
||||||
|
installing = false;
|
||||||
success = data === 0;
|
success = data === 0;
|
||||||
error = data !== 0;
|
error = data !== 0;
|
||||||
launch();
|
|
||||||
|
welcome(req, res);
|
||||||
});
|
});
|
||||||
welcome(req, res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launch() {
|
async function launch(req, res) {
|
||||||
try {
|
try {
|
||||||
|
res.json({});
|
||||||
server.close();
|
server.close();
|
||||||
|
req.setTimeout(0);
|
||||||
let child;
|
let child;
|
||||||
|
|
||||||
if (!nconf.get('launchCmd')) {
|
if (!nconf.get('launchCmd')) {
|
||||||
@@ -246,20 +213,15 @@ async function launch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filesToDelete = [
|
const 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(
|
||||||
await Promise.all(
|
filesToDelete.map(
|
||||||
filesToDelete.map(
|
filename => fs.promises.unlink(path.join(__dirname, '../public', filename))
|
||||||
filename => fs.promises.unlink(filename)
|
)
|
||||||
)
|
);
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err.stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
child.unref();
|
child.unref();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -287,42 +249,51 @@ async function compileTemplate() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compileSass() {
|
async function compileLess() {
|
||||||
try {
|
try {
|
||||||
const installSrc = path.join(__dirname, '../public/scss/install.scss');
|
const installSrc = path.join(__dirname, '../public/less/install.less');
|
||||||
const style = await fs.promises.readFile(installSrc);
|
const style = await fs.promises.readFile(installSrc);
|
||||||
const scssOutput = sass.compileString(String(style), {
|
const css = await lessRenderAsync(style, { filename: path.resolve(installSrc) });
|
||||||
loadPaths: [
|
await fs.promises.writeFile(path.join(__dirname, '../public/installer.css'), css.css);
|
||||||
path.join(__dirname, '../public/scss'),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
await fs.promises.writeFile(path.join(__dirname, '../public/installer.css'), scssOutput.css.toString());
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
winston.error(`Unable to compile SASS: \n${err.stack}`);
|
winston.error(`Unable to compile LESS: \n${err.stack}`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function compileJS() {
|
||||||
|
let code = '';
|
||||||
|
|
||||||
|
for (const srcPath of scripts) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const buffer = await fs.promises.readFile(path.join(__dirname, '..', srcPath));
|
||||||
|
code += buffer.toString();
|
||||||
|
}
|
||||||
|
const minified = uglify.minify(code, {
|
||||||
|
compress: false,
|
||||||
|
});
|
||||||
|
if (!minified.code) {
|
||||||
|
throw new Error('[[error:failed-to-minify]]');
|
||||||
|
}
|
||||||
|
await fs.promises.writeFile(path.join(__dirname, '../public/installer.min.js'), minified.code);
|
||||||
|
}
|
||||||
|
|
||||||
async function copyCSS() {
|
async function copyCSS() {
|
||||||
await fs.promises.copyFile(
|
const src = await fs.promises.readFile(
|
||||||
path.join(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'),
|
path.join(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'), 'utf8'
|
||||||
path.join(__dirname, '../public/bootstrap.min.css'),
|
|
||||||
);
|
);
|
||||||
|
await fs.promises.writeFile(path.join(__dirname, '../public/bootstrap.min.css'), src);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadDefaults() {
|
async function loadDefaults() {
|
||||||
const setupDefaultsPath = path.join(__dirname, '../setup.json');
|
const setupDefaultsPath = path.join(__dirname, '../setup.json');
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-bitwise
|
await fs.promises.access(setupDefaultsPath, fs.constants.F_OK + fs.constants.R_OK);
|
||||||
await fs.promises.access(setupDefaultsPath, fs.constants.F_OK | fs.constants.R_OK);
|
|
||||||
} catch (err) {
|
} catch (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') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err;
|
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({
|
||||||
|
|||||||
84
loader.js
84
loader.js
@@ -5,8 +5,9 @@ const fs = require('fs');
|
|||||||
const url = require('url');
|
const url = require('url');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
|
const async = require('async');
|
||||||
const logrotate = require('logrotate-stream');
|
const logrotate = require('logrotate-stream');
|
||||||
const { mkdirp } = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
|
|
||||||
const file = require('./src/file');
|
const file = require('./src/file');
|
||||||
const pkg = require('./package.json');
|
const pkg = require('./package.json');
|
||||||
@@ -26,14 +27,16 @@ if (!fs.existsSync(logDir)) {
|
|||||||
mkdirp.sync(path.dirname(outputLogFilePath));
|
mkdirp.sync(path.dirname(outputLogFilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = logrotate({ file: outputLogFilePath, size: '10m', keep: 3, compress: true });
|
const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
||||||
const silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
|
const silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
|
||||||
let numProcs;
|
let numProcs;
|
||||||
const workers = [];
|
const workers = [];
|
||||||
const Loader = {};
|
const Loader = {
|
||||||
|
timesStarted: 0,
|
||||||
|
};
|
||||||
const appPath = path.join(__dirname, 'app.js');
|
const appPath = path.join(__dirname, 'app.js');
|
||||||
|
|
||||||
Loader.init = function () {
|
Loader.init = function (callback) {
|
||||||
if (silent) {
|
if (silent) {
|
||||||
console.log = (...args) => {
|
console.log = (...args) => {
|
||||||
output.write(`${args.join(' ')}\n`);
|
output.write(`${args.join(' ')}\n`);
|
||||||
@@ -42,19 +45,36 @@ Loader.init = function () {
|
|||||||
|
|
||||||
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', (code, signal) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
if (Loader.timesStarted < numProcs * 3) {
|
||||||
|
Loader.timesStarted += 1;
|
||||||
|
if (Loader.crashTimer) {
|
||||||
|
clearTimeout(Loader.crashTimer);
|
||||||
|
}
|
||||||
|
Loader.crashTimer = setTimeout(() => {
|
||||||
|
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})`);
|
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...');
|
||||||
@@ -87,21 +107,23 @@ 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 (let 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();
|
const ports = getPorts();
|
||||||
const args = [];
|
const args = [];
|
||||||
if (nconf.get('max-memory')) {
|
|
||||||
args.push(`--max-old-space-size=${nconf.get('max-memory')}`);
|
|
||||||
}
|
|
||||||
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}`);
|
||||||
}
|
}
|
||||||
@@ -193,25 +215,12 @@ 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' });
|
const pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' });
|
||||||
if (pid) {
|
process.kill(pid, 0);
|
||||||
process.kill(pid, 0);
|
process.exit();
|
||||||
console.info(`Process "${pid}" from pidfile already running, exiting`);
|
} catch (e) {
|
||||||
process.exit();
|
fs.unlinkSync(pidFilePath);
|
||||||
} else {
|
|
||||||
console.info(`Invalid pid "${pid}" from pidfile, deleting pidfile`);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,12 +232,15 @@ fs.open(pathToConfig, 'r', (err) => {
|
|||||||
|
|
||||||
fs.writeFileSync(pidFilePath, String(process.pid));
|
fs.writeFileSync(pidFilePath, String(process.pid));
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
Loader.init();
|
async.series([
|
||||||
Loader.displayStartupMessages();
|
Loader.init,
|
||||||
Loader.start();
|
Loader.displayStartupMessages,
|
||||||
} catch (err) {
|
Loader.start,
|
||||||
console.error('[loader] Error during startup');
|
], (err) => {
|
||||||
throw err;
|
if (err) {
|
||||||
}
|
console.error('[loader] Error during startup');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,82 @@
|
|||||||
{
|
{
|
||||||
"extends": "nodebb/public"
|
"globals": {
|
||||||
|
"app": true,
|
||||||
|
"io": true,
|
||||||
|
"socket": true,
|
||||||
|
"ajaxify": true,
|
||||||
|
"config": true,
|
||||||
|
"utils": true,
|
||||||
|
"overrides": true,
|
||||||
|
"componentHandler": true,
|
||||||
|
"bootbox": true,
|
||||||
|
"Visibility": true,
|
||||||
|
"Tinycon": true,
|
||||||
|
"Promise": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"jquery": true,
|
||||||
|
"amd": true,
|
||||||
|
"browser": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"comma-dangle": ["error", {
|
||||||
|
"arrays": "always-multiline",
|
||||||
|
"objects": "always-multiline",
|
||||||
|
"imports": "always-multiline",
|
||||||
|
"exports": "always-multiline",
|
||||||
|
"functions": "never"
|
||||||
|
}],
|
||||||
|
"block-scoped-var": "off",
|
||||||
|
"no-dupe-class-members": "off",
|
||||||
|
"prefer-object-spread": "off",
|
||||||
|
"prefer-reflect": "off",
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
// identical to airbnb rule
|
||||||
|
// except for allowing for..in, because for..of is unavailable on some clients
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "ForOfStatement",
|
||||||
|
"message": "iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "LabeledStatement",
|
||||||
|
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "WithStatement",
|
||||||
|
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"classes": false,
|
||||||
|
"defaultParams": 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,
|
||||||
|
"superInFunctions": false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
83
public/.jshintrc
Normal file
83
public/.jshintrc
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"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,
|
||||||
|
"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,41 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Internal Server Error</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/5xx.css" />
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.onload = function() {
|
|
||||||
let count = 0;
|
|
||||||
const bounce = document.getElementById('click-me');
|
|
||||||
bounce.onclick = function() {
|
|
||||||
count++;
|
|
||||||
bounce.className = '';
|
|
||||||
setTimeout(function() {
|
|
||||||
bounce.className = 'animated bounce';
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
if (count > 5) {
|
|
||||||
document.getElementById('hide').className = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="center">
|
|
||||||
<h1 id="click-me" class="animated bounce">500</h1>
|
|
||||||
<p>
|
|
||||||
<strong>Internal server error. </strong>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{message}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<small id="hide" class="hide">Alright. You can stop clicking... it's not going to make the site come back sooner!</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
142
public/503.html
142
public/503.html
@@ -2,12 +2,148 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Excessive Load Warning</title>
|
<title>Excessive Load Warning</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/5xx.css" />
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background: #00A9EA;
|
||||||
|
color: white;
|
||||||
|
/* see public/less/admin/vars.less for documentation on system font family */
|
||||||
|
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";
|
||||||
|
text-align: center;
|
||||||
|
-webkit-transform-style: preserve-3d;
|
||||||
|
-moz-transform-style: preserve-3d;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 250px;
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.5;
|
||||||
|
margin: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p strong {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 125px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p strong {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
-webkit-transform: translateY(50%);
|
||||||
|
-ms-transform: translateY(50%);
|
||||||
|
transform: translateY(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes bounce {
|
||||||
|
0%, 20%, 53%, 80%, 100% {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||||
|
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||||
|
-webkit-transform: translate3d(0,0,0);
|
||||||
|
transform: translate3d(0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40%, 43% {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
-webkit-transform: translate3d(0, -30px, 0);
|
||||||
|
transform: translate3d(0, -30px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
-webkit-transform: translate3d(0, -15px, 0);
|
||||||
|
transform: translate3d(0, -15px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
90% {
|
||||||
|
-webkit-transform: translate3d(0,-4px,0);
|
||||||
|
transform: translate3d(0,-4px,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%, 20%, 53%, 80%, 100% {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||||
|
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||||
|
-webkit-transform: translate3d(0,0,0);
|
||||||
|
transform: translate3d(0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40%, 43% {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
-webkit-transform: translate3d(0, -30px, 0);
|
||||||
|
transform: translate3d(0, -30px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
-webkit-transform: translate3d(0, -15px, 0);
|
||||||
|
transform: translate3d(0, -15px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
90% {
|
||||||
|
-webkit-transform: translate3d(0,-4px,0);
|
||||||
|
transform: translate3d(0,-4px,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bounce {
|
||||||
|
-webkit-animation-name: bounce;
|
||||||
|
animation-name: bounce;
|
||||||
|
-webkit-transform-origin: center bottom;
|
||||||
|
-ms-transform-origin: center bottom;
|
||||||
|
transform-origin: center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animated {
|
||||||
|
-webkit-animation-duration: 1s;
|
||||||
|
animation-duration: 1s;
|
||||||
|
-webkit-animation-fill-mode: both;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animated.infinite {
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animated.hinge {
|
||||||
|
-webkit-animation-duration: 2s;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
let count = 0;
|
var count = 0,
|
||||||
const bounce = document.getElementById('click-me');
|
bounce = document.getElementById('click-me');
|
||||||
bounce.onclick = function() {
|
bounce.onclick = function() {
|
||||||
count++;
|
count++;
|
||||||
bounce.className = '';
|
bounce.className = '';
|
||||||
|
|||||||
135
public/5xx.css
135
public/5xx.css
@@ -1,135 +0,0 @@
|
|||||||
body {
|
|
||||||
background: #00A9EA;
|
|
||||||
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";
|
|
||||||
text-align: center;
|
|
||||||
-webkit-transform-style: preserve-3d;
|
|
||||||
-moz-transform-style: preserve-3d;
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 250px;
|
|
||||||
color: #fff;
|
|
||||||
opacity: 0.5;
|
|
||||||
margin: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p strong {
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
h1 {
|
|
||||||
font-size: 125px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p strong {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
position: relative;
|
|
||||||
top: 50%;
|
|
||||||
-webkit-transform: translateY(50%);
|
|
||||||
-ms-transform: translateY(50%);
|
|
||||||
transform: translateY(50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes bounce {
|
|
||||||
0%, 20%, 53%, 80%, 100% {
|
|
||||||
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
|
||||||
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
|
||||||
-webkit-transform: translate3d(0,0,0);
|
|
||||||
transform: translate3d(0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
40%, 43% {
|
|
||||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
-webkit-transform: translate3d(0, -30px, 0);
|
|
||||||
transform: translate3d(0, -30px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
70% {
|
|
||||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
-webkit-transform: translate3d(0, -15px, 0);
|
|
||||||
transform: translate3d(0, -15px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
90% {
|
|
||||||
-webkit-transform: translate3d(0,-4px,0);
|
|
||||||
transform: translate3d(0,-4px,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bounce {
|
|
||||||
0%, 20%, 53%, 80%, 100% {
|
|
||||||
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
|
||||||
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
|
||||||
-webkit-transform: translate3d(0,0,0);
|
|
||||||
transform: translate3d(0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
40%, 43% {
|
|
||||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
-webkit-transform: translate3d(0, -30px, 0);
|
|
||||||
transform: translate3d(0, -30px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
70% {
|
|
||||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
|
||||||
-webkit-transform: translate3d(0, -15px, 0);
|
|
||||||
transform: translate3d(0, -15px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
90% {
|
|
||||||
-webkit-transform: translate3d(0,-4px,0);
|
|
||||||
transform: translate3d(0,-4px,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bounce {
|
|
||||||
-webkit-animation-name: bounce;
|
|
||||||
animation-name: bounce;
|
|
||||||
-webkit-transform-origin: center bottom;
|
|
||||||
-ms-transform-origin: center bottom;
|
|
||||||
transform-origin: center bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animated {
|
|
||||||
-webkit-animation-duration: 1s;
|
|
||||||
animation-duration: 1s;
|
|
||||||
-webkit-animation-fill-mode: both;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animated.infinite {
|
|
||||||
-webkit-animation-iteration-count: infinite;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animated.hinge {
|
|
||||||
-webkit-animation-duration: 2s;
|
|
||||||
animation-duration: 2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 215 KiB |
@@ -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,16 +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",
|
|
||||||
"add": "Add",
|
|
||||||
"select-icon": "Select Icon"
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cache": "Cache",
|
|
||||||
"post-cache": "التخزين المؤقت للمشاركات",
|
"post-cache": "التخزين المؤقت للمشاركات",
|
||||||
"group-cache": "التخزين المؤقت للمجموعات",
|
|
||||||
"local-cache": "تخزين مؤقت محلي",
|
|
||||||
"object-cache": "تخزين مؤقت للأشياء",
|
|
||||||
"percent-full": "1% كاملة",
|
"percent-full": "1% كاملة",
|
||||||
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
|
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
|
||||||
"items-in-cache": "العناصر في التخزين المؤقت"
|
"items-in-cache": "العناصر في التخزين المؤقت"
|
||||||
|
|||||||
@@ -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": "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 لم يتم العثور",
|
||||||
|
|||||||
@@ -3,15 +3,10 @@
|
|||||||
"no-events": "لا توجد أحداث",
|
"no-events": "لا توجد أحداث",
|
||||||
"control-panel": "لوحة تحكم الأحداث",
|
"control-panel": "لوحة تحكم الأحداث",
|
||||||
"delete-events": "حذف الاحداث",
|
"delete-events": "حذف الاحداث",
|
||||||
"confirm-delete-all-events": "هل أنت متاكد أنك تريد حذف كل سجل اﻻحداث؟",
|
|
||||||
"filters": "تصفية",
|
"filters": "تصفية",
|
||||||
"filters-apply": "تطبيق التصفية",
|
"filters-apply": "تطبيق التصفية",
|
||||||
"filter-type": "نوع الحدث",
|
"filter-type": "نوع الحدث",
|
||||||
"filter-start": "تاريخ البدء",
|
"filter-start": "تاريخ البدء",
|
||||||
"filter-end": "تاريخ الانتهاء",
|
"filter-end": "تاريخ الانتهاء",
|
||||||
"filter-user": "Filter by User",
|
"filter-perPage": "لكل صفحة"
|
||||||
"filter-user.placeholder": "قم بكتابة اسم مستخدم للتصفية...",
|
|
||||||
"filter-group": "Filter by Group",
|
|
||||||
"filter-group.placeholder": "قم بكتابة اسم مجموعة للتصفية...",
|
|
||||||
"filter-per-page": "لكل صفحة"
|
|
||||||
}
|
}
|
||||||
@@ -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,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"forum-traffic": "حركة المنتدى",
|
"forum-traffic": "Forum Traffic",
|
||||||
"page-views": "مشاهدات الصفحات",
|
"page-views": "مشاهدات الصفحات",
|
||||||
"unique-visitors": "زائرين فريدين",
|
"unique-visitors": "زائرين فريدين",
|
||||||
"logins": "عمليات تسجيل الدخول",
|
"logins": "Logins",
|
||||||
"new-users": "مستخدم جديد",
|
"new-users": "New Users",
|
||||||
"posts": "مشاركات",
|
"posts": "مشاركات",
|
||||||
"topics": "مواضيع",
|
"topics": "مواضيع",
|
||||||
"page-views-seven": "آخر 7 ايام",
|
"page-views-seven": "آخر 7 ايام",
|
||||||
@@ -15,24 +15,24 @@
|
|||||||
"page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو <code>YYYY-MM-DD</code>",
|
"page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو <code>YYYY-MM-DD</code>",
|
||||||
"page-views-custom-error": "الرجاء إدخال نطاق تاريخ صالح بالتنسيق <code>YYYY-MM-DD</code>",
|
"page-views-custom-error": "الرجاء إدخال نطاق تاريخ صالح بالتنسيق <code>YYYY-MM-DD</code>",
|
||||||
|
|
||||||
"stats.yesterday": "الأمس",
|
"stats.yesterday": "Yesterday",
|
||||||
"stats.today": "اليوم",
|
"stats.today": "Today",
|
||||||
"stats.last-week": "الاسبوع الماضي",
|
"stats.last-week": "Last Week",
|
||||||
"stats.this-week": "هذا الاسبوع",
|
"stats.this-week": "This Week",
|
||||||
"stats.last-month": "الشهر الماضي",
|
"stats.last-month": "Last Month",
|
||||||
"stats.this-month": "هذا الشهر",
|
"stats.this-month": "This Month",
|
||||||
"stats.all": "كل الوقت",
|
"stats.all": "كل الوقت",
|
||||||
|
|
||||||
"updates": "تحديثات",
|
"updates": "تحديثات",
|
||||||
"running-version": "المنتدى يعمل حاليا على <strong>NodeBB الإصدار<span id=\"version\">%1</span></strong>.",
|
"running-version": "المنتدى يعمل حاليا على <strong>NodeBB الإصدار<span id=\"version\">%1</span></strong>.",
|
||||||
"keep-updated": "تأكد دائما من أن NodeBB يعمل على احدث إصدار للحصول على أحدث التصحيحات الأمنية وإصلاحات الأخطاء.",
|
"keep-updated": "تأكد دائما من أن NodeBB يعمل على احدث إصدار للحصول على أحدث التصحيحات الأمنية وإصلاحات الأخطاء.",
|
||||||
"up-to-date": "You are <strong>up-to-date</strong> <i class=\"fa fa-check\"></i>",
|
"up-to-date": "<p>المنتدى <strong>يعمل على أحدث إصدار</strong> <i class=\"fa fa-check\"></i></p>",
|
||||||
"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>.",
|
"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": "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-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": "This is a <strong>pre-release</strong> version of NodeBB. Unintended bugs may occur. <i class=\"fa fa-exclamation-triangle\"></i>",
|
"prerelease-warning": "<p>هذه نسخة <strong>ماقبل الإصدار</strong> من NodeBB. قد تحدث أخطاء غير مقصودة. <i class=\"fa fa-exclamation-triangle\"></i></p>",
|
||||||
"fallback-emailer-not-found": "مرسل البريد الإلكتروني الاحتياطي غير موجود!",
|
"fallback-emailer-not-found": "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",
|
"running-in-development": "المنتدى قيد التشغيل في وضع \"المطورين\". وقد تكون هناك ثغرات أمنية مفتوحة؛ من فضلك تواصل مع مسؤول نظامك.",
|
||||||
"latest-lookup-failed": "Failed to look up latest available version of NodeBB",
|
"latest-lookup-failed": "<p>Failed to look up latest available version of NodeBB</p>",
|
||||||
|
|
||||||
"notices": "إشعارات",
|
"notices": "إشعارات",
|
||||||
"restart-not-required": "إعادة التشغيل غير مطلوب",
|
"restart-not-required": "إعادة التشغيل غير مطلوب",
|
||||||
@@ -42,23 +42,22 @@
|
|||||||
"search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث",
|
"search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث",
|
||||||
|
|
||||||
"control-panel": "التحكم بالنظام",
|
"control-panel": "التحكم بالنظام",
|
||||||
"rebuild-and-restart": "أعد البناء & وأعد التشغيل",
|
"rebuild-and-restart": "Rebuild & Restart",
|
||||||
"restart": "أعد التشغيل",
|
"restart": "Restart",
|
||||||
"restart-warning": "ستؤدي إعادة بناء أو إعادة تشغيل NodeBB إلى إنقطاع الاتصال بالمنتدى لبضع ثوان.",
|
"restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.",
|
||||||
"restart-disabled": "تم تعطيل إعادة بناء NodeBB وإعادة تشغيله حيث لا يبدو أنك تقوم بتشغيله عبر البرنامج الخفي المناسب.",
|
"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": "وضع الصيانة",
|
||||||
"maintenance-mode-title": "انقر هنا لإعداد وضع الصيانة لـNodeBB",
|
"maintenance-mode-title": "انقر هنا لإعداد وضع الصيانة لـNodeBB",
|
||||||
"dark-mode": "Dark Mode",
|
|
||||||
"realtime-chart-updates": "التحديث الفوري للرسم البياني",
|
"realtime-chart-updates": "التحديث الفوري للرسم البياني",
|
||||||
|
|
||||||
"active-users": "المستخدمين النشطين",
|
"active-users": "المستخدمين النشطين",
|
||||||
"active-users.users": "الأعضاء",
|
"active-users.users": "الأعضاء",
|
||||||
"active-users.guests": "الزوار",
|
"active-users.guests": "الزوار",
|
||||||
"active-users.total": "المجموع",
|
"active-users.total": "المجموع",
|
||||||
"active-users.connections": "روابط الاتصال",
|
"active-users.connections": "Connections",
|
||||||
|
|
||||||
"guest-registered-users": "الزائر مقابل المستخدمين المسجلين",
|
"anonymous-registered-users": "المجهولين مقابل المستخدمين المسجلين",
|
||||||
"guest": "الزائر",
|
"anonymous": "مجهول",
|
||||||
"registered": "مسجل",
|
"registered": "مسجل",
|
||||||
|
|
||||||
"user-presence": "تواجد المستخدمين",
|
"user-presence": "تواجد المستخدمين",
|
||||||
@@ -69,33 +68,21 @@
|
|||||||
"unread": "غير مقروء",
|
"unread": "غير مقروء",
|
||||||
|
|
||||||
"high-presence-topics": "مواضيع ذات حضور قوي",
|
"high-presence-topics": "مواضيع ذات حضور قوي",
|
||||||
"popular-searches": "عمليات البحث الشائعة",
|
|
||||||
|
|
||||||
"graphs.page-views": "مشاهدات الصفحة",
|
"graphs.page-views": "مشاهدات الصفحة",
|
||||||
"graphs.page-views-registered": "زيارات الصفحات المسجلة",
|
"graphs.page-views-registered": "Page Views Registered",
|
||||||
"graphs.page-views-guest": "زيارات الصفحات للزوار",
|
"graphs.page-views-guest": "Page Views Guest",
|
||||||
"graphs.page-views-bot": "زيارات الصفحات الآلية",
|
"graphs.page-views-bot": "Page Views Bot",
|
||||||
"graphs.unique-visitors": "زوار فريدين",
|
"graphs.unique-visitors": "زوار فريدين",
|
||||||
"graphs.registered-users": "مستخدمين مسجلين",
|
"graphs.registered-users": "مستخدمين مسجلين",
|
||||||
"graphs.guest-users": "المستخدمين الزوار",
|
"graphs.anonymous-users": "مستخدمين مجهولين",
|
||||||
"last-restarted-by": "آخر إعادة تشغيل بواسطة",
|
"last-restarted-by": "Last restarted by",
|
||||||
"no-users-browsing": "لا يوجد مستخدمين يتصفحون",
|
"no-users-browsing": "No users browsing",
|
||||||
|
|
||||||
"back-to-dashboard": "العودة للوحة التحكم",
|
"back-to-dashboard": "Back to Dashboard",
|
||||||
"details.no-users": "لم ينضم أي مستخدم خلال الإطار الزمني المحدد",
|
"details.no-users": "No users have joined within the selected timeframe",
|
||||||
"details.no-topics": "لم يتم نشر أي مواضيع خلال الإطار الزمني المحدد",
|
"details.no-topics": "No topics have been posted within the selected timeframe",
|
||||||
"details.no-searches": "لم يتم إجراء أي بحث خلال الإطار الزمني المحدد",
|
"details.no-logins": "No logins have been recorded within the selected timeframe",
|
||||||
"details.no-logins": "لم يوثق أي تسجيل دخول خلال الإطار الزمني المحدد",
|
"details.logins-static": "NodeBB only saves session data for %1 days, and so this table below will only show the most recently active sessions",
|
||||||
"details.logins-static": "يقوم NodeBB بحفظ بيانات الجلسة لمدة ٪1 يوم/أيام فقط، ولذا فإن هذا الجدول أدناه سيعرض فقط أحدث الجلسات النشطة",
|
"details.logins-login-time": "Login Time"
|
||||||
"details.logins-login-time": "وقت تسجيل الدخول",
|
|
||||||
"start": "بدء",
|
|
||||||
"end": "إنهاء",
|
|
||||||
"filter": "تصفية",
|
|
||||||
"view-as-json": "View as JSON",
|
|
||||||
"expand-analytics": "Expand analytics",
|
|
||||||
"clear-search-history": "Clear Search History",
|
|
||||||
"clear-search-history-confirm": "Are you sure you want to clear entire search history?",
|
|
||||||
"search-term": "Term",
|
|
||||||
"search-count": "Count",
|
|
||||||
"view-all": "View all"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,20 @@
|
|||||||
{
|
{
|
||||||
"you-are-on": "أنت في <strong> %1:%2 </strong>",
|
"you-are-on": "You are on <strong>%1:%2</strong>",
|
||||||
"ip": "رقم الآي بي <strong> %1 </strong>",
|
"ip": "IP <strong>%1</strong>",
|
||||||
"nodes-responded": "عدد %1 نقطة/نقاط استجابوا خلال %2 جزء من الثانية.",
|
"nodes-responded": "%1 nodes responded within %2ms!",
|
||||||
"host": "المضيف",
|
"host": "host",
|
||||||
"primary": "primary / jobs",
|
"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",
|
||||||
|
|
||||||
"registered": "Registered",
|
"registered": "Registered",
|
||||||
"sockets": "Sockets",
|
"sockets": "Sockets",
|
||||||
"connection-count": "Connection Count",
|
|
||||||
"guests": "Guests",
|
"guests": "Guests",
|
||||||
|
|
||||||
"info": "Info"
|
"info": "Info"
|
||||||
|
|||||||
@@ -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,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"plugins": "Plugins",
|
|
||||||
"trending": "Trending",
|
"trending": "Trending",
|
||||||
"installed": "منصبة",
|
"installed": "منصبة",
|
||||||
"active": "مفعلة",
|
"active": "مفعلة",
|
||||||
|
|||||||
@@ -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,19 +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 widget essentials 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 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.title": "Title",
|
|
||||||
"container.body": "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?",
|
||||||
@@ -29,9 +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",
|
"hide-on-mobile": "Hide on mobile"
|
||||||
"end-date": "End date",
|
|
||||||
"hide-on-mobile": "Hide on mobile",
|
|
||||||
"hide-drafts": "Hide drafts",
|
|
||||||
"show-drafts": "Show drafts"
|
|
||||||
}
|
}
|
||||||
79
public/language/ar/admin/general/dashboard.json
Normal file
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"
|
||||||
|
}
|
||||||
8
public/language/ar/admin/general/homepage.json
Normal file
8
public/language/ar/admin/general/homepage.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"home-page": "Home Page",
|
||||||
|
"description": "Choose what page is shown when users navigate to the root URL of your forum.",
|
||||||
|
"home-page-route": "Home Page Route",
|
||||||
|
"custom-route": "Custom Route",
|
||||||
|
"allow-user-home-pages": "Allow User Home Pages",
|
||||||
|
"home-page-title": "Title of the home page (default \"Home\")"
|
||||||
|
}
|
||||||
6
public/language/ar/admin/general/languages.json
Normal file
6
public/language/ar/admin/general/languages.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"language-settings": "اعدادات اللغة",
|
||||||
|
"description": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
|
||||||
|
"default-language": "اللغة الافتراضية",
|
||||||
|
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي"
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"navigation": "Navigation",
|
|
||||||
"icon": "Icon:",
|
"icon": "Icon:",
|
||||||
"change-icon": "change",
|
"change-icon": "change",
|
||||||
"route": "Route:",
|
"route": "Route:",
|
||||||
@@ -10,10 +9,8 @@
|
|||||||
"id": "ID: <small>optional</small>",
|
"id": "ID: <small>optional</small>",
|
||||||
|
|
||||||
"properties": "Properties:",
|
"properties": "Properties:",
|
||||||
"show-to-groups": "Show to 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",
|
||||||
@@ -23,4 +20,4 @@
|
|||||||
"custom-route": "Custom Route",
|
"custom-route": "Custom Route",
|
||||||
"core": "core",
|
"core": "core",
|
||||||
"plugin": "plugin"
|
"plugin": "plugin"
|
||||||
}
|
}
|
||||||
5
public/language/ar/admin/general/social.json
Normal file
5
public/language/ar/admin/general/social.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"post-sharing": "Post Sharing",
|
||||||
|
"info-plugins-additional": "Plugins can add additional networks for sharing posts.",
|
||||||
|
"save-success": "Successfully saved Post Sharing Networks!"
|
||||||
|
}
|
||||||
9
public/language/ar/admin/general/sounds.json
Normal file
9
public/language/ar/admin/general/sounds.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"notifications": "التنبيهات",
|
||||||
|
"chat-messages": "Chat Messages",
|
||||||
|
"play-sound": "Play",
|
||||||
|
"incoming-message": "Incoming Message",
|
||||||
|
"outgoing-message": "Outgoing Message",
|
||||||
|
"upload-new-sound": "Upload New Sound",
|
||||||
|
"saved": "Settings Saved"
|
||||||
|
}
|
||||||
@@ -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,14 +1,8 @@
|
|||||||
{
|
{
|
||||||
"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",
|
||||||
"handle": "Category Handle",
|
|
||||||
"handle.help": "Your category handle is used as a representation of this category across other networks, similar to a username. A category handle must not match an existing username or user group.",
|
|
||||||
"description": "Category Description",
|
"description": "Category Description",
|
||||||
"bg-color": "Background Colour",
|
"bg-color": "Background Colour",
|
||||||
"text-color": "Text Colour",
|
"text-color": "Text Colour",
|
||||||
@@ -21,10 +15,8 @@
|
|||||||
"post-queue": "Post queue",
|
"post-queue": "Post queue",
|
||||||
"tag-whitelist": "Tag Whitelist",
|
"tag-whitelist": "Tag Whitelist",
|
||||||
"upload-image": "Upload Image",
|
"upload-image": "Upload Image",
|
||||||
"upload": "Upload",
|
|
||||||
"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",
|
"top-level": "Top Level",
|
||||||
@@ -39,8 +31,6 @@
|
|||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"analytics": "Analytics",
|
"analytics": "Analytics",
|
||||||
"federation": "Federation",
|
|
||||||
|
|
||||||
"view-category": "View category",
|
"view-category": "View category",
|
||||||
"set-order": "Set order",
|
"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.",
|
"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.",
|
||||||
@@ -79,22 +69,6 @@
|
|||||||
"analytics.topics-daily": "<strong>Figure 3</strong> – Daily topics created in this category</small>",
|
"analytics.topics-daily": "<strong>Figure 3</strong> – Daily topics created in this category</small>",
|
||||||
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
||||||
|
|
||||||
"federation.title": "Federation settings for \"%1\" category",
|
|
||||||
"federation.disabled": "Federation is disabled site-wide, so category federation settings are currently unavailable.",
|
|
||||||
"federation.disabled-cta": "Federation Settings →",
|
|
||||||
"federation.syncing-header": "Synchronization",
|
|
||||||
"federation.syncing-intro": "A category can follow a \"Group Actor\" via the ActivityPub protocol. If content is received from one of the actors listed below, it will be automatically added to this category.",
|
|
||||||
"federation.syncing-caveat": "N.B. Setting up syncing here establishes a one-way synchronization. NodeBB attempts to subscribe/follow the actor, but the reverse cannot be assumed.",
|
|
||||||
"federation.syncing-none": "This category is not currently following anybody.",
|
|
||||||
"federation.syncing-add": "Synchronize with...",
|
|
||||||
"federation.syncing-actorUri": "Actor",
|
|
||||||
"federation.syncing-follow": "Follow",
|
|
||||||
"federation.syncing-unfollow": "Unfollow",
|
|
||||||
"federation.followers": "Remote users following this category",
|
|
||||||
"federation.followers-handle": "Handle",
|
|
||||||
"federation.followers-id": "ID",
|
|
||||||
"federation.followers-none": "No followers.",
|
|
||||||
|
|
||||||
"alert.created": "Created",
|
"alert.created": "Created",
|
||||||
"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.",
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"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",
|
||||||
@@ -16,7 +10,7 @@
|
|||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"privileges": "Privileges",
|
"privileges": "Privileges",
|
||||||
"members-csv": "Members (CSV)",
|
"download-csv": "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 +39,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!"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"manage-privileges": "Manage Privileges",
|
|
||||||
"discard-changes": "Discard changes",
|
|
||||||
"global": "Global",
|
"global": "Global",
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"group-privileges": "Group Privileges",
|
"group-privileges": "Group Privileges",
|
||||||
@@ -8,12 +6,10 @@
|
|||||||
"edit-privileges": "Edit Privileges",
|
"edit-privileges": "Edit Privileges",
|
||||||
"select-clear-all": "Select/Clear All",
|
"select-clear-all": "Select/Clear All",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"chat-with-privileged": "Chat with Privileged",
|
|
||||||
"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",
|
"invite": "Invite",
|
||||||
"search-content": "Search Content",
|
"search-content": "Search Content",
|
||||||
"search-users": "Search Users",
|
"search-users": "Search Users",
|
||||||
@@ -34,7 +30,7 @@
|
|||||||
"edit-posts": "Edit Posts",
|
"edit-posts": "Edit Posts",
|
||||||
"view-edit-history": "View Edit History",
|
"view-edit-history": "View Edit History",
|
||||||
"delete-posts": "Delete Posts",
|
"delete-posts": "Delete Posts",
|
||||||
"view-deleted": "View Deleted Posts",
|
"view_deleted": "View Deleted Posts",
|
||||||
"upvote-posts": "Upvote Posts",
|
"upvote-posts": "Upvote Posts",
|
||||||
"downvote-posts": "Downvote Posts",
|
"downvote-posts": "Downvote Posts",
|
||||||
"delete-topics": "Delete Topics",
|
"delete-topics": "Delete Topics",
|
||||||
@@ -52,15 +48,13 @@
|
|||||||
"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-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-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-save": "Please confirm your intention to save these privileges",
|
||||||
|
"alert.saved": "Privilege changes saved and applied",
|
||||||
"alert.confirm-discard": "Are you sure you wish to discard your privilege changes?",
|
"alert.confirm-discard": "Are you sure you wish to discard your privilege changes?",
|
||||||
"alert.discarded": "Privilege changes discarded",
|
"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-copyToAll": "Are you sure you wish to apply this privilege set 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-copyToAllGroup": "Are you sure you wish to apply this group's privilege set 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-copyToChildren": "Are you sure you wish to apply this privilege set 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.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's privilege set to <strong>all descendant (child) categories</strong>?",
|
||||||
"alert.no-undo": "<em>This action cannot be undone.</em>",
|
"alert.no-undo": "<em>This action cannot be undone.</em>",
|
||||||
"alert.admin-warning": "Administrators implicitly get all privileges",
|
"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,11 +1,9 @@
|
|||||||
{
|
{
|
||||||
"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.",
|
"description": "Select tags by clicking or dragging, use <code>CTRL</code> to select multiple tags.",
|
||||||
"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",
|
||||||
|
|||||||
@@ -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,28 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Manage Custom User Fields",
|
|
||||||
"create-field": "Create Field",
|
|
||||||
"edit-field": "Edit Field",
|
|
||||||
"manage-custom-fields": "Manage Custom Fields",
|
|
||||||
"type-of-input": "Type of input",
|
|
||||||
"key": "Key",
|
|
||||||
"name": "Name",
|
|
||||||
"icon": "Icon",
|
|
||||||
"type": "Type",
|
|
||||||
"min-rep": "Minimum Reputation",
|
|
||||||
"input-type-text": "Input (Text)",
|
|
||||||
"input-type-link": "Input (Link)",
|
|
||||||
"input-type-number": "Input (Number)",
|
|
||||||
"input-type-date": "Input (Date)",
|
|
||||||
"input-type-select": "Select",
|
|
||||||
"input-type-select-multi": "Select Multiple",
|
|
||||||
"select-options": "Options",
|
|
||||||
"select-options-help": "Add one option per line for the select element",
|
|
||||||
"minimum-reputation": "Minimum reputation",
|
|
||||||
"minimum-reputation-help": "If a user has less than this value they won't be able to use this field",
|
|
||||||
"delete-field-confirm-x": "Do you really want to delete custom field \"%1\"?",
|
|
||||||
"custom-fields-saved": "Custom fields saved",
|
|
||||||
"visibility": "Visibility",
|
|
||||||
"visibility-all": "Everyone can see the field",
|
|
||||||
"visibility-loggedin": "Only logged in users can see the field",
|
|
||||||
"visibility-privileged": "Only privileged users like admins & moderators can see the field"
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,24 @@
|
|||||||
{
|
{
|
||||||
"manage-users": "Manage Users",
|
|
||||||
"users": "المستخدمين",
|
"users": "المستخدمين",
|
||||||
"edit": "Actions",
|
"edit": "Actions",
|
||||||
"make-admin": "Make Admin",
|
"make-admin": "Make Admin",
|
||||||
"remove-admin": "Remove Admin",
|
"remove-admin": "Remove Admin",
|
||||||
"change-email": "Change Email",
|
|
||||||
"new-email": "New Email",
|
|
||||||
"validate-email": "Validate Email",
|
"validate-email": "Validate Email",
|
||||||
"send-validation-email": "Send Validation Email",
|
"send-validation-email": "Send Validation Email",
|
||||||
"change-password": "Change Password",
|
|
||||||
"password-reset-email": "Send Password Reset Email",
|
"password-reset-email": "Send Password Reset Email",
|
||||||
"force-password-reset": "Force Password Reset & Log User Out",
|
"force-password-reset": "Force Password Reset & Log User Out",
|
||||||
"ban": "Ban",
|
"ban": "Ban User(s)",
|
||||||
"ban-users": "Ban User(s)",
|
|
||||||
"temp-ban": "Ban User(s) Temporarily",
|
"temp-ban": "Ban User(s) Temporarily",
|
||||||
"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",
|
"delete": "Delete <strong>User(s)</strong>",
|
||||||
"delete-users": "Delete <strong>User(s)</strong>",
|
|
||||||
"delete-content": "Delete User(s) <strong>Content</strong>",
|
"delete-content": "Delete User(s) <strong>Content</strong>",
|
||||||
"purge": "Delete <strong>User(s)</strong> and <strong>Content</strong>",
|
"purge": "Delete <strong>User(s)</strong> and <strong>Content</strong>",
|
||||||
"download-csv": "Download CSV",
|
"download-csv": "Download CSV",
|
||||||
"custom-user-fields": "Custom User Fields",
|
|
||||||
"manage-groups": "Manage Groups",
|
"manage-groups": "Manage Groups",
|
||||||
"set-reputation": "Set Reputation",
|
|
||||||
"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",
|
"filter-by": "Filter by",
|
||||||
"pills.unvalidated": "Not Validated",
|
"pills.unvalidated": "Not Validated",
|
||||||
@@ -56,11 +47,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.ip": "IP",
|
||||||
"users.postcount": "postcount",
|
"users.postcount": "postcount",
|
||||||
"users.reputation": "reputation",
|
"users.reputation": "reputation",
|
||||||
@@ -75,7 +61,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",
|
||||||
@@ -87,7 +73,6 @@
|
|||||||
"alerts.button-ban-x": "Ban %1 user(s)",
|
"alerts.button-ban-x": "Ban %1 user(s)",
|
||||||
"alerts.unban-success": "User(s) unbanned!",
|
"alerts.unban-success": "User(s) unbanned!",
|
||||||
"alerts.lockout-reset-success": "Lockout(s) reset!",
|
"alerts.lockout-reset-success": "Lockout(s) reset!",
|
||||||
"alerts.password-change-success": "Password(s) changed!",
|
|
||||||
"alerts.flag-reset-success": "Flags(s) reset!",
|
"alerts.flag-reset-success": "Flags(s) reset!",
|
||||||
"alerts.no-remove-yourself-admin": "You can't remove yourself as Administrator!",
|
"alerts.no-remove-yourself-admin": "You can't remove yourself as Administrator!",
|
||||||
"alerts.make-admin-success": "User is now administrator.",
|
"alerts.make-admin-success": "User is now administrator.",
|
||||||
@@ -104,7 +89,6 @@
|
|||||||
"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": "<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.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-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>",
|
||||||
@@ -113,7 +97,6 @@
|
|||||||
"alerts.create": "Create User",
|
"alerts.create": "Create User",
|
||||||
"alerts.button-create": "Create",
|
"alerts.button-create": "Create",
|
||||||
"alerts.button-cancel": "Cancel",
|
"alerts.button-cancel": "Cancel",
|
||||||
"alerts.button-change": "Change",
|
|
||||||
"alerts.error-passwords-different": "Passwords must match!",
|
"alerts.error-passwords-different": "Passwords must match!",
|
||||||
"alerts.error-x": "<strong>Error</strong><p>%1</p>",
|
"alerts.error-x": "<strong>Error</strong><p>%1</p>",
|
||||||
"alerts.create-success": "User created!",
|
"alerts.create-success": "User created!",
|
||||||
@@ -121,32 +104,6 @@
|
|||||||
"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, (%2 seconds)",
|
||||||
"alerts.select-a-single-user-to-change-email": "Select a single user to change email",
|
|
||||||
"export": "Export",
|
|
||||||
"export-users-fields-title": "Select CSV Fields",
|
|
||||||
"export-field-email": "Email",
|
|
||||||
"export-field-username": "Username",
|
|
||||||
"export-field-uid": "UID",
|
|
||||||
"export-field-ip": "IP",
|
|
||||||
"export-field-joindate": "Join date",
|
|
||||||
"export-field-lastonline": "Last Online",
|
|
||||||
"export-field-lastposttime": "Last Post Time",
|
|
||||||
"export-field-reputation": "Reputation",
|
|
||||||
"export-field-postcount": "Post Count",
|
|
||||||
"export-field-topiccount": "Topic Count",
|
|
||||||
"export-field-profileviews": "Profile Views",
|
|
||||||
"export-field-followercount": "Follower Count",
|
|
||||||
"export-field-followingcount": "Following Count",
|
|
||||||
"export-field-fullname": "Full Name",
|
|
||||||
"export-field-website": "Website",
|
|
||||||
"export-field-location": "Location",
|
|
||||||
"export-field-birthday": "Birthday",
|
|
||||||
"export-field-signature": "Signature",
|
|
||||||
"export-field-aboutme": "About Me",
|
|
||||||
|
|
||||||
"export-users-started": "Exporting users as csv, this might take a while. You will receive a notification when it is complete.",
|
"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.",
|
"export-users-completed": "Users exported as csv, click here to download."
|
||||||
"email": "Email",
|
|
||||||
"password": "Password",
|
|
||||||
"manage": "Manage"
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
"dashboard/logins": "Logins",
|
"dashboard/logins": "Logins",
|
||||||
"dashboard/users": "Users",
|
"dashboard/users": "Users",
|
||||||
"dashboard/topics": "Topics",
|
"dashboard/topics": "Topics",
|
||||||
"dashboard/searches": "Searches",
|
|
||||||
"section-general": "عام",
|
"section-general": "عام",
|
||||||
|
|
||||||
"section-manage": "إدارة",
|
"section-manage": "إدارة",
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
"manage/users": "الأعضاء",
|
"manage/users": "الأعضاء",
|
||||||
"manage/admins-mods": "Admins & Mods",
|
"manage/admins-mods": "Admins & Mods",
|
||||||
"manage/registration": "قائمة انتظار التسجيل",
|
"manage/registration": "قائمة انتظار التسجيل",
|
||||||
"manage/flagged-content": "Flagged Content",
|
|
||||||
"manage/post-queue": "قائمة انتظار المشاركة",
|
"manage/post-queue": "قائمة انتظار المشاركة",
|
||||||
"manage/groups": "المجموعات",
|
"manage/groups": "المجموعات",
|
||||||
"manage/ip-blacklist": "قائمة حظر عناوين IP",
|
"manage/ip-blacklist": "قائمة حظر عناوين IP",
|
||||||
@@ -38,7 +36,6 @@
|
|||||||
"settings/tags": "الكلمات المفتاحية",
|
"settings/tags": "الكلمات المفتاحية",
|
||||||
"settings/notifications": "التنبيهات",
|
"settings/notifications": "التنبيهات",
|
||||||
"settings/api": "API Access",
|
"settings/api": "API Access",
|
||||||
"settings/activitypub": "Federation (ActivityPub)",
|
|
||||||
"settings/sounds": "Sounds",
|
"settings/sounds": "Sounds",
|
||||||
"settings/social": "Social",
|
"settings/social": "Social",
|
||||||
"settings/cookies": "Cookies",
|
"settings/cookies": "Cookies",
|
||||||
@@ -74,13 +71,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": "Press "/" to 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...",
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"intro-lead": "What is Federation?",
|
|
||||||
"intro-body": "NodeBB is able to communicate with other NodeBB instances that support it. This is achieved through a protocol called <a href=\"https://activitypub.rocks/\">ActivityPub</a>. If enabled, NodeBB will also be able to communicate with other apps and websites that use ActivityPub (e.g. Mastodon, Peertube, etc.)",
|
|
||||||
"general": "General",
|
|
||||||
"pruning": "Content Pruning",
|
|
||||||
"content-pruning": "Days to keep remote content",
|
|
||||||
"content-pruning-help": "Note that remote content that has received engagement (a reply or a upvote/downvote) will be preserved. (0 for disabled)",
|
|
||||||
"user-pruning": "Days to cache remote user accounts",
|
|
||||||
"user-pruning-help": "Remote user accounts will only be pruned if they have no posts. Otherwise they will be re-retrieved. (0 for disabled)",
|
|
||||||
"enabled": "Enable Federation",
|
|
||||||
"enabled-help": "If enabled, will allow this NodeBB will be able to communicate with all Activitypub-enabled clients on the wider fediverse.",
|
|
||||||
"allowLoopback": "Allow loopback processing",
|
|
||||||
"allowLoopback-help": "Useful for debugging purposes only. You should probably leave this disabled.",
|
|
||||||
|
|
||||||
"probe": "Open in App",
|
|
||||||
"probe-enabled": "Try to open ActivityPub-enabled resources in NodeBB",
|
|
||||||
"probe-enabled-help": "If enabled, NodeBB will check every external link for an ActivityPub equivalent, and load it in NodeBB instead.",
|
|
||||||
"probe-timeout": "Lookup Timeout (milliseconds)",
|
|
||||||
"probe-timeout-help": "(Default: 2000) If the lookup query does not receive a response within the set timeframe, will send the user to the link directly instead. Adjust this number higher if sites are responding slowly and you wish to give extra time.",
|
|
||||||
|
|
||||||
"server-filtering": "Filtering",
|
|
||||||
"count": "This NodeBB is currently aware of <strong>%1</strong> server(s)",
|
|
||||||
"server.filter-help": "Specify servers you would like to bar from federating with your NodeBB. Alternatively, you may opt to selectively <em>allow</em> federation with specific servers, instead. Both options are supported, although they are mutually exclusive.",
|
|
||||||
"server.filter-help-hostname": "Enter just the instance hostname below (e.g. <code>example.org</code>), separated by line breaks.",
|
|
||||||
"server.filter-allow-list": "Use this as an Allow List instead"
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
"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": "Set Content-Security-Policy frame-ancestors header to Place NodeBB in an iFrame",
|
||||||
@@ -16,12 +15,6 @@
|
|||||||
"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.maxAge": "HSTS Max Age",
|
||||||
|
|||||||
@@ -3,27 +3,14 @@
|
|||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"lead-text": "From this page you can configure access to the Write API in NodeBB.",
|
"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.",
|
"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",
|
"docs": "Click here to access the full API specification",
|
||||||
|
|
||||||
"require-https": "Require API usage via HTTPS only",
|
"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.",
|
"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",
|
"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",
|
"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",
|
"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.",
|
"no-description": "No description specified.",
|
||||||
"actions": "Actions",
|
"token-on-save": "Token will be generated once form is saved"
|
||||||
"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."
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
{
|
{
|
||||||
"zero-is-disabled": "Enter 0 to disable this restriction",
|
|
||||||
"chat-settings": "Chat Settings",
|
"chat-settings": "Chat Settings",
|
||||||
"disable": "Disable chat",
|
"disable": "Disable chat",
|
||||||
"disable-editing": "Disable chat message editing/deletion",
|
"disable-editing": "Disable chat message editing/deletion",
|
||||||
"disable-editing-help": "Administrators and global moderators are exempt from this restriction",
|
"disable-editing-help": "Administrators and global moderators are exempt from this restriction",
|
||||||
"max-length": "Maximum length of chat messages",
|
"max-length": "Maximum length of chat messages",
|
||||||
"max-chat-room-name-length": "Maximum length of chat room names",
|
|
||||||
"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 (ms)",
|
"delay": "Time between chat messages in milliseconds",
|
||||||
"notification-delay": "Notification delay for chat messages",
|
"notification-delay": "Notification delay for chat messages. (0 for no delay)",
|
||||||
"notification-delay-help": "Additional messages sent between this time are collated, and the user is notified once per delay period. Set this to 0 to disable the 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.",
|
"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."
|
|
||||||
}
|
}
|
||||||
@@ -5,18 +5,14 @@
|
|||||||
"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.",
|
||||||
|
|
||||||
"confirmation-settings": "Confirmation",
|
|
||||||
"confirmation.expiry": "Hours to keep email confirmation link valid",
|
|
||||||
|
|
||||||
"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",
|
||||||
@@ -40,13 +36,6 @@
|
|||||||
"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",
|
"notifications.settings": "Email notification settings",
|
||||||
"require-email-address": "Require new users to specify an email address",
|
"notifications.remove-images": "Remove images from email notifications"
|
||||||
"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,13 +1,11 @@
|
|||||||
{
|
{
|
||||||
"general-settings": "General Settings",
|
|
||||||
"on-this-page": "On this page:",
|
|
||||||
"site-settings": "اعدادات الموقع",
|
"site-settings": "اعدادات الموقع",
|
||||||
"title": "عنوان الموقع",
|
"title": "عنوان الموقع",
|
||||||
"title.short": "عنوان قصير",
|
"title.short": "عنوان قصير",
|
||||||
"title.short-placeholder": "ان لم تقم بكتابة عنوان مختصر, سيتم استخدام عنوان الموقع الكلي",
|
"title.short-placeholder": "ان لم تقم بكتابة عنوان مختصر, سيتم استخدام عنوان الموقع الكلي",
|
||||||
"title.url": "Title Link URL",
|
"title.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": "اسم المنتدي",
|
||||||
"title.show-in-header": "Show Site Title in Header",
|
"title.show-in-header": "Show Site Title in Header",
|
||||||
"browser-title": "عنوان المتصفح",
|
"browser-title": "عنوان المتصفح",
|
||||||
@@ -15,16 +13,16 @@
|
|||||||
"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": "Choose what page is shown when users navigate to the root URL of your forum.",
|
"description": "وصف الموقع",
|
||||||
"keywords": "الكلمات الدليله للموقع",
|
"keywords": "الكلمات الدليله للموقع",
|
||||||
"keywords-placeholder": "Keywords describing your community, comma-separated",
|
"keywords-placeholder": "Keywords describing your community, comma-separated",
|
||||||
"logo-and-icons": "Site Logo & Icons",
|
"logo": "شعار الموقع",
|
||||||
"logo.image": "صورة",
|
"logo.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": "رفع",
|
||||||
"logo.url": "Logo Link URL",
|
"logo.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": "نص بديل",
|
||||||
"log.alt-text-placeholder": "Alternative text for accessibility",
|
"log.alt-text-placeholder": "Alternative text for accessibility",
|
||||||
"favicon": "صورة المفضله",
|
"favicon": "صورة المفضله",
|
||||||
@@ -37,10 +35,7 @@
|
|||||||
"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.",
|
"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-in": "Search In",
|
|
||||||
"search-default-in-quick": "Quick Search In",
|
|
||||||
"search-default-sort-by": "Sort by",
|
|
||||||
"outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page",
|
"outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page",
|
||||||
"site-colors": "Site Color Metadata",
|
"site-colors": "Site Color Metadata",
|
||||||
"theme-color": "لون الثيم",
|
"theme-color": "لون الثيم",
|
||||||
@@ -48,15 +43,5 @@
|
|||||||
"background-color-help": "Color used for splash screen background when website is installed as a PWA",
|
"background-color-help": "Color used for splash screen background when website is installed as a PWA",
|
||||||
"undo-timeout": "Undo Timeout",
|
"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.",
|
"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",
|
"topic-tools": "Topic Tools"
|
||||||
"home-page": "Home Page",
|
|
||||||
"home-page-route": "Home Page Route",
|
|
||||||
"custom-route": "Custom Route",
|
|
||||||
"allow-user-home-pages": "Allow User Home Pages",
|
|
||||||
"home-page-title": "Title of the home page (default \"Home\")",
|
|
||||||
"default-language": "اللغة الافتراضية",
|
|
||||||
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي",
|
|
||||||
"default-language-help": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
|
|
||||||
"post-sharing": "Post Sharing",
|
|
||||||
"info-plugins-additional": "Plugins can add additional networks for sharing posts."
|
|
||||||
}
|
}
|
||||||
7
public/language/ar/admin/settings/guest.json
Normal file
7
public/language/ar/admin/settings/guest.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"settings": "Settings",
|
||||||
|
"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\"",
|
||||||
|
"topic-views.enabled": "Allow guests to increase topic view counts",
|
||||||
|
"reply-notifications.enabled": "Allow guests to generate reply notifications"
|
||||||
|
}
|
||||||
8
public/language/ar/admin/settings/homepage.json
Normal file
8
public/language/ar/admin/settings/homepage.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"home-page": "Home Page",
|
||||||
|
"description": "Choose what page is shown when users navigate to the root URL of your forum.",
|
||||||
|
"home-page-route": "Home Page Route",
|
||||||
|
"custom-route": "Custom Route",
|
||||||
|
"allow-user-home-pages": "Allow User Home Pages",
|
||||||
|
"home-page-title": "Title of the home page (default \"Home\")"
|
||||||
|
}
|
||||||
6
public/language/ar/admin/settings/languages.json
Normal file
6
public/language/ar/admin/settings/languages.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"language-settings": "اعدادات اللغة",
|
||||||
|
"description": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
|
||||||
|
"default-language": "اللغة الافتراضية",
|
||||||
|
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي"
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"navigation": "Navigation",
|
|
||||||
"icon": "Icon:",
|
"icon": "Icon:",
|
||||||
"change-icon": "change",
|
"change-icon": "change",
|
||||||
"route": "Route:",
|
"route": "Route:",
|
||||||
@@ -10,10 +9,8 @@
|
|||||||
"id": "ID: <small>optional</small>",
|
"id": "ID: <small>optional</small>",
|
||||||
|
|
||||||
"properties": "Properties:",
|
"properties": "Properties:",
|
||||||
"show-to-groups": "Show to 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",
|
||||||
@@ -23,4 +20,4 @@
|
|||||||
"custom-route": "Custom Route",
|
"custom-route": "Custom Route",
|
||||||
"core": "core",
|
"core": "core",
|
||||||
"plugin": "plugin"
|
"plugin": "plugin"
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,5 @@
|
|||||||
"notifications": "Notifications",
|
"notifications": "Notifications",
|
||||||
"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)"
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
{
|
{
|
||||||
"general": "General",
|
|
||||||
"sorting": "Post Sorting",
|
"sorting": "Post Sorting",
|
||||||
"sorting.post-default": "Default Post Sorting",
|
"sorting.post-default": "Default Post Sorting",
|
||||||
"sorting.oldest-to-newest": "Oldest to Newest",
|
"sorting.oldest-to-newest": "Oldest to Newest",
|
||||||
"sorting.newest-to-oldest": "Newest to Oldest",
|
"sorting.newest-to-oldest": "Newest to Oldest",
|
||||||
"sorting.recently-replied": "Recently Replied",
|
|
||||||
"sorting.recently-created": "Recently Created",
|
|
||||||
"sorting.most-votes": "Most Votes",
|
"sorting.most-votes": "Most Votes",
|
||||||
"sorting.most-posts": "Most Posts",
|
"sorting.most-posts": "Most Posts",
|
||||||
"sorting.most-views": "Most Views",
|
|
||||||
"sorting.topic-default": "Default Topic Sorting",
|
"sorting.topic-default": "Default Topic Sorting",
|
||||||
"length": "Post Length",
|
"length": "Post Length",
|
||||||
"post-queue": "Post Queue",
|
"post-queue": "Post Queue",
|
||||||
"restrictions": "Posting Restrictions",
|
"restrictions": "Posting Restrictions",
|
||||||
|
"restrictions-new": "New User Restrictions",
|
||||||
"restrictions.post-queue": "Enable post queue",
|
"restrictions.post-queue": "Enable post queue",
|
||||||
"restrictions.post-queue-rep-threshold": "Reputation required to bypass post queue",
|
"restrictions.post-queue-rep-threshold": "Reputation required to bypass post queue",
|
||||||
"restrictions.groups-exempt-from-post-queue": "Select groups that should be exempt from the post queue",
|
"restrictions.groups-exempt-from-post-queue": "Select groups that should be exempt from the post queue",
|
||||||
@@ -20,13 +17,18 @@
|
|||||||
"restrictions.post-queue-help": "Enabling post queue will put the posts of new users in a queue for approval",
|
"restrictions.post-queue-help": "Enabling post queue will put the posts of new users in a queue for approval",
|
||||||
"restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users",
|
"restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users",
|
||||||
"restrictions.seconds-between": "Number of seconds between posts",
|
"restrictions.seconds-between": "Number of seconds between posts",
|
||||||
|
"restrictions.seconds-between-new": "Seconds between posts for new users",
|
||||||
|
"restrictions.rep-threshold": "Reputation threshold before these restrictions are lifted",
|
||||||
|
"restrictions.seconds-before-new": "Seconds before a new user can make their first post",
|
||||||
"restrictions.seconds-edit-after": "Number of seconds a post remains editable (set to 0 to disable)",
|
"restrictions.seconds-edit-after": "Number of seconds a post remains editable (set to 0 to disable)",
|
||||||
"restrictions.seconds-delete-after": "Number of seconds a post remains deletable (set to 0 to disable)",
|
"restrictions.seconds-delete-after": "Number of seconds a post remains deletable (set to 0 to disable)",
|
||||||
"restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics (set to 0 to disable)",
|
"restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics (set to 0 to disable)",
|
||||||
"restrictions.title-length": "Title Length",
|
"restrictions.min-title-length": "Minimum Title Length",
|
||||||
"restrictions.post-length": "Post Length",
|
"restrictions.max-title-length": "Maximum Title Length",
|
||||||
|
"restrictions.min-post-length": "Minimum Post Length",
|
||||||
|
"restrictions.max-post-length": "Maximum Post Length",
|
||||||
"restrictions.days-until-stale": "Days until topic is considered stale",
|
"restrictions.days-until-stale": "Days until topic is considered stale",
|
||||||
"restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic. (set to 0 to disable)",
|
"restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic.",
|
||||||
"timestamp": "Timestamp",
|
"timestamp": "Timestamp",
|
||||||
"timestamp.cut-off": "Date cut-off (in days)",
|
"timestamp.cut-off": "Date cut-off (in days)",
|
||||||
"timestamp.cut-off-help": "Dates & times will be shown in a relative manner (e.g. \"3 hours ago\" / \"5 days ago\"), and localised into various\n\t\t\t\t\tlanguages. After a certain point, this text can be switched to display the localised date itself\n\t\t\t\t\t(e.g. 5 Nov 2016 15:30).<br /><em>(Default: <code>30</code>, or one month). Set to 0 to always display dates, leave blank to always display relative times.</em>",
|
"timestamp.cut-off-help": "Dates & times will be shown in a relative manner (e.g. \"3 hours ago\" / \"5 days ago\"), and localised into various\n\t\t\t\t\tlanguages. After a certain point, this text can be switched to display the localised date itself\n\t\t\t\t\t(e.g. 5 Nov 2016 15:30).<br /><em>(Default: <code>30</code>, or one month). Set to 0 to always display dates, leave blank to always display relative times.</em>",
|
||||||
@@ -38,26 +40,22 @@
|
|||||||
"teaser.last-post": "Last – Show the latest post, including the original post, if no replies",
|
"teaser.last-post": "Last – Show the latest post, including the original post, if no replies",
|
||||||
"teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies",
|
"teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies",
|
||||||
"teaser.first": "First",
|
"teaser.first": "First",
|
||||||
"showPostPreviewsOnHover": "Show a preview of posts when mouse overed",
|
"unread": "Unread Settings",
|
||||||
"unread-and-recent": "Unread & Recent Settings",
|
|
||||||
"unread.cutoff": "Unread cutoff days",
|
"unread.cutoff": "Unread cutoff days",
|
||||||
"unread.min-track-last": "Minimum posts in topic before tracking last read",
|
"unread.min-track-last": "Minimum posts in topic before tracking last read",
|
||||||
|
"recent": "Recent Settings",
|
||||||
"recent.max-topics": "Maximum topics on /recent",
|
"recent.max-topics": "Maximum topics on /recent",
|
||||||
"recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page",
|
"recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page",
|
||||||
"signature": "Signature Settings",
|
"signature": "Signature Settings",
|
||||||
"signature.disable": "Disable signatures",
|
"signature.disable": "Disable signatures",
|
||||||
"signature.no-links": "Disable links in signatures",
|
"signature.no-links": "Disable links in signatures",
|
||||||
"signature.no-images": "Disable images in signatures",
|
"signature.no-images": "Disable images in signatures",
|
||||||
"signature.hide-duplicates": "Hide duplicate signatures in topics",
|
|
||||||
"signature.max-length": "Maximum Signature Length",
|
"signature.max-length": "Maximum Signature Length",
|
||||||
"composer": "Composer Settings",
|
"composer": "Composer Settings",
|
||||||
"composer-help": "The following settings govern the functionality and/or appearance of the post composer shown\n\t\t\t\tto users when they create new topics, or reply to existing topics.",
|
"composer-help": "The following settings govern the functionality and/or appearance of the post composer shown\n\t\t\t\tto users when they create new topics, or reply to existing topics.",
|
||||||
"composer.show-help": "Show \"Help\" tab",
|
"composer.show-help": "Show \"Help\" tab",
|
||||||
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
|
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
|
||||||
"composer.custom-help": "Custom Help Text",
|
"composer.custom-help": "Custom Help Text",
|
||||||
"backlinks": "Backlinks",
|
|
||||||
"backlinks.enabled": "Enable topic backlinks",
|
|
||||||
"backlinks.help": "If a post references another topic, a link back to the post will be inserted into the referenced topic at that point in time.",
|
|
||||||
"ip-tracking": "IP Tracking",
|
"ip-tracking": "IP Tracking",
|
||||||
"ip-tracking.each-post": "Track IP Address for each post",
|
"ip-tracking.each-post": "Track IP Address for each post",
|
||||||
"enable-post-history": "Enable Post History"
|
"enable-post-history": "Enable Post History"
|
||||||
|
|||||||
@@ -2,24 +2,13 @@
|
|||||||
"reputation": "Reputation Settings",
|
"reputation": "Reputation Settings",
|
||||||
"disable": "Disable Reputation System",
|
"disable": "Disable Reputation System",
|
||||||
"disable-down-voting": "Disable Down Voting",
|
"disable-down-voting": "Disable Down Voting",
|
||||||
"upvote-visibility": "Up Vote visibility",
|
"votes-are-public": "All Votes Are Public",
|
||||||
"upvote-visibility-all": "Everyone can see up votes",
|
|
||||||
"upvote-visibility-loggedin": "Only logged in users can see up votes",
|
|
||||||
"upvote-visibility-privileged": "Only privileged users like admins & moderators can see up votes",
|
|
||||||
"downvote-visibility": "Down Vote visibility",
|
|
||||||
"downvote-visibility-all": "Everyone can see down votes",
|
|
||||||
"downvote-visibility-loggedin": "Only logged in users can see down votes",
|
|
||||||
"downvote-visibility-privileged": "Only privileged users like admins & moderators can see down votes",
|
|
||||||
"thresholds": "Activity Thresholds",
|
"thresholds": "Activity Thresholds",
|
||||||
"min-rep-upvote": "Minimum reputation to upvote posts",
|
|
||||||
"upvotes-per-day": "Upvotes per day (set to 0 for unlimited upvotes)",
|
|
||||||
"upvotes-per-user-per-day": "Upvotes per user per day (set to 0 for unlimited upvotes)",
|
|
||||||
"min-rep-downvote": "Minimum reputation to downvote posts",
|
"min-rep-downvote": "Minimum reputation to downvote posts",
|
||||||
"downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)",
|
"downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)",
|
||||||
"downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)",
|
"downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)",
|
||||||
"min-rep-chat": "Minimum reputation to send chat messages",
|
|
||||||
"min-rep-post-links": "Minimum reputation to post links",
|
|
||||||
"min-rep-flag": "Minimum reputation to flag posts",
|
"min-rep-flag": "Minimum reputation to flag posts",
|
||||||
|
"min-rep-website": "Minimum reputation to add \"Website\" to user profile",
|
||||||
"min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile",
|
"min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile",
|
||||||
"min-rep-signature": "Minimum reputation to add \"Signature\" to user profile",
|
"min-rep-signature": "Minimum reputation to add \"Signature\" to user profile",
|
||||||
"min-rep-profile-picture": "Minimum reputation to add \"Profile Picture\" to user profile",
|
"min-rep-profile-picture": "Minimum reputation to add \"Profile Picture\" to user profile",
|
||||||
@@ -29,15 +18,5 @@
|
|||||||
"flags.limit-per-target": "Maximum number of times something can be flagged",
|
"flags.limit-per-target": "Maximum number of times something can be flagged",
|
||||||
"flags.limit-per-target-placeholder": "Default: 0",
|
"flags.limit-per-target-placeholder": "Default: 0",
|
||||||
"flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a "report" and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.",
|
"flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a "report" and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.",
|
||||||
"flags.limit-post-flags-per-day": "Maximum number of times a user can flag posts in a day",
|
"flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned"
|
||||||
"flags.limit-post-flags-per-day-help": "Set to 0 to disable, (default: 10)",
|
|
||||||
"flags.limit-user-flags-per-day": "Maximum number of times a user can flag users in a day",
|
|
||||||
"flags.limit-user-flags-per-day-help": "Set to 0 to disable, (default: 10)",
|
|
||||||
"flags.auto-flag-on-downvote-threshold": "Number of downvotes to auto flag posts",
|
|
||||||
"flags.auto-flag-on-downvote-threshold-help": "Set to 0 to disable, (default: 0)",
|
|
||||||
"flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned",
|
|
||||||
"flags.action-on-resolve": "Do the following when a flag is resolved",
|
|
||||||
"flags.action-on-reject": "Do the following when a flag is rejected",
|
|
||||||
"flags.action.nothing": "Do nothing",
|
|
||||||
"flags.action.rescind": "Rescind the notification sent to moderators/administrators"
|
|
||||||
}
|
}
|
||||||
5
public/language/ar/admin/settings/social.json
Normal file
5
public/language/ar/admin/settings/social.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"post-sharing": "Post Sharing",
|
||||||
|
"info-plugins-additional": "Plugins can add additional networks for sharing posts.",
|
||||||
|
"save-success": "Successfully saved Post Sharing Networks!"
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
"link-to-manage": "Manage Tags",
|
"link-to-manage": "Manage Tags",
|
||||||
"system-tags": "System Tags",
|
"system-tags": "System Tags",
|
||||||
"system-tags-help": "Only privileged users will be able to use these tags.",
|
"system-tags-help": "Only privileged users will be able to use these tags.",
|
||||||
"tags-per-topic": "Tags per topic",
|
|
||||||
"min-per-topic": "Minimum Tags per Topic",
|
"min-per-topic": "Minimum Tags per Topic",
|
||||||
"max-per-topic": "Maximum Tags per Topic",
|
"max-per-topic": "Maximum Tags per Topic",
|
||||||
"min-length": "Minimum Tag Length",
|
"min-length": "Minimum Tag Length",
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
{
|
{
|
||||||
"posts": "المشاركات",
|
"posts": "المشاركات",
|
||||||
"orphans": "Orphaned Files",
|
"allow-files": "السماح للأعضاء بتحميل الملفات الإعتيادية",
|
||||||
"private": "جعل الملفات التي تم رفعها خاصة",
|
"private": "جعل الملفات التي تم رفعها خاصة",
|
||||||
"strip-exif-data": "Strip EXIF Data",
|
"strip-exif-data": "Strip EXIF Data",
|
||||||
"preserve-orphaned-uploads": "Keep uploaded files on disk after a post is purged",
|
|
||||||
"orphanExpiryDays": "Days to keep orphaned files",
|
|
||||||
"orphanExpiryDays-help": "After this many days, orphaned uploads will be deleted from the file system.<br />Set 0 or leave blank to disable.",
|
|
||||||
"private-extensions": "File extensions to make private",
|
"private-extensions": "File extensions to make private",
|
||||||
"private-uploads-extensions-help": "Enter comma-separated list of file extensions to make private here (e.g. <code>pdf,xls,doc</code>). An empty list means all files are private.",
|
"private-uploads-extensions-help": "Enter comma-separated list of file extensions to make private here (e.g. <code>pdf,xls,doc</code>). An empty list means all files are private.",
|
||||||
"resize-image-width-threshold": "Resize images if they are wider than specified width",
|
"resize-image-width-threshold": "Resize images if they are wider than specified width",
|
||||||
"resize-image-width-threshold-help": "(in pixels, default: 2000 pixels, set to 0 to disable)",
|
"resize-image-width-threshold-help": "(in pixels, default: 1520 pixels, set to 0 to disable)",
|
||||||
"resize-image-width": "Resize images down to specified width",
|
"resize-image-width": "Resize images down to specified width",
|
||||||
"resize-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
|
"resize-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
|
||||||
"resize-image-keep-original": "Keep original image after resize",
|
|
||||||
"resize-image-quality": "Quality to use when resizing images",
|
"resize-image-quality": "Quality to use when resizing images",
|
||||||
"resize-image-quality-help": "Use a lower quality setting to reduce the file size of resized images.",
|
"resize-image-quality-help": "Use a lower quality setting to reduce the file size of resized images.",
|
||||||
"max-file-size": "الحد الأقصى لحجم الملف (بالكيبيبايت)",
|
"max-file-size": "الحد الأقصى لحجم الملف (بالكيبيبايت)",
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
"authentication": "المصادقة",
|
"authentication": "المصادقة",
|
||||||
|
"require-email-confirmation": "يطلب تأكيد البريد الإلكتروني",
|
||||||
"email-confirm-interval": "لا يمكن للمستخدم إعادة إرسال رسالة تأكيد البريد الالكتروني حتى مرور",
|
"email-confirm-interval": "لا يمكن للمستخدم إعادة إرسال رسالة تأكيد البريد الالكتروني حتى مرور",
|
||||||
"email-confirm-interval2": "minutes have elapsed",
|
"email-confirm-email2": "دقائق",
|
||||||
"allow-login-with": "السماح بتسجيل الدخول باستخدام",
|
"allow-login-with": "السماح بتسجيل الدخول باستخدام",
|
||||||
"allow-login-with.username-email": "اسم المستخدم أو البريد الالكتروني",
|
"allow-login-with.username-email": "اسم المستخدم أو البريد الالكتروني",
|
||||||
"allow-login-with.username": "اسم المستخدم فقط",
|
"allow-login-with.username": "اسم المستخدم فقط",
|
||||||
|
"allow-login-with.email": "البريد الالكتروني فقط",
|
||||||
"account-settings": "إعدادت الحساب",
|
"account-settings": "إعدادت الحساب",
|
||||||
"gdpr-enabled": "Enable GDPR consent collection",
|
"gdpr_enabled": "Enable GDPR consent collection",
|
||||||
"gdpr-enabled-help": "When enabled, all new registrants will be required to explicitly give consent for data collection and usage under the <a href=\"https://ec.europa.eu/info/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules/eu-data-protection-rules_en\">General Data Protection Regulation (GDPR)</a>. <strong>Note</strong>: Enabling GDPR does not force pre-existing users to provide consent. To do so, you will need to install the GDPR plugin.",
|
"gdpr_enabled_help": "When enabled, all new registrants will be required to explicitly give consent for data collection and usage under the <a href=\"https://ec.europa.eu/info/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules/eu-data-protection-rules_en\">General Data Protection Regulation (GDPR)</a>. <strong>Note</strong>: Enabling GDPR does not force pre-existing users to provide consent. To do so, you will need to install the GDPR plugin.",
|
||||||
"disable-username-changes": "عدم السماح بتغيير اسم المستخدم",
|
"disable-username-changes": "عدم السماح بتغيير اسم المستخدم",
|
||||||
"disable-email-changes": "عدم السماح بتغيير البريد الالكتروني",
|
"disable-email-changes": "عدم السماح بتغيير البريد الالكتروني",
|
||||||
"disable-password-changes": "عدم السماح بتغيير كلمة المرور",
|
"disable-password-changes": "عدم السماح بتغيير كلمة المرور",
|
||||||
@@ -29,8 +31,6 @@
|
|||||||
"session-time-days": "Days",
|
"session-time-days": "Days",
|
||||||
"session-time-seconds": "Seconds",
|
"session-time-seconds": "Seconds",
|
||||||
"session-time-help": "These values are used to govern how long a user stays logged in when they check "Remember Me" on login. Note that only one of these values will be used. If there is no <i>seconds</i> value we fall back to <i>days</i>. If there is no <i>days</i> value we default to <i>14 days</i>.",
|
"session-time-help": "These values are used to govern how long a user stays logged in when they check "Remember Me" on login. Note that only one of these values will be used. If there is no <i>seconds</i> value we fall back to <i>days</i>. If there is no <i>days</i> value we default to <i>14 days</i>.",
|
||||||
"session-duration": "Session length if \"Remember Me\" is not checked (seconds)",
|
|
||||||
"session-duration-help": "By default — or if set to <code>0</code> — a user will stay logged in for the duration of the session (e.g. however long the browser window/tab remains open). Set this value to explicitly invalidate the session after the specified number of seconds.",
|
|
||||||
"online-cutoff": "Minutes after user is considered inactive",
|
"online-cutoff": "Minutes after user is considered inactive",
|
||||||
"online-cutoff-help": "If user performs no actions for this duration, they are considered inactive and they do not receive realtime updates.",
|
"online-cutoff-help": "If user performs no actions for this duration, they are considered inactive and they do not receive realtime updates.",
|
||||||
"registration": "تسجيل المستخدم",
|
"registration": "تسجيل المستخدم",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"max-about-me-length": "الحد الأعلى من الأحرف في حقل \"عني\"",
|
"max-about-me-length": "الحد الأعلى من الأحرف في حقل \"عني\"",
|
||||||
"terms-of-use": "شروط استخدام المنتدى <small>(تترك فارغة لتعطيلها)</small>",
|
"terms-of-use": "شروط استخدام المنتدى <small>(تترك فارغة لتعطيلها)</small>",
|
||||||
"user-search": "بحث الأعضاء",
|
"user-search": "بحث الأعضاء",
|
||||||
"user-search-results-per-page": "Number of users to display in search results",
|
"user-search-results-per-page": "عدد النتائج المراد عرضها",
|
||||||
"default-user-settings": "إعدادات الأعضاء الافتراضية",
|
"default-user-settings": "إعدادات الأعضاء الافتراضية",
|
||||||
"show-email": "عرض البريد الإلكتروني",
|
"show-email": "عرض البريد الإلكتروني",
|
||||||
"show-fullname": "عرض الاسم الكامل",
|
"show-fullname": "عرض الاسم الكامل",
|
||||||
@@ -71,7 +71,6 @@
|
|||||||
"digest-freq.off": "Off",
|
"digest-freq.off": "Off",
|
||||||
"digest-freq.daily": "Daily",
|
"digest-freq.daily": "Daily",
|
||||||
"digest-freq.weekly": "Weekly",
|
"digest-freq.weekly": "Weekly",
|
||||||
"digest-freq.biweekly": "Bi-Weekly",
|
|
||||||
"digest-freq.monthly": "Monthly",
|
"digest-freq.monthly": "Monthly",
|
||||||
"email-chat-notifs": "Send an email if a new chat message arrives and I am not online",
|
"email-chat-notifs": "Send an email if a new chat message arrives and I am not online",
|
||||||
"email-post-notif": "Send an email when replies are made to topics I am subscribed to",
|
"email-post-notif": "Send an email when replies are made to topics I am subscribed to",
|
||||||
@@ -79,19 +78,7 @@
|
|||||||
"follow-replied-topics": "Follow topics that you reply to",
|
"follow-replied-topics": "Follow topics that you reply to",
|
||||||
"default-notification-settings": "Default notification settings",
|
"default-notification-settings": "Default notification settings",
|
||||||
"categoryWatchState": "Default category watch state",
|
"categoryWatchState": "Default category watch state",
|
||||||
"categoryWatchState.tracking": "Tracking",
|
"categoryWatchState.watching": "Watching",
|
||||||
"categoryWatchState.notwatching": "Not Watching",
|
"categoryWatchState.notwatching": "Not Watching",
|
||||||
"categoryWatchState.ignoring": "Ignoring",
|
"categoryWatchState.ignoring": "Ignoring"
|
||||||
"restrictions-new": "New User Restrictions",
|
}
|
||||||
"restrictions.rep-threshold": "Reputation threshold before these restrictions are lifted",
|
|
||||||
"restrictions.seconds-between-new": "Seconds between posts for new users",
|
|
||||||
"restrictions.seconds-before-new": "Seconds before a new user can make their first post",
|
|
||||||
"restrictions.seconds-edit-after-new": "Number of seconds a post remains editable for new users (set to 0 to disable)",
|
|
||||||
"restrictions.milliseconds-between-messages": "Time between chat messages for new users (ms)",
|
|
||||||
"restrictions.groups-exempt-from-new-user-restrictions": "Select groups that should be exempt from the new user restrictions",
|
|
||||||
"guest-settings": "Guest Settings",
|
|
||||||
"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\"",
|
|
||||||
"topic-views.enabled": "Allow guests to increase topic view counts",
|
|
||||||
"reply-notifications.enabled": "Allow guests to generate reply notifications"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"post-sort-option": "Post sort option, %1",
|
|
||||||
"topic-sort-option": "Topic sort option, %1",
|
|
||||||
"user-avatar-for": "User avatar for %1",
|
|
||||||
"profile-page-for": "Profile page for user %1",
|
|
||||||
"user-watched-tags": "User watched tags",
|
|
||||||
"delete-upload-button": "Delete upload button",
|
|
||||||
"group-page-link-for": "Group page link for %1"
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,21 @@
|
|||||||
{
|
{
|
||||||
"category": "قسم",
|
"category": "قسم",
|
||||||
"subcategories": "قسم فرعي",
|
"subcategories": "قسم فرعي",
|
||||||
"new-topic-button": "موضوع جديد",
|
"new_topic_button": "موضوع جديد",
|
||||||
"guest-login-post": "سجل الدخول للمشاركة",
|
"guest-login-post": "سجل الدخول للمشاركة",
|
||||||
"no-topics": "<strong>لا توجد مواضيع في هذه القسم</strong>لم لا تحاول إنشاء موضوع؟<br />",
|
"no_topics": "<strong>لا توجد مواضيع في هذه القسم</strong>لم لا تحاول إنشاء موضوع؟<br />",
|
||||||
"browsing": "تصفح",
|
"browsing": "تصفح",
|
||||||
"no-replies": "لم يرد أحد",
|
"no_replies": "لم يرد أحد",
|
||||||
"no-new-posts": "لا توجد مشاركات جديدة.",
|
"no_new_posts": "لا توجد مشاركات جديدة.",
|
||||||
"watch": "تابع",
|
"watch": "تابع",
|
||||||
"ignore": "تجاهل",
|
"ignore": "تجاهل",
|
||||||
"watching": "متابع",
|
"watching": "متابع",
|
||||||
"tracking": "Tracking",
|
|
||||||
"not-watching": "لست متابع",
|
"not-watching": "لست متابع",
|
||||||
"ignoring": "متجاهل",
|
"ignoring": "متجاهل",
|
||||||
"watching.description": "Notify me of new topics.<br/>Show topics in unread & recent",
|
"watching.description": "أظهر المواضيع في الغير مقروء و الحديث",
|
||||||
"tracking.description": "Shows topics in unread & recent",
|
|
||||||
"not-watching.description": "Do not show topics in unread, show in recent",
|
"not-watching.description": "Do not show topics in unread, show in recent",
|
||||||
"ignoring.description": "Do not show topics in unread & recent",
|
"ignoring.description": "Do not show topics in unread and recent",
|
||||||
"watching.message": "You are now watching updates from this category and all subcategories",
|
"watching.message": "You are now watching updates from this category and all subcategories",
|
||||||
"tracking.message": "You are now tracking updates from this category and all subcategories",
|
|
||||||
"notwatching.message": "You are not watching updates from this category and all subcategories",
|
"notwatching.message": "You are not watching updates from this category and all subcategories",
|
||||||
"ignoring.message": "You are now ignoring updates from this category and all subcategories",
|
"ignoring.message": "You are now ignoring updates from this category and all subcategories",
|
||||||
"watched-categories": "الأقسام المُتابعة",
|
"watched-categories": "الأقسام المُتابعة",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user